decb.c Copyleft (C) 2009-2014 Antonio Maschio @ Update: 09 December 2014 This is decb, a simulator of the DEC-20 BASIC language, version 17F, as reported in the January 1976 book "BASIC User's Guide" (the book, a.k.a. DEC-20-LBMAA-A-D, is a reprint of the March 1974 paper "Basic Conversational Language Manual" for version 17D, a.k.a. DEC-10-LBLMA-A-D). I'm not certain if version 17F is the one described in the LBMAA-A-D since nowhere it's written what version it is (see down for explicative notes). The cited 1976 book "BASIC User's Guide" is, from now on, identified with the label LBMAA-A-D, and all the listing references point to this book (unless otherwise specified). All these books are downloadable for free as pdf at the great bitsavers.org. decb is part of the 'vibes' project, a set of programs for simulating the MONITOR, the BASIC interface and the BASIC language, for which decb is the BASIC language interpreter for programs execution (vibes is still in the design phase). I have planned also Algol for this project, but I'm not sure this last will be really created, because I don't know this language. Byron S. Gottfried's book "Programming with BASIC", 1st edition, 1975 (in which he uses the DEC System and a BASIC version close to 17F) served as well for the making of this program (what a wonderful teacher he was!) Some features, not completely reported in the DEC-20 documentation, were found and tested at the Living Computer Museum (livingcomputermuseum.org) on a PDP-10 (from now on this site will be identified by LCM), where version 17F runs, and herein integrated (see also the BAS17F.DOC file in the DOC: directory). One notable fact, is that the LBMAA-A-D dates back to January 1976, while the document BAS17F.DOC dates back to September 1981: how long it lasted! And the fact that the PDP-10 LCM BASIC behaves just as described in the LBMAA-A-D makes me think the latter describes version 17F. Beta version claim: Thanks to the many who downloaded and tested decb in Gamma phase, and in particular I want to thank I.J. (he wants to remain unknown) who spent much of his time in testing and trying many decb features, founding many bugs and bad behaviours, now corrected (I hope) in this Beta version. There is still much work to do for version 1.0... This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . It's GPL! Enjoy! Notes about the DECSYSTEM-20 (from the LCM): ------------------------------------------- DEC PDP-10: KL-10 (DECSYSTEM-20) Introduced in 1974, it had 36-bit word size, a magnetic core (later semiconductor) memory, capacity of 32K to 4096K (4M) words, 500 nanosecond instruction cycle, 1.8 MIPS. The KL-10 was a new implementation of the PDP-10 architecture intended for high-end timesharing in data centers. It was built using a fast but power-hungry technology called emitter-coupled logic (ECL), which was soon replaced by other methods of making logic components that used far less power but offered even higher speed. Although the KL-10 was intended for the new TOPS-20 operating system, which provided more powerful tools for system developers, customers demanded that DEC provide a version of TOPS-10 for the DECSYSTEM-20 because of its more efficient use of resources. Notes about the DEC-20 BASIC implemented here: --------------------------------------------- The DEC-20 had a long term service, and programs written for it underwent a long chain of updated versions; the BASIC environment didn't escape this. Nonetheless, if you run the BASIC in the DEC-20 at the LCM, you'll discover a very advanced BASIC, with subroutines, no line numbering, many statements per line, added statements, missing DEC-10 statements and so forth. It is definitely another BASIC. So you may ask: why decb (which is related to the DEC-20) simulates an older version? Why did you make this DEC-20 simulator, while the very same computer hosts another (very advanced) BASIC? An answer may be this: in 1974 the BASIC on the DEC-10 was at its last revision (v. 17F) since 1968 (when the first DEC BASIC was issued); the LBLMA-A-D is an evidence of this, being last update dating back to March 1974). This BASIC was then ported to the DEC-20, initially with the same architecture (the LBMAA-A-D, which dates back to January 1976, is a reprint of the LBLMA-A-D, after all...), starting presumably from version 17F circa (this is the version simulated by decb); but the years passed, and new updates came on the DEC-20, but not on the older DEC-10; the last of these DEC-20 updates is the version you find today on the "modern" DEC-20 system at the LCM. The old DEC-10 living at the LCM, where a KL10 CPU #3530 is running a Tops-10 7.04 operating system, hosts the DEC-BASIC version closer to decb (and was the testing engine for decb, actually). I wrote decb according to the LBMAA-A-D directives, that match completely the BASIC running on the DEC-10, but the fact that I call it a DEC-20 simulator comes from the first sentence in chapter 1 of the LBMAA-A-D, where you can read: "This chapter introduces the user to BASIC on the DECsystem-20 and to its restrictions and characteristics...." It was early 1976, and times would change. It was a long time ago... ***************************************************************************** The following routine define the PRAGMA instruction (decb feature) **************************************************************************** PRAGMA() read following string and evaluate parameters, to activate available options at runtime (generally, options are a subset of all the options that can also be activated on the command line). Blanks are ignored. This function is encapsulated in a REM statement, so that it can be used in any BASIC without interfering with the source. Options must be followed by a specifier E or D (E for enable, D for disable) under the following rules: - Unrecognized options will be ignored - Unrecognized specifiers will be ignored and cause option to be ignored. In case option is ignored, the default behavior is not changed. p points to the character after PRAGMA Available options: C enable capitalization on input R disable resetting of memory upon CHAIN calls Note: more options may be available in future versions. Note: there must be one PRAGMA statement for each program; in case of a CHAIN set, it should be set in the first program, if option has to be used in all the programs in the CHAIN set. Secondary system function. ***************************************************************************** The following routines define the behavior of all the statements involving the USING feature as defined in the LBMAA-A-D, which were included since 1971 version VI of Dartmouth BASIC Ref. LBMAA-A-D Chapter 11, "Formatted Output" **************************************************************************** FWRITEUSINGSHARP() perform the 'PRINT/WRITE USING #N' / 'PRINT/WRITE #N, USING' statement. The algorithm is: get the image and call formatAnalyzer() upon it. flag: if it's TRUE, perform the PRINT USING# statement, otherwise WRITE# ch: a channel id in case command comes from FWRITESHARP() p either points to the character after USING# in the first form or to the USING string in the second form Ref. LBMAA-A-D 11.1, 11-1, "The USING Statements" PRINTUSING() perform the PRINT USING statement The algorithm is the same of the previous function: get the image and call formatAnalyzer() upon it. Note: the PRINT USING command has nothing in common with PRINT. p points to the character after USING Ref. LBMAA-A-D 11.1, 11-1, "The USING Statements" ***************************************************************************** The following routines define the behavior of all the FILES statements as defined in the LBMAA-A-D, and which were included since 1969 version V of Dartmouth BASIC Ref. LBMAA-A-D Chapter 10, "Data File Capability" **************************************************************************** For the file types, consult: Ref. LBMAA-A-D 10.1, 10-1, "Types of Data Files" Ref. LBMAA-A-D 10.1.1, 10-1, "Sequential Access Files" Ref. LBMAA-A-D 10.1.2, 10-2, "Random Access Files" FFILE() implementation of the FILE/FILES statements. isFiles: flag: if TRUE, execute FILES (which does not accept variables and is executed before program start by preParse); if FALSE, execute FILE (which accepts variables and is executed during runtime). l: contains current line number for perror_(); p holds address of character after FILE/FILES Ref. LBMAA-A-D 10.2, 10-2, "The FILE and FILES Statements" IFEND() implementation of the IF END#/IF END: statement Note: decb IF END does not distinguish between sequential files with or without line numbers (that is in READ or INPUT mode); so decb version of IF END returns TRUE whenever whichever stream has reached EOF. Ref. LBMAA-A-D 10.10, 10-14, "The IF END statement" FMARGIN() implementation of the MARGIN statement Ref. LBMAA-A-D 10.8, 10-12, "The MARGIN and MARGIN ALL Statements" FPAGE() implementation of the PAGE [ALL] statement Ref. LBMAA-A-D 10.9, 10-12, "The PAGE, PAGE ALL, NOPAGE, NOPAGE ALL Statements" Ref. LBMAA-A-D, 6.8, 6-8, "PAGE Statement" Ref. LBMAA-A-D, 6.9, 6-8, "NOPAGE Statement" Ref. LBMAA-A-D, 10.9, 10-13, "The PAGE, PAGE ALL, NOPAGE, and NOPAGE ALL Statements" FNOPAGE() implementation of the NOPAGE [ALL] statement Ref. LBMAA-A-D 10.9, 10-13, "The PAGE, PAGE ALL, NOPAGE, NOPAGE ALL Statements" FQUOTE() implementation of the QUOTE/NOQUOTE [ALL] statement quotemode: a flag to set/reset quote mode; if TRUE, set QUOTE mode, if FALSE, set NOQUOTE mode. Ref. LBMAA-A-D 10.7, 10-10 "The QUOTE, QUOTE ALL, NOQUOTE, and NOQUOTE ALL Statements" PRINT() Wrapper to the PRINT family statements to select the correct path (designed for PRINT/PRINT#/WRITE#) flag: if TRUE, perform PRINT/PRINT#, otherwise perform WRITE# p points to the character after PRINT or WRITE FWRITESHARP() implementation of the PRINT/PRINT#/WRITE# statement (sequential files only) flag: if TRUE, perform PRINT/PRINT#, otherwise perform WRITE# Note: I noticed that on the LCM, if the last printed item was a number, there was one void line between the line of the number and the "TIME:" line, while if last item was a string void lines were three; I judge this too weird, and too onerous to implement a control for what last printed item type is, so I decided to keep three void lines for better readability Ref. LBMAA-A-D 1.4.3, 1-8, "PRINT Statement" Ref. LBMAA-A-D 6.1, 6-1, "More about the PRINT Statement" Ref. LBMAA-A-D 10.5.1, 10-7, "WRITE and PRINT Statements for Sequential Access Files" FWRITECOLON() implementation of the WRITE:/PRINT: statement (random files only) Note: according to the LBMAA-A-D the WRITE: and PRINT: statements are completely equivalent, and may be used on the same channel consecutively and/or alternatively without raising errors Ref. LBMAA-A-D 10.5.2, 10-9, "WRITE and PRINT Statements for Random Access Files" FREADSHARP() implementation of the READ#/INPUT# statements Note: READ# and INPUT#, according to the LBMAA-A-D differ: READ expects a line number for each data file line, and this number is discarded; if no number is found, an error is issued; INPUT#, does not expect a line number, and if found, treats it as a datum; These commands may not be mixed in the same channel; flag: if TRUE, perform INPUT#, otherwise perform READ# Ref. LBMAA-A-D 10.4, 10-5, "The READ and INPUT Statements" FREADCOLON() implementation of the READ:/INPUT: statements Note: according to the LBMAA-A-D the READ: and INPUT: statements are completely equivalent, and may be used in the same channel consecutively without raising errors (page 10-6). Ref. LBMAA-A-D 10.4, 10-5, "The READ and INPUT Statements" FRESTORE() implementation of the RESTORE statement for files; omitting the # (sign number) or the : (colon) is interpreted as a RESTORE on a sequential file Ref. LBMAA-A-D 10.3, 10-4, "The SCRATCH and RESTORE Statements" FSCRATCH() implementation of the SCRATCH statement; omitting the # (sign number) or the : (colon) is interpreted as a SCRATCH on a sequential file Ref. LBMAA-A-D 10.3, 10-4, "The SCRATCH and RESTORE Statements" FSET() implementation of the SET statement; Ref. LBMAA-A-D 10.6, 10-9, "The SET Statement and the LOC and LOF Functions" CHAIN() implementation of the CHAIN command; enable a program to load and parse an external program. Note: the CHAIN statement is the last to be executed, so anything following it is discarded, both in listing and in the program flow. In practice, the caller program transfers the control to the CHAINed file. The CHAINed file doesn't inherit the caller's variables (tested on the LCM, but not specified anywhere in the LBMAA-A-D). The starting line of the CHAINed program may be specified after a comma Ref. LBMAA-A-D 6.6, 6-6, "CHAIN Statement" Ref. LBMAA-A-D 9.5, 9-8, "Executing a BASIC Program in Memory", CHAIN ***************************************************************************** The following routines define the behavior of the MAT statements, which were included as a CardBasic option (external program on cards) since 1964 version II of Dartmouth BASIC, and as standard since 1966 version III. I base the following routines on chapter 7 of the LBMAA-A-D, and of course on the September 1966 manual for version III, which also presents an exhaustive explanation on the usage of matrices. Many references and suggestions have been grabbed by the beautiful book "Programming with BASIC" by Byron S. Gottfried (1975, Italian edition) and on the 1978 historical essay "BASIC Session" by Thomas E. Kurtz. Ref. LBMAA-A-D Chapter 7, "Vectors and Matrices"> **************************************************************************** MATbyK(result-matrix) multiply a matrix by an expression, both taken from stream; read multiplier and argument matrix from stream, multiply to the result matrix varr: the result matrix code, checked into MATtype() and passed here. Ref. LBMAA-A-D 7.9, 7-4, "MAT C=(K)*A" MATTRN(result-matrix) The MAT TRN statement for transposing a matrix; read argument matrix from stream, transpose it to the result matrix varr: the result matrix, checked into MATtype() and passed here. Ref. LBMAA-A-D 7.8, 7-4, "MAT C=TRN(A)" MATINV(result-matrix) The MAT INV statement for inverting a matrix Read parameters from the stream, setup parameters and pass them to matinvert(), the **real** matrix inverter. varr: the result matrix, checked into MATtype() and passed here. Ref. LBMAA-A-D 7.10, 7-4, "MAT C=INV(A) and the DET Function" matinvert(result-matrix, argument-matrix) Invert matrix 'argument' into matrix 'result' and calculate the determinant using the Gauss factorization scheme. Variables: - varr: the unknown result global inverted matrix (NxN) in the BASIC code - var1: the global knowns matrix to be inverted (NxN) in the BASIC code, it is used to form ma ma is the local composed matrix (2NxN) (to the left the knowns matrix, to the right the identity matrix); this matrix is invisible to BASIC x is the local calculated inverted matrix (NxN), invisible to BASIC and later copied back to varr. detValue is the global determinant value, visible to BASIC through DET. Copyleft Antonio Maschio - March 2008 MATIDN(result-matrix) The MAT IDN statement for setting a square array to identity matrix; var: the code of the matrix to be IDNed (there is no argument matrix) Note: as tested on the LCM, the IDN statement accepts forms of re-dimensioning like IDN(3), IDN(3,1), IDN(3,2) and IDN(2,3); - in the first case, the zero vector is returned (it refers to col. 0); - in the second case, the vector is {1 0 0} (in vertical) because the first actual column is preserved; - in case of IDN(3,2) the first two columns are taken {1 0 0 ; 0 1 0} (in vertical); - in the last case the 2x2 left matrix is IDNed, while the last column is preserved (with nulls) Ref. LBMAA-A-D 7.2, 7-2, "MAT C=ZER, MAT C=CON, MAT C=IDN" MATZER(result-matrix, option-value) The MAT ZER and MAT CON statements for setting (or resetting) an array to all zeroes (MAT ZER) or ones (MAT CON), depending on option value 'op' (0 for ZER, 1 for CON); Note: as tested on the LCM, the ZER/CON statements accept forms of re-dimensioning like CON(0), CON(4), CON(0,4) and CON(4,0); - in the first case, the vector is a one-element vector = 1; - in the second case, the vector is {1 1 1 1} (in vertical) because the first actual column is preserved and set to ones; the case CON(0,4) it's a horizontal vector, which has the form {0 1 1 1 1}; the last case is equivalent to the second; any other configurations like CON(m,n) are trivial var: the code of matrix to be ZEROed op: if TRUE perform CON, if FALSE perform ZER Ref. LBMAA-A-D 7.2, 7-2, "MAT C=ZER, MAT C=CON, MAT C=IDN" MATsum(result-matrix,matrix1,matrix2,option-value) Add two matrices or subtract the second from the first. varr: the result matrix code var1: first matrix addendum var2: second matrix addendum op: if -1, do subtraction, if 1 do addition Ref. LBMAA-A-D 7.6, 7-4, "MAT C=A+B and MAT C=A-B" MATmul(result-matrix,matrix1,matrix2) The cross-product between two matrices or vectors varr: the result matrix code var1: first matrix var2: second matrix Ref. LBMAA-A-D 7.7, 7-4, "MAT C=A*B" MATtype() this function is invoked by parse when a MAT statement is met (other than MAT READ, MAT PRINT or MAT INPUT, which follow their way), to choose the correct command. Controls are done on result matrix (instantiating it if not existing), and in case of addition, subtraction and multiplication between matrices, control is done on argument matrices; peculiar functions like IDN, CON, ZER, INV and TRN check argument matrix on their own. Ref. LBMAA-A-D, Chapter 7, "Vectors and Matrices" System function for matrix management matCopy(result-matrix, argument-matrix) copy source argument matrix into destination result matrix; dst: destination matrix src: source matrix Automatic redimension is performed in case target matrix can hold argument matrix dimensions. Ref. LBMAA-A-D 7.5, 7-4, "MAT B=A" MATINPUT() The MAT INPUT statement mode 1 is for numerical matrices, mode 2 for string matrices. Multiple-matrices input enabled Note: MAT INPUT does not treat more than one array per statement Ref. LBMAA-A-D 7.4, 7-3, "MAT INPUT V and the NUM function" Ref. LBMAA-A-D 8.1, 8-1, "Reading and printing strings" MATREAD() The MAT READ statement- Multiple-matrices reading enabled Ref. LBMAA-A-D 7.1, 7-1, "MAT Instruction Conventions" Ref. LBMAA-A-D 8.1, 8-1 "Reading and printing strings" MATPRINT() The MAT PRINT statement - rewritten April 2014 Multiple-matrices printing enabled Ref. LBMAA-A-D 7.3, 7-3, "MAT PRINT A" Ref. LBMAA-A-D 8.1, 8-1, "Reading and printing strings" ***************************************************************************** The following routines define the behavior of the statements involved with arrays, the DIM statement and the CHANGE statement; the DIM statement for numerical arrays was included since 1964 version II of the Dartmouth BASIC, and for strings since 1967 version IV; the CHANGE statement was included since 1967 version IV (not including the add-on variation CHANGE..TO..BIT, which appeared in 1969 version V). **************************************************************************** CHANGE() The CHANGE statement convert the explicit string (a variable or a constant enclosed into parentheses) to the series of ASCII character it's composed of, and store it into the vector specified after TO; convert also the vector which holds the string data to the string variable specified after TO. The vector need not to be dimensioned if the string is shorter than 10 characters (pos. 0 holds the length, pos. 1 through 10 hold the string), but if it's longer it must be declared, otherwise an error will be raised. Ref. LBMAA-A-D 8.4, 8-3, "The CHANGE Statement" DIM() The DIM statement Note: since DIM is preprocessed by preParse(), and thus cannot use any variable indexing (because variable are not initialized yet!), only numbers and not variables (or algebraic entities) must be used in the DIM statement; this is the very same behaviour of the DEC-20 basic. Besides, since it is never executed in runtime, the line number is not known, and it must be passed as argument (l) l: the current line that preParse() is analyzing. Ref. LBMAA-A-D 3.1, 3-1, "The Dimension Statement (DIM)" see also Ref. LBMAA-A-D 7.2, 7-2 (MAT instructions) see also Ref. LBMAA-A-D 8.2, 8-2 (string instructions) ***************************************************************************** The following routines define the remaining standard BASIC statements generally present in BASIC since 1964 (apart from PRINT, which has been reunited with PRINT#/WRITE#) **************************************************************************** INPUT() The INPUT statement Ref. LBMAA-A-D 6.2, 6-4, "INPUT Statement" Ref. LBMAA-A-D 8.2, 8-2, "String Conventions" getRandom() return a pseudo-random number, called by the RND function. The algorithm is the following: - get the decimals from third to eighth position of the division of randSeed by randDivi (at start respectively 27268 and 32767) to obtain a number greater than 0 (excluded) and lower than 1 (excluded) as the generated random number; - the first five decimals of this number are taken as the new randSeed for the next iteration. Note: to reduce the risk of circular ranges I add two variables, varying continuously: 'fix' that alters randSeed and div that alters randDivi; to reduce also the risk of having 'exact' divisions (like 0.250000, which is 'unfair' as random number) randSeed is always odd, randDivi is even. Note: This algorithm, after a variable length list of generated numbers, falls into a cycle, whose length is exactly 8532532; the start of the cycle is variable, and depends on the initial values for randSeed and randDivi. Note: the values of 27268 and 32767 for the starting values of randSeed and randDivi were chosen to return, as the first random number, the same first random number generated by the DEC-BASIC Ref. LBMAA-A-D, 5-2 System function for the pseudo-random number generation management RANDOMIZE() the actual randomizing function, called by the RANDOMIZE statement. The algorithm (depending from current time) is the following: - get the module of current 'usec' measure to the sum of the value obtained summing months (12), days (31), hours (23), minutes (59) and seconds (59), which gives 184; - use a linear conversion of the module to get a new value for randSeed; the function is built for never returning values too close to zero or to randDivi; in particular, the range is [1/8*ranDivi, 7/8*randDivi] Note: randSeed is always odd (see getRandom). Ref. LBMAA-A-D 5.1.3, 5-3, "The RANDOMIZE Statement ONGOTO() The ON..GOTO/ON..THEN statements. Note: the ON..GOSUB statement is strangely not listed and its absence was tested on the LCM. Ref. LBMAA-A-D 1.4.6, 1-9, "ON-GO TO Statement" IFTHEN() The IF..THEN/IF..GOTO statements, Note: THEN and GOTO are perfectly interchangeable, since THEN is turned to GOTO into tokenize(); this enables writing THEN 80 instead of GO TO 80 Ref. LBMAA-A-D 1.4.5, 1-9, "IF-THEN Statement" Ref. LBMAA-A-D 8.2, 8-2, "String conventions" GOTO() The GOTO Statement - called also by IF..THEN Ref. LBMAA-A-D 1.4.4, 1-9, "GO TO Statement" GOSUB() The GOSUB Statement. Note: in the LBMAA-A-D there's no mention to the limit of the GOSUB stack (the higher level of allowed nested GOSUBs); but as tested on the LCM, this limit does not exist (or no error message seems related to this matter). I set this limit to LIMIT, which is sufficiently wide to host a great number of nested GOSUBs, but the relative error message is a new conception (if you happen to overpass 999 GOSUB levels, you've surely gotten something wrong!). The BAS17F.DOC file at the LCM, available in the DOC: directory, reports the following bug error correction: 222 BASIC DETECTS A RECURSIVE SUBROUTINE CALL ONLY AFTER THE SECOND CALL IS MADE. In any case, the LCM does not admit auto-calling GOSUBs, a normal behavior, after all (see error #126). Ref. LBMAA-A-D 5.2, 5-6, "Subroutines" Ref. LBMAA-A-D 5.2.1, 5-6, "GOSUB and RETURN Statements" RETURN() The RETURN Statement. Note: see the note in GOSUB() Ref. LBMAA-A-D 5.2.1, 5-6, "GOSUB and RETURN Statements" FOR() The FOR..TO..STEP/FOR..TO..BY Statements. STEP and BY are interchangeable - actually, STEP is turned to BY into tokenize(). if flag Pf[] is true initialize and perform the plain FOR; if flag Pf[] is false, initialize to current value (NEXT was met before). Note: as tested on the LCM, the FOR/NEXT in the compiled version of a LCM BASIC program worked well even if NEXT was called before the relative FOR, because the addresses were pre-calculated in the compiling phase, and the back jump to FOR occurred after the initialization of the variable index; this had the side effect of not setting the starting value, but rather conserving the current value, updated with the step value; decb, being an interpreter, has to workaround this by means of the phantom flag array Pf[]; it works this way: - in normal execution, FOR finds it FALSE for index 'var'; so it sets start cycle value as in the FOR statement for index 'var' and sets Pf to TRUE; NEXT, in turn, will find it TRUE and will reset it to FALSE when it exits the cycle. - in an inverted execution, when NEXT is called before FOR, Pf[] is found FALSE; thus, NEXT knows that a FOR must be called in order to instantiate the FOR stack, but first it sets it to TRUE; FOR, then, will find it TRUE, and thus will set starting index 'var' to its current value + the step. - in either cases, NEXT resets Pf[] to FALSE at the end of the cycle. Ref. LBMAA-A-D 2.1, 2-1, "FOR and NEXT Statements" NEXT() The NEXT... Statement Note: see the note in FOR(). NEXT **must** be followed by at least the first variable name. Note: this version handles multiple addressing, like NEXT J,I,K Ref. LBMAA-A-D 2.1, 2-1, "FOR and NEXT Statements" FNEND() close a multiline DEF FN. Ref. LBMAA-A-D 5.1.6, 5-5, "The Define User Function (DEF) and Function End Statement (FNEND)" DEF() The DEF Statement for numerical functions; handles multiple arguments Note: this function is called in preParse() before RUN(). The label for FN must be in the A..Z range and not subscripted. Note: this function executes a storing only; the actual command (the function interpretation and execution) is performed into getFunc() for function FNX; in case of an inline DEF FN, the string is stored into an array called fn[].def; in case of a multiline DEF FN they are stored into an array of char pointers called fn[].lines[]. Note: the LBMAA-A-D at page 5-5 states that no string variable may appear as argument in a DEF function: "... string variables [...] are not allowed..."; moreover, nowhere in the manual there's a mention of the existence of string functions like DEF FNA$..., and this is confirmed by the LCM BASIC. Ref. LBMAA-A-D 5.1.6, 5-5, "The Define User Function (DEF) and Function End Statement (FNEND)" READ() The READ statement this function checks the next available item in the READ line, and stores in it the first available correspondent item (number or string) Note: READ and DATA work on two dedicated arrays, one for numbers, one for strings, whose activation flags are dataLimit and dataLimitS and whose pointers are dc and dcs; these arrays are built in the DATA section of preParse() (there is no DATA command, actually). Ref. LBMAA-A-D 1.4.2, 1-7, "READ and DATA Statements" Ref. LBMAA-A-D 8.1, 8-1, "Reading and Printing Strings" RESTORE() the RESTORE statement. Note: if used without DATA statement, a NO DATA warning is issued. Note: a RESTORE followed by a digit is interpreted as a RESTORE#. Ref. LBMAA-A-D 6.5, 6-6, "RESTORE Statement" LET() The LET statement (even for implicit LET) address simple assignment (e.g X=3) and multi-variables assignments (e.g. X=Y=T=3); the effective addressing routine is delegated to assign() for numbers and to strAssign() for strings. return TRUE if assignment can be accomplished, FALSE otherwise; Note: a multi-variable assignment proceeds from right to left, that is the first item is updated last; this is important in cases like LET A=B(A)=expression, where B(A) is updated with the 'old' value of A, while in LET B(A)=A=expression, B(A) is updated with the 'new' value of A; this conforms to the DEC-20 original behavior. Note: this algorithm doesn't consider the equal signs into strings (in which case they are 'characters') or into parenthesis (in which case they are logical operators). Ref. LBMAA-A-D 1.4.1, 1-7, "LET Statement" ***************************************** BASIC INTERFACE * **************************************** listingLen() return the length of the program in memory in chars (option --length) Note: the LENGTH command counts 5 for each line number, and counts characters in the listing (including spaces) as multiple of 5. Note: this algorithm does not return values matching exactly the output of the BASIC interface command LENGTH, but the magnitude is correct. Ref. LBMAA-A-D 9.7, 9-11, "Obtaining Information" renum() update inner references in a set of instructions, starting from the new line Ref. LBMAA-A-D 9.3.3, 9-5 Note: in the 9.3.3 paragraph there's no mention to the updating of the inner references, but this was tested on the LCM. This system routine is used into RESEQUENCE() and INSERTLINENUMBERS() listAndSave() wrapper for LIST. list file and re-save onto file if isReseqSave is TRUE (option -s). n1 and n2: limits of the LISTing required System function RESEQUENCE() perform the RESEQUENCE command (Ref. LBMAA-A-D 9-8) Note: the renumbering updates the inner references after GOTO, GOSUB, THEN and USING (if followed by a line number), ON..GOTO multiple addresses; Note: the command was used in the BASIC interface as 'RESEQUENCE n,f,k', where n = the new line, f = the old line from which the renumbering starts, k = the step the renumbering used from f to the end. Ref. LBMAA-A-D 9.3.3, 9-5, "Renumbering Lines in the File" INSERTLINENUMBERS() Insert line numbers before each line of the unnumbered BASIC file, taking care of updating the inner references and separating the lines with multiple instructions, separated by colon or backslash, and renumbering the whole file after that. System function printSpacedMiniTokens() print the minitokens found in a string with a space before and after (if any is missing), in all sections outside a literal string; used into LIST(); Note: this command prints directly the minitokens, the trailing spaces and the rest of the string; for the definition of minitoken see tokens.h at position 0. fd: the stream to which the print is done str: the string to be printed System function LIST() the LIST command fd is the output channel (stdout or file) on which LIST is performed Note: this statement is executed only by means of option --list/-l and is not available as a programming statement. Note: LIST, being executed after OLD(), preserves REM and tick comments and discard all the other kind of comments (the special markers) Note: if option --grace is used, it prints all line numbers in 5 digits (as was in LMC, as reported by the TYPE command, in the MONITOR, for BAS files), the listing is indented and statements are gracefully spaced Ref. LBMAA-A-D 9.2, 9-3, "Listing Files" for LIST, LISTNH, LISTREVERSE and LISTNHREVERSE segments OLD() execute the OLD command, loading file in memory Note: this statement is executed at start automatically and is not available as a programming statement. If one among '@', '&' or '#' is set as the first character of a line, the loading is stopped/suspended; this lets store documentation within the source file; this feature didn't belong to the DEC BASIC. Ref. LBMAA-A-D 9.1, 9-3, "Creating the File in Memory", OLD section RUN() the RUN command (Ref. LBMAA-A-D 9-12) Note: this statement is executed at start automatically and is not available as a programming statement. Ref. LBMAA-A-D 4.3, 4-4, "Executing the Program" Ref. LBMAA-A-D 9.5, 9-12, "Executing a BASIC Program", RUN n/RUNNH n section This algorithm can be interrupted by one CTRL-C, while the DEC BASIC on the LCM could be interrupted by two CTRL-C Ref. LBMAA-A-D 4.5, 4-5, "Interrupting the Execution of the Program" END() end program, accomplishing various tasks, such as closing opened channels, print timing, fixing graphical stuff and closing regularly as C requires. Note: as exit state, return the state passed to it as argument. Note: as the returned states, I use: EXIT_SUCCESS (which is 0) to indicate a proper successful execution EXIT_FAILURE (which is 1) to indicate a stop after an error condition EXIT_STOP (which is 2) to indicate an invoked (successful) STOP termination Ref. LBMAA-A-D 1.4.7, 1-10, "END Statement" (not much about it, though) Ref. LBMAA-A-D 6.3, 6-5, "STOP Statement" ***************************************************************************** The following functions constitute the whole parser engine: S() : the numerical expression parser getVal() : the single numerical value parser getFunc() : the numerical function parser priority() : return numerical operator precedence SS() : the strings expression parser strRel() : the strings relation evaluator (returns a truth value) conceived April 2008 for the dib project modified for vibes with strings insertion September-November 2009 updated and verified for decb - February 2013, February-April 2014 Copyleft Antonio Maschio - 2008-2014 **************************************************************************** SS() The PARSER for string expression; it's a general routine that contains also evaluation of functions and concatenation of strings in series if the + (plus) or & (ampersand) operators are used. The name SS() comes from S() and the second S means "strings". The address of starting position into the flow stream is pointed by p The algorithm is as follows: until there is a string in any form, append it to mypad; return mypad. strRel() evaluate a string expression, with operators <, >, =, ç (for <>), § (for <=) and ! (for >=); always return a truth value Note: in relations, any kind of string variable (even string vectors) may be used into the formula. Note: since evaluation of string relations is useful only into IF..THEN statements, they are recognized/used there, and not into SS(); see Ref. LBMAA-A-D, 1.3.5, 1-6, "Relational Symbols", where it's written: "Six other mathematical symbols of relation are used in IF-THEN statements where it is necessary to compare values." priority() return math priority for operator op, according to the following table: 6: the minus is evaluated immediately 5: (..) parentheses (caught in getVal) have the highest precedence 4: power (^ and **) 3: multiplication, division 2: addition, subtraction 1: <, >, =, <=, >=, <> (also for strings) 0: anything else has NULL priority and should cause error Note: string concatenation operators (in the syntax + and &) are evaluated directly into SS(); string relational operators (<, >, <=, >=, =, <>) are evaluated into strRel(), which is invoked only into IFTHEN() Ref. LBMAA-A-D 1.3.5, 1-6, "Relational Symbols" Note: PRIOR is defined as the highest priority returned by priority(), thus excluding the leading minus and the parentheses. S() The PARSER for numerical expressions The address of starting position into the flow stream is pointed by p The algorithm is as follows: an algebraic expression is turned into an RPN stack-based expression, with numbers and operators in order in their own stack; operations are performed from higher priorities down to lower and from left to right; priorities are given by the priority() function, which is part of this tool. Note: the name S() is retained from Spinellis version. Note: the < and > operators (with their companion <= and >=) may not give the same results of the DEC BASIC with big numbers, because of the different ways of storing numbers; for instance, 1E29*10 is not stored in the same way of 1E30, because of rounding errors in calculating the multiplication (the error propagates rapidly when big numbers are calculated in series). So don't expect the very same behaviors with big numbers. Ref. LBMAA-A-D 1.3.3, 1-6, "Numbers" less than < greater than > not equal <> greater than or equal >= less than or equal <= equal = power ^ subtraction - addition + division / multiplication * getVal() get single value (number, variable, function or array element) the case of the unary minus has been caught in S() The address of starting position into the flow stream is pointed by p getFunc() functions calculator ***************************************************************************** The following functions define general system environment subroutines: they are internal routines not directly accessible as BASIC statements. **************************************************************************** **************************************** STATEMENTS AID * *************************************** formatAnalyzer() Analyze the BASIC USING string for formatted output and convert it to C. This is the routine used by all the PRINT/WRITE USING statements; the purpose of this function is building a C-format string from the BASIC format string, to ensure a correct printing according to the required features; the real numbers printing routine is delegated to printFormattedNumber() and the real strings printing routine is delegated to printString(). BForm: the BASIC formatter string list: the sequence of what is to be printed (it follows the USING part) isWrite: the WRITE flag: if TRUE, prints a Line Number + tab (WRITE) ch: the output/file channel id Ref. LBMAA-A-D 11.2, 11-3, "Image Specifications" Copyleft Antonio Maschio - April 2011 printString() print a formatted string according to BASIC formatting, following rules of chapter 11 of the LBMAA-A-D (1974); str: the string to print us: a TUsing structure holding formatting data ch: the output channel id System function for the USING management printFormattedNumber() build the print environment of a float number according to BASIC USING formatter, following the rules of chapter 11 of the LBMAA-A-D; this routine uses the information contained in the BASIC formatter for building the 'us' formatter, leaving the physical printing of the number to outNum(). num: the floating point number to print us: a TUsing structure holding formatting data ch: the output channel id System function for the USING management intLen() find the length of the integer part of any number passed as double, up to INFF (suitable for outNum). If the number is negative, it is turned to positive (the minus sign is not counted because printFormattedNumber() arranges it). num: the float to be printed System function for the USING management outNum() emit a positive float number according to the BASIC format. num: the number to be printed us: a TUsing structure holding formatter data ch: the output channel id System function for the USING management checkNames() control if a file is already open (a file cannot be opened more than once, using FILES or FILE). c: the channel id to check fname: the file name to check return TRUE if a file already opened does exist, FALSE otherwise. System function for the file management setupName() check file name, returning correct file name with extension "BAS", if not specified for an unexisting file; deprive name of file specifiers $ or %. If an improper % character followed by number was chosen, len is -1. fn: the file name string mode: return character specifier '$' or '%' or NULL (e.g. $ or %) len: return len of string field if specified (e.g. $15) Note: this routine works with names for current directory; if names are contained into a path, this routine works if path does not contains dots; if it does, the ? FILE %s NOT FOUND message will appear, unless the complete name (with extension) is specified on the command line Note: fn must be an array of width COMPSCR = SCREENLINE + 1 System function for the file management testEOF() return TRUE if current file has passed the last character/item, or FALSE. Note: it does NOT control if stream is open. ch: the testing channel id System function for the file management getEOF() return location of last record for random access files Note: it does NOT control if stream is open ch: the testing channel id System function for the file management printContained() print a text into the MARGIN limit; returns TRUE in case of correct printing. ch: the output channel id string: the string to be printed (number or string) margin: the channel margin (for speed) System function for the printing management fillGap() In a random file, fill the intermediate elements of a random access file with spaces, whenever the index is moved at least one element over the last; used within FWRITECOLON() and in SET; No checking is done upon channel ch: the channel id actualIndex: the current value of the random file position System function for the printing management matReadAssign(result-matrix,type,rows,columns) iterate through matrix indexes to assign numerical DATA values var: the matrix code t: the matrix type m, n: matrix rows and columns numbers System routine for matrix management, used into MATREAD() isod(string-position) (IS a One Dimensional array) test if p points to a one (TRUE) or two (FALSE) dimensions array. p: (which is not touched) must point to the character after the opening '('. Note: to test eventually for a bidimensional array, use !isod() System function for the DIM management isaa(string-position) (IS an Array Assignment) test if the current string **begins** with an array assignment. p: (which is not touched) must point to the character that begins the array variable (e.g. 'A(' or 'A1('. System function for the DIM management issaa(string-position) (IS a String Array Assignment) test if the current string **begins** with a string array assignment p: (which is not touched) must point to the character that begins the array string variable (e.g. 'A$(' or 'A1$('. System function for the DIM management dimArray(matrix-index,type,rows-number,columns-number) dimension a numerical array allocating the necessary memory space. var: the index of the variable holding the matrix name T: matrix type, 1 for unidimensional vectors, 2 for matrices M: the row number (=0 for one rows matrices) N: the column number (=0 for vectors and one columns matrices) Note: the one-dimensional vector is supposed to be vertical (one column). Note: controls over M&N are supposed to happen elsewhere. Note: all elements of declared array elements are set to 0.0 = zero. System function for the DIM management getArrayVal(matrix-index,type,row-position,column-position) get value of an element of a numerical array, given its coordinates. var: the index of the variable holding the matrix name T: matrix type, 1 for unidimensional vectors, 2 for matrices M: the row number (=0 for one rows matrices) N: the column number (=0 for vectors and one columns matrices) Note: a two dimensional array may be referenced using the first subscript; Note: the second subscript defaults to zero (Ref. BAS17F.DOC 3.8) for DIM and to one (Ref. LBMAA-A-D, chapter 7) for MAT statements. System function for the DIM management setArrayVal(matrix-index,type,row-position,column-position,value) set an element of a numerical array to a specific value, given the value itself and the element coordinates. var: the index of the variable holding the matrix name t: matrix type, 1 for unidimensional vectors, 2 for matrices m: the row number (=0 for one rows matrices) n: the column number (=0 for vectors and one columns matrices) val: the double value to be stored. Note: a two dimensional array may be referenced using the first subscript; Note: the second subscript defaults to zero (Ref. BAS17F.DOC 3.8) for DIM and to one (Ref. LBMAA-A-D, chapter 7) for MAT statements. System function for the DIM management dimAssign() wrapper for setArrayVal(), to be called into LET(), for figures like A(N)=Expr1 or A(N,M)=Expr2. Note: the use of A0..Z9 is legal, as reported in the LBMAA-A-D 3.1. System function for the DIM management dimStrArray(matrix-index,type,rows-number,columns-number) dimension a string array, allocating the necessary memory space; var: the index of the variable holding the matrix name T: the matrix type, 1 for unidimensional vectors, 2 for matrices (currently not available) M: the row number (=0 for one rows matrices) N: the column number (=0 for vectors and one columns matrices) Note: N is not used, since string matrices are not implemented yet. Note: the one-dimensional vector is supposed to be vertical (one column). Note: controls over M&N are supposed to happen elsewhere Note: all elements of declared array are set to the void string. System function for the DIM management getArrayStr(matrix-index,type,row-position,column-position) get value of an element of a string array, given its coordinates. var: the index of the variable holding the matrix name t: matrix type, 1 for unidimensional vectors, 2 for matrices (currently not available) m: the row number (=0 for one rows matrices) n: the column number (always 0) Note: I keep the form alive, to implement the bidimensional arrays in some future. System function for the DIM management setArrayStr(matrix-index,type,row-position,column-position,value) set an element of a string array to a specific value, given the value itself and the element coordinates Parameter n is not used, since string matrices are not yet implemented; var: the index of the variable holding the matrix name t: matrix type, 1 for unidimensional vectors, 2 for matrices (currently not available) m: the row number (=0 for one rows matrices) n: the column number (always 0) Note: I keep the form alive, to implement the bidimensional arrays in some future. System function for the DIM management dimStrAssign() wrapper for setArrayStr to be called into LET() for figures like A$(N)=StrExpr1; usage of A0..Z9 is legal, as reported in the LBMAA-A-D 3.1. System function for the DIM management setINPUTarray() store value in a numerical array pos: the variable address value: the number to be stored Note: return updated position of pos System function for the INPUT management setINPUTnum() store value in a numerical variable pos: the variable address value: the number to be stored Note: return updated position of pos Service function for the INPUT management isARing() check if GOSUB is calling an address that is in the GOSUB chain; if so, the subroutine is calling itself Note: this behavior is described nowhere in the LBMAA-A-D, but I assume it behaves in a very compliant way, since I have tested some little programs in order to understand the behavior under stress conditions System function used into GOSUB() skipDEF() advance line number to next FNEND, whose presence had already been verified into preParse(). It's executed inside parse(). System function for the DEF FN management isSingleDEF() check if the DEF function starting at d is a single line DEF (return SINGLE) a multiline DEF (return MULTI) or a function with bugs (return FALSE) d: points to the characters after DEF (which must be 'FN') System function for the DEFFN management Note: I couldn't find anywhere in the LBMAA-A-D manual the fact that arguments of a DEF FN structure must be unsubscripted variables, but I tested it on the LCM. Ref. LBMAA-A-D 5.1.6, 5-5 calcDEF() calculates the DEF FN result for a single/multi line DEF FNF; data relative to DEF FN functions must have been preprocessed by preParse(), in order for calcDEF to run. res: the double value will hold the result of the DEF calculation p points to the character after FN, which contains the function index. return the updated position of p System function for the DEFFN management **************************************** TIME & STACK * *************************************** pushRUN() store current running state used into calcDEF() popRUN() restore current running state used into calcDEF() getTime() return current time structure getMin() return current min (0-59) getSec() return current second (0-59) getHour() return current hour (0-23) getDay() return current day (1-31) getMonth() return current month (1-12) getYear() return current year getMonthString() return current month string **************************************** OUTPUT FORMATTING * *************************************** Note: the FAC()/FCR() functions are strictly connected with the PAGE/NOPAGE/ features for the simulated terminal because they all use/set the page size limits FAC() Advance Carriage on a file stream FCR() Carriage Return onto file stream fillPage() pad current page with PA_VOID empty lines in case of PAGE statement set or paging overflowing (this behaviour, though not described in the LBMAA-A-D was nonetheless tested on the LCM emitNUM() output number (with sign) contained into formatted string num to stream ch or to string dest; update file counter ; a space follows always the number to stdout/file; if flag is TRUE, redirect printing to dest, otherwise to ch Note: dest width must be COMPSTR = MAXSTRLEN + 1 printNUM() format number num to a printing output according to DEC BASIC rules (Ref. LBMAA-A-D 6-4) onto stream identified by ch return updated position of counter (as yielded by emitNum) if flag is true, the number (in emitNUM) will be printed on string dest rather than on stdout Note: dest width must be COMPSTR = MAXSTRLEN + 1 Note: this function does not print anything, delegating emitNUM() getString() get a formatted string from user keyboard the ENTER key stop any input Note: the input is not capitalized, unless option -c is used assign() The assign subroutine for numbers - called by LET() assign a number to a number-variable variable name is taken from input string if a string variable is found, delegate strAssign(), passing it the address of variable and of assignment return TRUE if assignment went OK, false otherwise System function strAssign() the assign subroutine for strings - called by LET assign a string to a string-variable e contains the address of variable to be filled f contains the address of assignment System function **************************************** MATH & STRINGS * *************************************** isStrFunc() check if q points to a string function check(string1,string2) check if sb syntax is correct against sc; if it is, put a space in the position found, so that a subsequent parsing by S() may stop without issuing an error message from getFunc() and return true; if syntax it's not correct, return false. This is done in statements with some core-words, like IF THEN where the parsing of must stop just before THEN; check() inserts so a space in place of the T of THEN, causing the parser to stop there doExp(str, &base, &exp) find base and exponent of a number which must be printed in exponential form given the pointer to the string str containing the number in raw form. See the BCLM manual, for some output of numbers in exponential form Used into printNUM() power(base,exponent) powering function stripBlanks() strip blanks from src and copy stripped string to dest stop stripping while into a string in(letter-index,number-index) return index for vars A0..Z9 using a math algorithm, to ensure their memory space is not the same of variables A..Z; this avoids overwriting makechar() returns first character of string str as a string spaces() return a pointer to a string of v spaces up to LIMIT+1 characters turnToUpper() turn a string to upper in all sections outside a literal string; this means that with option -c lower and upper characters may be used for statements into the source; decb will in any case turn them to upper case statements Note: the following strncpy_() and strncat_() are my own personal versions for strcpy/strncpy and strcay/strncat. I must use mine because cpy/cat functions in C have erratic behaviors within different operating systems: - In Linux (tested on Suse) strncpy is error prone, and strcpy is preferred - in OpenBSD strncpy is preferred and strcpy should not be used - in Mac OS X 10.4 (the one I use) strcpy and strncpy both work The same is (arguably) for strcat/strncat. Since many reported strange things happening because of strcpy/strncpy (from error in compilation to completely wrong string functions output), I made up my mind and built these substitutes for both. strncpy_() dst: the destination string (which must have its proper dimension) src: the source to be copied onto dst dw: the destination width, the unity used to declare dst (for the sake of precision, if dst is declared as 10 characters wide, it means that text goes from pos. 0 to pos 8, and in pos. 9 is the last terminator; thus width equal to 10 means 9 characters at most for the string; dw must be put equal to the width, in this example 10; of course, any lower value is good.) The copy will take care of never going past the limit of dst, and to fill the rest of the space (if any) with nulls; System function replacing strcpy/strncpy strncat_() dst: the destination string (which must have its proper dimension) src: the source to be copied onto dst dw: the destination width, the unity used to declare dst (for the sake of precision, if dst is declared as 10 characters wide, it means that text goes from pos. 0 to pos 8, and in pos. 9 is the last terminator; thus width equal to 10 means 9 characters at most for the string; dw must be put equal to the width, in this example 10; of course, any lower value is good.) The copy will take care of never going past the limit of dst, and to fill the rest of the space (if any) with nulls; if dst is found not initialized (that is nowhere the terminator is found) it is initialized as void. System function replacing strcat/strncat Note: the following sprintfw() is my own personal version wrapper for snprintf(), which appear to behave erratically on my system (is does not work with function return values, but works with real arrays) and probably the same (with inverse properties) on other systems. This wrapper assures that only physical arrays are treated by snprintf() sprintw() the snprintf wrapper dest is the destination string, len is the string width (including the terminator character) format is the formatting string (with the same printf rules) ... is the printing string argument (may be a function return value) return the number of characters written in the host string System function replacing sprintf/snprintf Note: the following strstroq() is my own personal version wrapper for strstr(), to have it working only outside of quotes. strstroq() locate string pr into string pl this function has the same usage of C strstr(), but it works only if outside of a string, in BASIC normally surrounded by double quotes (strstroq = strstr Out of Quotes) System function replacing strstr storeSingleLineToCore() saves into core memory the line in argument, which must be constituted by a line number less than 100000 and a BASIC statement; return TRUE on successful storing; a void line is transparent for this command System function varCode() check which kind of variable is being parsed, returning parser var codes w is a char pointer to a variable structure see also its wrapper varcheck() defined as macro Ref. LBMAA-A-D 1.3.4, 1-6, "Variables" System function ***************************************** PARSING * **************************************** tokenize() substitute operators names with tokens or default statement names, according to the following list: 'ç' for <> '§' for <= '!' for >= '^' for ** BY for STEP GOTO for THEN DIM for DIMENSION LN( for LOG( and LOGE( CLOG( for LOG10( SQR( for SQRT( ;??; for The substitution has the advantage to consider operators as a one-char element to select, and statement/function substitution has the advantage that one command/function only has to be selected for execution, being certain the programmer's will is respected Note: using option -d (debug), the current executing line is printed, after tokenizing and blank-stripping, to see the 'real' string under evaluation System function preParse() A fundamental subroutine to be run BEFORE each process. Scan source in core memory and executes DATA and DEF recordings, sets DIM (DIMENSION) arrays spaces, opens FILES files, detects END, and in general activates all commands that must be preprocessed before program execution. Check also FOR-NEXT crossed references while GOTO, GOSUB and THEN addresses are checked into their relative functions. Note for subsequent add-ons: This command is executed BEFORE stripblanks() into parse(), so assure to do stripblanks() before processing the line, or use isblank() to skip all blanks!!! m: the array of the string pointers containing the program lines to be executed. System basic function parse() The parser It's a general purpose routine that executes lines contained into the m line-memory buffer, using B as a temporary buffer used for cleaning and tokenizing scopes System basic function exec() the statement execution subroutine (the core of the engine, which calls all the statements references to execute the commands) lpc is the address of the execution line (the first character after the line number). return TRUE if execution succeeds, FALSE otherwise Note: all statements already executed in preParse, are simple no-op, here, as well as statements REM and ' (tick) are. System basic function **************************************** ERROR TREATMENT * *************************************** perror_() print a string message based upon the following information data: - errNum is the error code number defined in errors.h and must always be given as first argument - ll is the line number in which the error is occurred; if null, the line reference is not printed (general errors); this must always be given as the second argument - all the other parameters should be the ones needed in the error strings, to fill the data in the % specifiers, according to their type The file name is added only in a CHAINed process, and only from the second file on; the first file run has no file name reference sigquit() end procedure if a SIGBUS, a SIGABRT or a SIGSEGV signal was trapped sigint() end procedure if a SIGHUP or a SIGINT signal was trapped ************************************** DOCUMENTATION AND LICENSE * ************************************* printHelp() help message printUsage() usage message printExistence() existence message printVersion() help banner printLicense() version message printWarranty() warranty message (required by GPL3) printConditions() conditions message (required by GPL3) ***************************************************************************** MAIN **************************************************************************** main() Here is where it all begins... parse options, get file name, setup environment and perform, at request, the following actions: - OLD Ref. LBMAA-A-D 9.1, 9-1, "Creating the File in Memory" - RESEQUENCE Ref. LBMAA-A-D 9.3.3, 9-5, "Renumbering Lines in the File" - LIST Ref. LBMAA-A-D 9.2, 9-3, "Listing Files" - RUN Ref. LBMAA-A-D 9.5, 9-8, "Executing a BASIC Program" - LENGTH Ref. LBMAA-A-F- 9.7, 9-12, "Obtaining information" - And a further option: insert line numbers into files born without them end of decb.c