AnalytiCalc Your Very Own Spreadsheet ( ... that won't break your budget) available in source code courtesy of Glenn C. Everhart (and with thanks to the DECUS library which supplied the CALC program which inspired this one and furnished some of its math logic..) AnalytiCalc is a spread sheet and calculator program written in Fortran to make it relatively easy to put onto a variety of different machines developed out of a program called CALC in the DECUS library. It is written in a dialect of Fortran-66 to minimize the difficulty of porting it to other than DEC systems. In AnalytiCalc, there are two sheets to keep in mind. One of these is the "physical" sheet, which contains some number of rows and columns named by CCrr where CC is an alpha name of column (starting from A thru Z, then AA, AB, AC, etc., and for as many letters as it takes, and rr is a row number in decimal starting from 1 thru the max you allow. These cells are continuous but may not all be used. A direct access file is used to store their format information; the size of this file is determined by the size of the physical sheet. 128 bytes are used for each entry. These break down into 109 for the number or formula entered by you into that cell (which may be a multistatement line), 1 for a check, 1 for validity information, 8 for display format, and a few spares for flags. Since this file is accessed frequently, it should be on as fast a disk as you have. The second sheet is the "display" sheet, which is the part of the physical sheet you actually display on your VT100 terminal. (You may customize the terminal by modifying the UVT100 routines; these control actual special terminal functions. AnalytiCalc does not ask very much of a terminal, so most any reasonable screen should be usable.) You may associate ANY part of the physical sheet with the display sheet. This may go down to the individual cell if you like. At startup, the upper left part of the physical sheet is the display sheet; change it at will. You may specifically elect to make a column in the physical sheet into a row of the display sheet or vice versa, but normally will select rows to go into rows and so on. The computations in the physical sheet may relate values to other values elsewhere on the sheet by equations for each cell based on other cells. These equations may refer to certain physical sheet cells by name in FORTRAN-like syntax, or they may be written to refer to cells relative to current position in physical or display sheets. When you copy cells around the spreadsheet, you may specify whether the variable names should be adjusted for the motion or not, facilitating reuse of formulas. However, the current-location-relative forms are not altered in either case. They are useful where the display sheet is some arbitrary projection onto parts of the physical sheet and it is desired to have formulas work and make sense over that projection rather than over parts of the physical sheet which may be scattered all over. While the current-position-relative forms are not relocated when moved, they work without needing to be altered. These forms (current location relative) are of the appearance D#nn#mm or P#nn#mm where D implies display sheet and P implies physical sheet. The D form is frequently more useful, but only if those cells will always be on the displayed page. The numerics nn and mm may be either positive displacements or negative ones with the - sign. Thus the following are valid: D#3#-6 Current col+3, current row-6 P#0#2 Current column, 2 rows down D#-5#2 5 columns left, 2 rows down. You may also specify a range of variables by giving their names separated by : (colon) with no intervening spaces. This range must lie either in the same physical row or column and begin with the upper left end of the range. In expressions, there are 27 accumulators global to the whole sheet, named A thru Z and %. % is always the last computed number and A-Z may be used in multistatement lines. These are lines of equations with individual operations delimited by the character "\" (backslash). In computing the sheet, AnalytiCalc goes over the cells one at a time and computes any cells that are valid (i.e., for which there has been anything ENtered). You may define a sheet that will require multiple passes to compute. Use the Recalc command to do this. Note that the result is left in the current physical location. For functions of more than 1 argument (MIN,MAX,SUM,AVG,STD), no = is permitted. For other expressions, normal CALC syntax is OK. However, note that no such expression may exceed 80 characters. Break up long expressions if you require them. When you drop into Calc, variables are left with values set and variables A1 on exist and may be given precisions up to double precision (8 bytes are available). However they will not in general be passed back to the spreadsheet but may be used as hidden accumulators for the spreadsheet. There are two commands to let you put numbers from Calc into the spreadsheet's formula file (*P positions you in the physical sheet while in Calc, and *W writes the current contents of the % variable into that cell), but they are not very convenient and probably would be little used. The variable % is set up for single-argument functions or expressions, and also for the MIN/MAX/AVG etc. functions. The syntax of the *P command is either just *P (which causes Calc to prompt for column and row), or *P Variablename (which moves the current location to the named location), or *P@ variable1,variable2 (which moves to the location which is defined by variable 1 and 2 as col. and row). Some other commands exist for the main benefit of AnalytiCalc in Calc. The command *F LABEL will evaluate the % variable. If it is positive and nonzero, then the current input line will be rewound and the program will (silently) seek a line of the form *CLABEL. This allows loops to be defined with % as the counter. The command *QF or *QW file ?searchkey? will open file and look for lines starting with searchkey as given (with _ as a wild character). Upon finding this, it will look for the and if it finds it, it will select the part of the line in the file between the 2 characters given (which may be alike) as they are first found, and will do one of 2 things. If the command was *QF, then Calc will Float the value found there (attempting to use a large E format) and return it in %. If the command was *QW, then Calc will Write the selected part of the record into the formula at the current physical position on the sheet (which may be reset by an IMMEDIATELY PRECEDING *P command). The record must BEGIN with the search key (at present). It is up to the user to make sure the formulas copied make sense. This however allows access to sequential files of data which can be created by an editor, word processor, etc., and which might hold keyed information that the spreadsheet should be able to manipulate. For an adequate introduction to the commands and uses of AnalytiCalc, see the manual file (AnalytiCalc.RNO). The following is a crude and partial listing of some of the commands available: COMMANDS INCLUDE: E = ENTER NUMBERS OR FORMULAS M = MOVE DIRECTION (1,2,3,4 = U,D,L,R) D = DISPLAY CHARACTERISTIC CHANGES DISPLAY ALTERING SUBCOMMANDS: DL V1:V2 RN:M OR CN:M - DISPLAY VARIABLE RANGE V1:V2 AT DISPLAY ROW OR COL N THRU M. This means you move physical sheet variables V1:V2 onto the display sheet either going across a Row (Rn:m) or down a column (Cn:m) on the display sheet for as many cells of the display sheet as there are variables in the range you specify. The labels on the sheet are updated to correspond to the physical cells used. However, the n:m in the Rn:m or Cn:m are display sheet numbers, not physical cell numbers. Thus, you must use the display column and row numbers (Columns are labelled, e.g., B = 2, 2 being the display sheet column number), not physical numbers. Display sheet rows and columns are always counted from 1 to a maximum. DF V1:V2 [FORMAT] SET FORMAT FOR DISPLAY OF V1 THRU V2 TO [FORMAT] (NOT INCL. []) A OR L DESIGNATOR SAYS SHOW TEXT IN FORMULA BUFFER. ELSE SHOW NUMBER VALUE AT THAT LOC. DT V1:V2 F OR I - SET NUMERIC TYPE OF V1 THRU V2 TO FLOAT OR INT. DW N,M - SET WIDTH OF COL. N TO M CHARS WIDE. This will redraw the screen. You may set any column width independently of others. DB MC,MR - SET MAX COLS TO MC, MAX ROWS TO MR. V = VIEWSCREEN UPDATE. REDISPLAY EVERYTHING FROM SCRATCH. VF = VIEW BUT DISPLAY FORMULAS ALL LOCS. C = COPY NUMBERS/FORMULAS/DISPLAY STUFF(FORMAT)/ALL 1,2,3,4 = MOVE CURSOR UP,DOWN,LEFT,RIGHT 1 ROW/COL (THESE DO NOT INVALIDATE CALCULATION SO RECALCULATION IS NOT DONE FOR THESE COMMANDS.) R = RECALCULATE SHEET. RM = Recalculate Manually (inhibits recalculation until an R command is given to reenable it and perform a recomputation. This speeds up entry of data on large sheets. K = DROP INTO CALC CALCULATOR (*E RETURNS TO SHEET) L = LOCATE CURSOR (MOVE TO POSITION ON SHEET) (L VARIABLE IS THE COMMAND, AND IT LOCATES ORIGIN ON PHYSICAL SHEET. WILL ALSO MOVE CURSOR ON DISPLAY SHEET IF THAT CELL IS DISPLAYED, BUT OTHERWISE DOES NOT DISPLAY THE NUMBER.) Z = ZERO FORMULA/NUMBERS (OR ALL SHEET) ZERO VARIABLE ZEROES THAT VARIABLE ZERO VARIABLE1:VARIABLE2 ZEROES THAT RANGE (ROW OR COL) ZA command zeroes all of sheet. ZE v1:v2 zeroes range only. The command is ZA (you will be asked if you mean it; Y confirms, anything else abandons Zero command, or ZE V1:V2 to zero the range V1:V2, which just does it. X = EXIT (RETURNS TO OS) S = SETUP (Allows reset of title, default display format for floats, and global col widths and the like.) P = PUT NUMBERS TO FILE. Actually, P and G files are ASCII but in a funny format. Locations are based at current physical position however, and a maximum displacement down and right is asked for. Cells will be saved or loaded only within that displacement. This allows you to merge all or parts of spreadsheets, or to save only parts of sheets. On load, minimum displacements will be asked for also. This means you can load a saved sheet whose maximum and minimum horizontal and vertical coordinates are specified. Thus you can get whatever part of a saved sheet you need to merge with your current one. Formulas, types, and values are saved/restored. G = GET NUMBERS OUT OF FILE. USES CURRENT ORIGIN FROM L COMMAND OR 1,1 TO ENTER NUMBERS (ALLOWS COMBINING DATA). Note the lower end of restore ranges counts starting at 1, not 0. W = WRITE SCREEN ON PRINTER (HARDCOPY FORMAT APPROX. AS DISPLAY.) @file Starts reading commands from file. Only one level of indirection is supported and commands must be exactly as they would be from the terminal. Control returns to the terminal on EOF. OR var or OA var = Origin set (Absolute or Relative). These commands are the way to do a quick scroll around the physical sheet. They will establish the origin of the display sheet (the upper left part of the display sheet) as the physical variable location named. In OA mode, the display sheet's map is rewritten starting at 1,1 and continuing to the current display bounds as defaulted or set by DB n,m command. In OR mode, the map is rewritten right and down from wherever your current display position is only, giving the effect of windowing quickly. Note that the DL command permits you to further refine windows and have as many as you want on your sheet. NOTE THAT N-ARY FUNCTIONS ARE FNAME[ARGS,ARGS,...] AND RANGES ARE CELL1:CELLN. MULTIPLE COMMANDS IN FORMULA ARE DELIMITED BY \ CHARACTER. ENTER COMMAND EN expession. expresion may be numbers/text/formulas MOVE COMMAND (set MOTION DIRECTION AFTER ENTER) M1,M2,M3,M4 MOTION DIRECTION IS U,D,L,R COPY NUMBERS COMMAND COPY (NUMBERS,FORMAT,DISPLAY,ALL) CV=COPY VALUE, CD=COPY DISPLAY FMT, CF=COPY FORMULA, CA=COPY ALL Ca V1:V2 V3:V4 COPIES FIRST RANGE TO SECOND. 1,2,3,4 POSITIONING COMMANDS. These correspond to Uparrow, Downarrow, Leftarrow, and Rightarrow (in the same order as on the VT100 keyboard). LOCATE CURSOR ORIGIN (Go directly somewhere on the phys. sheet.) FORMAT IS L VARIABLE ONLY 1 VARIABLE NAME TO BE ENTERED. ZERO COMMAND ZA OR ZE V1:V2 PUT NUMBERS OUT TO FILE USES RELATIVE FORMS TO CURRENT POS. PD = PUT OUT DISPLAY SHEET. PP = PUT OUT PHYSICAL SHEET. Customizing: The VAX version of AnalytiCalc uses only FORTRAN writes for terminal output. Nevertheless, there are a few things to watch out for: 1. The RECALC file's DOIF subroutine calls a routine called DOSTMT which is also the caller of the routine calling DOIF. As long as your machine uses a recursive calling sequence as do VAX, PDP11, or generally stack-oriented computers, this presents no problem. However, if you have a nonstack machine, this could clobber your return and blow AnalytiCalc's mind. The solution is to make another copy of the DOSTMT routine and have DOIF call it; nested IFs are syntax errors anyhow and this would solve the recursion problem. 2. There are some OPEN statements whose function is to arrange that FORTRAN should ignore all carriage control on output to the terminal (called TT: or TI: on VAX or PDP11 respectively) so that the Fortran writes to logical unit 6 do not cause any extra carriage return/linefeeds to occur. If you can't get that to happen with these OPENs, you need to find another way. Also it's assumed that the ESCapes can be typed by FORTRAN. 3. The terminal should not be in the "autowrap" mode where it gets a CR,LF from the OS every so many characters. 4. If the terminal is not a VT100 equivalent (or maybe an ANSI 3.64 type; not much VT100 stuff is used that's not 3.64 compatible), you must modify UVT100 to generate the control codes needed for cursor position. All special cursor control is done there. 5. There may be some debug code left with first character D in column 1. If so, treat it as comments or remove it. The DEC compiler ignores such lines unless told to do a debug compile. 6. The syntax of CALL ASSIGN(logical unit, array) is to assign the file in array, terminated by a null byte, to that logical unit. Do it differently if you must. 7. Assumptions that Logical*1 variables are a byte long and may contain characters are widespread herein. If you have a machine like a PDP10 or DEC20 with 36 bits, try to get it to somehow treat characters as some submultiple of a word and fix up the equivalences. Byte addressed machines should not have much trouble with this code, but there could be problems in some cases with the REAL*n, INTEGER*n and so on declarations. IBM Fortran probably will buy them but not all others. The byte variables mostly just contain characters or may be treated as though they did. The exception is the FVLD array in which they are treated as small integers. Each array element corresponds to an element of the physical sheet and has a value meaning: -3, -2 Text, but contains no alphas -1 Text, contains alphas 0 Cell not initialized 1 Number/formula, contains alphas (recalculate every time) 2 Number, no alphas, already computed binary value 3 Number, no alphas, must still compute binary value Numbers outside this range are undefined but the -/0/+ significance is all that is tested in most places. The +2 is tested for equality. 8. To convert numbers for printing, the ENCODE statement is used. Its syntax is ENCODE(count,format,array)list where count = maximum number of characters to convert format = valid FORTRAN format designator (an array most times) array = the array which gets the character string resulting from the conversion list = the normal Fortran I/O list. If you don't have ENCODE, you may be able to replace it with WRITE/REREAD on a scratch file or something similar. There are only 5 or so Encodes in the program, so the job won't be too tough to replace them once you figure out how to do it on your machine. I also used ENCODE to test the Formats entered by the DF command. That is done prior to writing the formats to disk so that a format that will crash Fortran doesn't corrupt your working file on disk and prevent recovery. The Q start option or the Y start option can recover the sheet, minus the new format, if all is well. Note the Fortran 77 read/write to arrays could be a suitable substitute for encode/decode. 9. Overlay commands are supplied; these may be crudely followed on other systems too. 10. To permit fewer columns, if references to % were isolated and its entry in the TYPE array isolated, the restriction on the permitted numbers of columns could be lifted. These are the major things to look for when converting AnalytiCalc to a different environment. Be aware there are other differences between Fortrans, but not too many will be seen, hopefully. The author will appreciate hearing about, and preferably receiving, copies of sources to AnalytiCalc that anyone getting it modifies either to add functionality or to get it to a different machine. Please note it's copyrighted for non-commerical use only, so if you want to make a commercial version, you MUST get in touch. I will be more favorably inclined to permitting sales of modified versions to those who send me machine readable sources. A recent effort converted the PDP11 version to a Fortran 77 compiler. Issues encountered were: LOGICAL*1 arrays needed to be changed to CHARACTER*1 Because byte ordering was not guaranteed and assigns of int=char or char=int didn't always work, the functions int=ICHAR(char) and char=CHAR(int) were needed. To handle cases that had the char as a SIGNED value, I needed my own function JCHAR which looked at the ICHAR output and if it was greater than 127 subtracted from 256 to give the desired result. Since int=int.and.int works via masking in DEC Fortran it was used that way. Other fortrans don't all allow this and complicated EQUIVALENCES are needed to do logical operations. If the compiler doesn't do logical ops via booleans, you lose again and need functions instead. If your compiler lacks BLOCK DATA you have to put all the code in a subroutine (which can be overlain). The parameter statement is a problem if missing. Use a generic macro processor to convert parameters to numbers as needed. Watch out if it's flaky. Console I/O may not be able to avoid the Fortran included CR/LF characters. It may need to use a subroutine to dump characters directly. In this case, F77 internal writes to char strings can be useful. Limitations to 127 character strings are a pain but they are not really a problem here; there never is this long a string needing to be an internally read/written string. Just dimension the char string a little short; it's equivalenced to the right length string as a char*1 string. Missing ENCODE/DECODE can be replaced by internal writes. Note one ignores Fortran carriage control on these. Default logical unit assignments may not be as easy to control on non DEC OSs. Where integer precision is controlled by source code, one must be careful to define new functions to convert between different integer lengths and floating point. System dates may require assembly language calls to get to. Paint out multiprecision accumulators wherever desirable to shrink the program. For speed on micros, any external HELP file needs to be random access; sequential read is TOO SLOW. Especially on MSDOS and the like where no notion of contiguity exists so all accesses may require MANY disk accesses. Overlay schemes in MSDOS run MUCH more slowly than PDP11 since there's no contiguous allocation and loads seem to require full file open logic. At least 1 and maybe 2 orders of magnitude speed difference. Glenn Everhart 409 High St. Mt. Holly, NJ 08060