Prose samples by Robert Elton Maas Technical specifications & documentation %% CMSSAVE.DOC (12k, 1985) When DUMPLISP is called, it surveys the memory layout creating a list of WArrays and other major sections of memory (the kernel itself, and the incarnation of PSLDSECT which contains pointers into the WArrays) which are treated as if they were also WArrays. It then writes a binary file (which should be in IBM VariableBlock format) which has some records containing LISP items (32 bits per item, a whole number of items per record) and other records containing strings of text (an arbitrary number of 8-bit bytes per record). On VM/CMS, records may be as short as one 8-bit byte and as long as 65535 8-bit bytes, however this code restricts records to a maximum of 60000 8-bit bytes so that there's some leeway to include trace info within the same records during debugging. This file has the following parts: - A record containing the fullword (32-bit) integer 39321 (hexadecimal 9999) followed by information needed by TEST2=DMSSTART. Currently the information (each a fullword integer) is as follows: old active size of HEAP (minimum allowed re-allocation), old total allocation for HEAP (default re-allocation), old active size of BPS (mininum), old total allocation for BPS (default), old active size of STACK (always 0, stack not currently saved), old total STACK allocation (default), old active size of BNDSTK (always 0, bndstk not currently saved), old total BNDSTK allocation (default), (the above items in bytes, but the following in items = words) old number of IDs (mininum, but equals total allocated currently), old total number of IDs allocated (default). - A record containing the fullword (32-bit) integer 39313 (hexadecimal 9991) followed by information needed by (N)PSLREST. Currently the information (each a fullword integer) is as follows: the number of WArrays to be dumped, DSECT offset for HeapLowerBound, DSECT offset for BndStkLowerBound, DSECT offset for StaticFloatBuffer, DSECT offset for StaticFloatItem, (those items because corresponding WVAR needs updating when target WArray or datum moves during relocation) ID# for LambindArgs*. - Two records for each WArray, one of which contains (as fullword integers) the offsets and pointers and the integer telling how many characters are in the name of the WArray, and the second of which contains the characters of the WArray name. Note they are grouped by WArray, i.e. Array#1.OffsetsEtc, Array#1.Name, Array#2.OffsetsEtc, Array#2.Name, ... Array#n.OffsetsEtc, Array#n.Name (See below for semantics of offsets and pointers). - A record containing just the integer 39321 (hexadecimal 9999). - The contents of the specially-important WArrays in the order PSLDSECT, SYMFNC, SYMNAM, SYMPRP, SYMVAL, BPS, HEAP (see below for details of format of WArrayContent). - A dump of the PNAMES of all the identifiers except the first 128 (the ASCII characters) (see below for details of format of IDNames). - A record containing the integer 39321 (hexadecimal 9999) twice. - The contents of all the WArrays not dumped above (see below for details of format of WArrayContent). - A record containing just the integer 39321 (hexadecimal 9999). %% DM2SIM.WRU (23k, circa 1979) Overall design of multiple-process smart-terminal emulator This discussion will ignore details of interrupt handling. Let it suffice to say that there are five processes active, one for each of the four ports (Modem-Input, Modem-Output, Beehive-Input and Beehive-Output) and one background job which is ordinarily a simple loop that does nothing useful and which runs when none of the four port-handling processes are running. The four port-handling processes are all activated upon receipt of the appropriate interrupt (character arrived in UART, for the two Input handlers, and character-transmit-done while XMTIRQ enabed, for the two Output handlers) whereas the background job runs only when no interrupts are in need of service. Port-handler routines which require extensive compute time during which several additional interrupts could happen, are run with CPU interrupts enabled; at present this applies only to the Beehive-Output process. Processes BI and MO are rather simple. BI takes characters from the terminal (the Beehive) and stuffs them into a MO-buffer, activating process MO if necessary. MO takes characters from the MO-buffer and sends them to the modem-UART at the earliest available time. If the character from the terminal is ctrl-shift-L, process BI eats one additional character before deciding what to send to the MO-buffer (or deciding to enter command mode or to perform some other special function). A complete copy of what is supposed to be on the screen is kept in RAM. Process MI receives DM2500 commands from the modem and upon getting a complete command it performs the appropriate update upon the screen image in RAM. To make it possible to complete this update within 1/30 of a second, in particular for the case of insert-line or delete-line, there is a "line-map" which is a table telling for each row on the emulated screen which sub-array in the screen-image buffer corresponds to it. There is also an inverse line-map which goes the other direction, telling for each sub-array in RAM which line on the screen it corresponds. Whenever an insert-line, delete-line, or scroll occurs, both the line-map and the inverse line-map are appropriately updated. There is another table that tells for each line whether the data in the subarray is (1) completely correct and written onto the Beehive screen completely (2) correct but only partly written onto the Beehive, the characters still needing updating having their '200 bit on in the subarray (3) correct but totally in need of being written onto the Beehive, the '200 bits not yet being set properly (4) invalid because all data on this line was supposed to have ben cleared but we haven't had time to change all 80 characters to spaces yet. Depending on which process notices the deficiency, conditions (3) and (4) may be corrected to condition (2) by either process MI or BO. (4) is corrected by replacing each of the 80 characters by '240 which is with the '200 bit on to indicate need to put on Beehive. (3) is corrected by turning on the '200 bit in each of the 80 characters without affecting the other 7 bits representing the character value. In either case condition (2) obtains after the fixup. Making updates to change (2) to (1) will be discussed below with the BO process. ... One extra feature of this emulator is the BELL count. Dinging the bell can be done at any time since it doesn't move the cursor or otherwise affect what is going on during refreshing. To keep things simple, bells are given higher priority than other screen refreshes. When a bell (ctrl-G) comes in from the modem, the bell count is incremented. At the very next opportunity, even in the midst of a co-routined cursor positionning or other complicated process-BO function, a ctrl-G is sent to the Beehive instead of letting the normal BO process execute normally. When the bell count reaches zero, when the ctrl-G has finished transmitting to the Beehive (and the UART interrupts accordingly) process BO is resumed where it left off. Other conditions can increment the bell count including illegal DM2500 characters received, lost data due to typing faster than 30 cps, and other impossible conditions. The bell offers a convenient way to signal all these conditions to the user without upsetting or complicating screen refreshing. %% doc.txt (20k, 1992) The goal is to transform a bitmap image into a set of lines, each line represented by an ordered list of points running along the line. The general method is to draw contours around the perimeter of each piece of line to isolate that piece from other clumps of black pixels, calculate the x,y coordinates and slope along these pieces, and splice together the various pieces of a line. (A line may be broken because it is a dotted or dashed line, because it's so thin that scanning noise or resampling during rotation breaks it in random places, or because grid lines that cross the line have been removed.) * Creating outline bitmap: The outline of white pixels immediately adjacent to black pixels of the line (called "trace edges" in HyperCard), has a lot of points in common with the outline contours we want to generate. Accordingly when picking a starting point for locating and tracing along an outline contour, the outline bitmap can be used as a checklist of places to try. To create the outline bitmap we perform the bitwise OR of the original data in nine positions (offset -1 0 and +1 in each the X and Y direction), then turn off the bits of the original bitmap. For efficiency in the first step, we OR together just three copies offset along just one axis, then OR together three copies of that intermediate result offset along the other axis. * Creating outline contours: The outline bitmap created above is used as a checklist of places to start tracing the path of a contour. As a contour is traced, all the pixels on or adjacent to the contour are erased from the outline bitmap so that black bits remaining will always result in starting new contours instead of repeating already-done contours. The result will be a data structure representing the sequence of pixel coordinates around each outline contour. The toplevel loop simply searches for a pixel that is still black, and checks that pixel and its eight neighbors to find which looks like the best place to start a contour. A Sobel 3-by-3 edge-detector matrix (1,2,1; 0,0,0; -1,-2,-1) is correlated with the original data at all eight orientations centered on each of the nine pixels, and the largest value is accepted providing it's at least two (the largest possible value is four of course, so there will typically be lots of equally good results, and which one we pick is arbitrary). Once an acceptable starting point and orientation is chosen, we place an imaginary "body" at the starting point and an imaginary "hand" on a black pixel which is adjacent in the direction the Sobel operator said was the strongest black response, or if that point is white then on one of the adjacent black pixels clockwise or counterclockwise 45 degrees from that point as viewed from the "body". To trace all the points on the contour, we now "walk" forward, keeping our "body" on a white pixel and our "right hand" on an adjacent black pixel. We continue walking in that way until we return to our starting point. ... %% FLASH.WRU (6k, early 1970's) The "MAAS BINARY FLASHCARD SYSTEM" is a new and effective method for studying rote-memory items such as foreign language vocabulary. This is necessary before more complicated grammatical structure can be understood, although most students find it easy enough to pick up enough basic vocabulary that they don't find any problem here. However, the student who has a terrible time picking up enough vocabulary to understand grammatical examples needs some way to memorize something. When other methods fail, such a student may wish to try my system. ... INFORMAL GENERAL DESCRIPTION OF THE PRINCIPAL OF THE SYSTEM: After you have learned some words, you don't look at them for one day. At the end of that time, you quiz yourself on them, and any that are incorrectly answered are returned to the "hurdle" pile. The ones you get correct are put in a 2-day delay, that is, you don't look at them for 2 days, then you quiz yourself again. The ones you get right go in 4-day delay, etc. Each time you get some cards correct, the time is delayed. As I write this, there are some cards which I learned 2 1/2 years ago and are currently in a 512-day delay waiting for the next quiz next year sometime, after which the ones I get correct will not need quizzing for 1024 days (about 2 2/3 years!!). The process of learning cards in the first place works on a similar principal, except that to cut down overhead you double the number of cards you look in the meantime (between consecutive quizzes of the same card) instead of doubling the time interval. %% FOR370.DOC (18k, circa 1988) 1. General Capabilities: Providing that a desired FORTRAN or other FORTRAN-compatible program or sub-program conforms to IBM 360 OS calling conventions and to FORTRAN-77's way of handling parameters and return values, PSL provides a way to convert LISP objects into FORTRAN-referencible parameters, call a FORTRAN/IBM subprogram, and convert the returned value back to a LISP object. It also mostly correctly handles side-effects that occur inside LISP when the FORTRAN/IBM subprogram modifies the memory locations pointed-at by its reference parameters, both simple variables (one word or double-word) and arrays (whole blocks of memory starting at the address passed as a parameter). PSL also provides a way to call a toplevel FORTRAN program that was previously loaded into memory, although this is not terribly useful because it can be called only once before it has to be re-loaded. 2a. Machine-independent overview of method: All data is allocated within the PSL heap. Data or pointers to data are passed to Fortran subprograms. A lookup table in PSL allows individual subprograms to be called by name. Results from function subprograms are used to create (allocate) new PSL heap objects. Whether PSL and Fortran reside in the same address space or in different spaces, and whether simple addresses or interprocess pointers are used to point into the PSL heap and to call Fortran subprograms, is dependent on the particular installation. 2b. IBM VM/CMS overview of method: Both the PSL and the Fortran programs reside in a single address space on a single "virtual machine". PSL runs as a nucleus extension, while Fortran runs as a module. References in Fortran to PSL heap data, references in PSL (via the lookup table) to Fortran subprograms and toplevel programs, and parametric references to Fortran subprograms, are all represented as (virtual) machine addresses. All the subprograms callable from a given setup reside within a single module which is built via the loader from compiled Fortran and assembled Assembler source. A pair of files, the module and a text file containing the lookup table as a PSL s-expression, are kept around to represent a given setup. When PSL calls Fortran, registers are juggled and an OS call is performed. At present Fortran cannot call PSL. When the Fortran subprograms are in a TXTLIB (library of individually loadable control sections), and it is desired to make them all available to PSL, first the map of the TXTLIB is created, then PSL parses this map to create a list of entry points, then PSL writes out an assembler program that consists solely of a sequence of references to the entry points, then that assembler program is assembled and linked with the TXTLIB to force all entries to be loaded into the module. Regardless of how the module is created, the map of the module is parsed by PSL to create the lookup table, which is written as an s-expression to the disk for fast access (parsing the load map takes about 5 minutes whereas loading the s-expression jump table takes less than 1 second). %% End of samples of technical specifications & documentation