COMPANY : Information Appliance Inc. (IAI) AREA : Design Notes TITLE : Cat Technical Documentation: Editor Documentation DATE : 1987 AUTHOR : LC, PB (Paul Baker) SYSTEM : Cat @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ JEF RASKIN COPYRIGHT NOTICE -- APRIL 1997 @ This work includes material copyrighted by, trademarks of, and patents held @ by Jef Raskin (jefraskin@aol.com / 8 Gypsy Hill / Pacifica CA 94044 USA). @ Right to copy, use, modify, and distribute for personal, research, and @ educational purposes is granted to anyone who affixes this notice to all @ copies. Commercial rights reserved. If you make money or plan to make money @ by using any of this or work derived from it, contact me to arrange for @ commercial licensing. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ JEF RASKIN COPYRIGHT NOTICE -- APRIL 1997 Cat Technical Documentation -Editor Documentation- (C) Copyright 1987 by Information Appliance Inc. All Rights Reserved The Cat system is protected by one or more patents pending; all text, code, and circuitry is (C) copyright 1987 by Information Appliance, Inc. This document is confidential and includes trade secrets and other proprietary information. Its disclosure is for limited purposes only and its contents may not be used or further disclosed in whole or in part without the express written permission of Information Appliance, Inc. Table of Contents Part I. Editor Basics 1. Pointers and Data Structures 2. Text Display 3. Ruler Display 4. The Cursor 5. What's in the Text 6. Typing, Erasing, and Copying Text Part II. Text Commands 7. Character Style: Underline, Bold, Caps 8. Paragraph Format: Left Margin, Right Margin, Indent, Set/Clear Tab, Paragraph Style, Line Spacing 9. Document Format: Document Lock, Local Leap 10. Leap 11. Drag 12. Copyup Part III. Special Commands 13. Keyboard, Learn, KBI/II 14. Sort 15. Calc 16. Spelling Checker: Add Spelling, Spell Check Leap 17. Explain 18 Titles 19. Disk 20. Communications: Send, Phone, Send Control 21. Print 22. Set Up 23. Index _______________________________________________________________________________ 1. Pointers and Data Structures _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 4/22/87 Second Draft LC 5/4/87 Fixes LC 6/5/87 More Fixes LC 6/30/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The data maintained by the Cat editor is kept in an area called "the text". The addresses of important areas/locations in the text are kept in many system integers. The basic data structure used to hold formatting information about the text is called a "control/format array". The 3 main data structures used to maintain the text, the #ctrl array, the window table, and the interval table are each comprised of one or more control/format arrays. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 The Text 3 What the Text Is 3 Where the Text Is 4 Pointers Used to Maintain the Text 4 Beginning of the Text Area 4 Start of the Gap Region 5 The Second Text Partition 5 The Undo Buffer 7 Editor Data Structures 7 Control/Format Array 7Transient Format Information 8 Paragraph Format Information 9 Units Used in the Control/Format Array 10 Document Format Information 11 The Control Array 11 The Window Table 12 The Update Table 12 The Interval Table 13 How Control/Format Information is Obtained 14 More on Intervals 14 How the Interval Table is Used 15 The Top Four Intervals 16 Routines 16 Text Maintenance Routines 17 Window Table Routines 18 Update Table Routines 18 Interval Routines 20 Wrap Routines 21 Routines Which Get Specific Control/Format Information 22 Summary _______________________________________________________________________________ List of Illustrations 1.1 Location of the Text 1.2 The Text 1.3 Cursor Logistics 1.4 Control/Format Array 1.5 Window Table 1.6 Interval Table _______________________________________________________________________________ The Text _______________________________________________________________________________ _______________________________________________________________________________ What the Text Is The text contains all of the characters, calculations, and formatting information input by the user into the editing environment. The text contains several types of (apparently) randomly organized data: - ASCII character codes - character attribute information - paragraph format information - document format information - calculation data The editor's only function is to alter, maintain, and display this data. This section of the manual will discuss the lower level constructs which allow the editor to function properly and quickly. To manage the text within its allocated area of memory, the editor relies upon many system integers which contain pointers to key locations in the text. Examples of key locations are: where the text starts and ends, which character in the text the cursor is currently over, and where new characters typed in by the user should be inserted. The editor data structures are used to give meaning to the text data. The fields in the data structures give the editor information on how the character data should be displayed. _______________________________________________________________________________ Where the Text Is The text is located in the Cat's RAM space. The current Cat system has 384K of RAM located starting at address $400,000. The following diagram (1.1) shows where in the RAM space the text is located. Diagram 1.1 Location of the text. _______________________________________________________________________________ Pointers Used to Maintain the Text _______________________________________________________________________________ The following diagram (1.2) shows a close-up of the text area and describes some of the pointers used to maintain the RAM text area. _______________________________________________________________________________ The Beginning of the Text Area The text system integer is used to hold the address of the very first byte of text data in the first text partition. The bot ('beginning-of-text') system integer is used to hold the address of the first byte of the user's text data. The memory between the two addresses is used as a "buffer zone" between the start of the allocated text area and the start of the actual text data. The start buffer zone is filled with 8 carriage return characters . _______________________________________________________________________________ The Start of the Gap Region The text data is broken into two pieces which are separated by a region of memory referred to as "the gap". The gap system integer is used to hold the address of the start of the gap. The existence and location of the gap help contribute to the quick response of the editor in many situations. For example, the start of the gap is usually adjacent to the character in the text which is marked with the cursor. This means new characters typed in can be placed directly into the gap region and appended the end of the first partition of text without requiring any movement of the rest of the text data. The bos ('beginning-of-selection') system integer is used to hold the address of the first character in the current selection. If the selection is extended, bos will hold the address of the first highlighted character in the selection. If the selection is not extended, if the wide cursor is present, bos will hold the address of the character seen in the non-blinking half of the cursor. When the cursor is narrow, the bos will hold the address of the character under the narrow cursor. When the selection is not extended the bos will point to the last address in the first partition of text data; the bos pointer and the gap pointer will be right next to each other (see the following diagram, 1.3). Diagram 1.2 Text Pointers Diagram 1.3 Cursor Logistics _______________________________________________________________________________ The Second Partition of Text Data The start address of the second text partition is held in the beot ('beginning-of-end-of-text') and eos ('end-of-selection') system integers. If the cursor is wide, eos will hold the address of the character under the blinking portion of the cursor. If the cursor is narrow, eos will hold the address of the character which immediately follows the narrow cursor. eos and beot will usually always hold the same address (see the following diagram, 1.3). The eot ('end-of-text') system integer is used to hold the address of the last byte of user text data in the second text partition. The endtext system integer is used to hold the address of the last memory location in the RAM text area. The area between the eot and endtext pointers forms a buffer area between the end of the user's text and the absolute end of the RAM text area. The end text buffer holds 30 carriage return characters. _______________________________________________________________________________ The Undo or Cut Buffer The last area of interest in the RAM text area is the undo buffer area. The undo buffer is located in the gap region. The start address of the undo buffer is kept in the bou ('beginning-of-undo-buffer') system integer. The word eou ('end-of-undo-buffer'), which is defined as: : eou ( - a ) beot 4 - ; is used to find the end of the undo buffer. The undo buffer is relocated whenever the eos position changes. The undo buffer is used to hold information (text, formatting info) which is required in order to undo an operation. Whenever information needs to be placed in the undo buffer the bou pointer is repositioned so that the undo buffer is just large enough to hold the desired information. _______________________________________________________________________________ Editor Data Structures _______________________________________________________________________________ The editor uses pointers to manage the placement and movement of text data in memory. Data structures are used to give the editor information about what is in the text, primarily for display purposes. _______________________________________________________________________________ The Control/Format Array The control/format array is the basic data structure unit used in the Cat editor. A single control/format array holds esize ($38, might change to $40) bytes of formatting information which pertains to a particular line in the text. The three main data structures used by the editor (described below) each consist of one or more control/format arrays. Three basic types of formatting information are kept in the control/format array: transient format information, paragraph format information, and document format information. The structure of a control/format array is shown in the following diagram (1.4). The names of the fields, their hexadecimal array offsets (in bytes), and a description of their contents are listed below. _______________________________________________________________________________ Transient Format Information Transient format information is volatile format information which must be "calculated" each time it is requested. The transient information in the control/format array can become obsolete as a result of a single character insertion or deletion. If a character insertion/deletion moves the cursor to a different line or page, the line and line start information, or page information will immediately become invalid. The contents of most of the transient format information fields are described sufficiently below. The use of the %spr field is explained in the section on text display. Name Offset Description %page 00 The global page number in which this line is located. %pgl 02 The local page number (within the current document) in which this line is located. %wr 04 Address of the first character in this line. %ln 08 The global line number for this line. %lnl 0A The local line number (within the current document) for this line. %spr 0C Can hold one of four values: 0, 1, 2, 3. Used by the words which are responsible for displaying lines of text. 0: Display a real line of text. 1: Do nothing. 2: Display 1 blank half-line. 3: Display 1 blank half-line. Diagram 1.4 Control/Format Array _______________________________________________________________________________ Paragraph Format Information The paragraph format information fields hold values which control how the characters in the paragraph (a section of text surrounded by a break - carriage return, page break, document break) should be placed on the screen when they are displayed. For example, the %left field holds the width of the left margin, expressed in units of half spaces. The display routines will use the contents of the %left field when they need to determine where the first character on a line should be placed. Name Offset Description %lsp 0E Local line spacing. Can hold one of three values: 2, 3, 4. Used by the words which are responsible for displaying lines of text. 2: Single-spaced text. 3: 1 1/2 spaced text. 4: Double-spaced text. %oldlsp 0F Previous %lsp value. %left 10 Current left margin width, expressed in half spaces. 0 <= n <= 158 %wide 11 Width of the text area, expressed in half spaces. 2 <= n <= 160 %indent 12 Indent distance for this line, expressed in half spaces. 0 <= n <= 158 %iwide 13 The width of the text area on an indented line. %just 14 Paragraph Style. 0: Normal, left justified. 1: Right justified. 2: Center justified. 3: Fully justified. %tabs 16 Two, 80-bit bit arrays. The screen is 80 full spaces wide. The state of each bit in the first bit array indicates whether the corresponding space on the screen has a tab associated with it. The second bit array indicates whether the corresponding space has a decimal tab associated with it. A note on the units used in the control/format array: Vertical Positioning Units: Half-lines and Line Spacing Half-lines are blank lines which are 1/2 the thickness of a line of text. Depending on the line spacing currently in use, zero, one, or two, half-lines will be inserted between each line of text in the display. The Cat editor supports three types of line spacing: single-spacing, 1 1/2 spacing, and double-spacing. In single-spaced text, each line of text immediately follows the previous line of text, no half-lines are used. In 1 1/2 spaced text, one half-line is inserted between each line of text. In double-spaced text, each line of text is followed by two blank half-lines. The concept of a "half-line" is only used by the routines responsible for the display of lines of text. The line information fields above ( #ln and #lnl ) are used to count actual lines in the text. Half-lines are not part of the text, they are convenient abstractions for the display routines. 22 lines of text can be displayed on the Cat screen at one time. Since each line of text is 2 half-lines wide, the screen can hold 44 half-lines. Horizontal Positioning Units: Spaces and Half-spaces The font used by the Cat editor is a non-proportional font; each character in the character set has the same width, 8 pixels. A half-space is 4 pixels wide. Half-spaces are often inserted into fully justified text. _______________________________________________________________________________ Document Format Information The document format information fields contain information about the document in which the line is located. Several of the fields ( %above , %below , %iprint ) hold information which will determine the printed appearance of the document. The contents of the %lock field indicate whether or not the line is alterable. If the document which contains the line is locked, the line cannot be altered. Name Offset Description %long 2A The value in %long indicates how long a page can be before an implicit page break will occur. The page length is expressed in half lines. %above 2C Holds the height of the top margin on a printed page, expressed in half-lines. %below 2D Holds the height of the bottom margin on a printed page, expressed in half-lines. %lock 2E If this line is locked, will hold the ASCII value for the gray lock character. %ipage 30 First page number in the document. %iprint 32 Number of the first printable page in the document. _______________________________________________________________________________ The Control Array The control array consists of one control/format array. The address of the control array is kept in the #ctrl system integer. The #ctrl is used as a scratch control/format array by routines which need a temporary location for the storage of control/format information. Control/format information which must be saved for future reference is usually moved from the #ctrl array to one of the other data structures described below. A back-up control array called the "previous control array" is used to hold the previous contents of the #ctrl array. The system integer #pctrl holds the address of the start of the previous control array. _______________________________________________________________________________ The Window Table The window table ('window-table') consists of lastline ($4E) control/format arrays. Each control/format array in the window table contains formatting information about one half-line currently displayed on the screen and about half-lines which are just above or just below the top or bottom lines in the display. The following diagram (1.5) illustrates the connection between the window table and the text displayed on the screen. The 50 control format arrays in the window table are shown on the right side of the diagram. The display text to which the control/format arrays correspond are shown on the left side of the diagram. The number to the left of a displayed line of text is the number of the window table entry which corresponds to the line of text. The numbers increment by two because two window table entries are required to represent lines of single-spaced text. (The display of single-, 1 1/2-, and double-spaced text is covered in detail in the "Text Display" section of this manual.) The system integers firstseen ($10) and lastseen ($3B) hold the line numbers of the first and last visible half-lines represented in the window table. The system integer lastline holds the number of the last line represented in the window table. The address of the start of the window table is kept in the #wtable system integer. The window table is ( lastline * esize ) bytes in size. Diagram 1.5 The Window Table _______________________________________________________________________________ The Update Array The update array is a close friend of the window table. The update array contains a one byte flag for each half-line represented in the window array. If a byte contains a non-zero value, the corresponding half-line in the display requires refreshing. If the byte contains a zero value, the corresponding half-line is properly displayed. When display routines are called to redraw the screen contents, they will usually check the update array first so that only the lines which require refreshing are redrawn. _______________________________________________________________________________ The Interval Table To allow quick display of any character in the text, the the text data is divided into many equally sized "text intervals" and formatting information about the first line of text within each interval is kept in a table called the "interval table" (see diagram 1.6). Currently, the size of a single text interval is $400, or 1024 decimal, bytes. Since the entire text size in the editor can be either 256K or 384K bytes, the interval table will have either 256 or 384 (decimal) entries. Each entry in the interval table is a control/format array which holds the formatting information about the first line in the corresponding text interval. Diagram 1.6 The Interval Table _______________________________________________________________________________ How Control/Format Information is Obtained Basically, to get the formatting information which applies to a line in the text, one must step through the text data which contains the characters for the line and look for and obtain the formatting information which is hidden among the characters (document and paragraph format information). This process is called "wrapping" through the text. wrap is the editor word which examines a line of text and produces a set of control/format information for the line. wrap always places its results in the #ctrl arry . wrap's default action is to step through one line of text only and to store the format information found into the #ctrl array. wrap must always start from a "known" location in the text, i.e. at the start of a line whose control/format information is known. wrap expects the information about this known line, its start address (found in the %wr field) and format information, to be held in the #ctrl array. When wrap finishes, the #ctrl array will contain a complete set of format information about the line which follows the line whose format information was passed to wrap in the #ctrl array. So, given information about a known line in the text, wrap will return control/format information about the following line in the text. These default actions of wrap were designed for stepping through the text, line by line, and producing control/format information. These are the basic steps used by wrap : - Use the information about the known line to quickly find the start address of the line which follows the known line. The line which follows is the line in which we are interested. - Proceed forward from this new start position and look for text characters and format information. - Each time a character is encountered, determine the width of the character and add it to a running total of the width of all characters on the current line. - If format information is encountered, transfer it to the #ctrl array. - If the total width ever exceeds the width of the screen, the end of the line has been found. Back up to the last word break. The word before the last word break will be the last word on the line. wrap looks in the system integer wraplim to decide when to stop wrapping through the text. Usually, wraplim contains a 0, which means wrap should only wrap the current text line. If wraplim contains a non-zero value, it is assumed to be the address at which wrapping should stop. Before wrap overwrites the contents of the #ctrl with the newly found format information it saves the current #ctrl information into the #pctrl array. _______________________________________________________________________________ More on Intervals _______________________________________________________________________________ As you can see, keeping the control/format information the editor data structures updated requires heavy constant use of wrap . wrap will obtain information about a desired line much more quickly if it is passed a known line position which is very close to the target line. This is why the interval concept was developed. The interval table holds control/format information about many places in the text (see the previous interval table diagram). Theoretically, one will never have to wrap more than one text interval of text in order to find information about any line of text in the text. _______________________________________________________________________________ How the Interval Table is Used When the editor first comes up, the entire text is word-wrapped to obtain control/format information about the first line of text in each text interval so that all of the entries in the interval table may be filled. During the use of the editor, the contents of each text interval will change and the format information in the corresponding control/format entry in the interval table will become invalid. Operations which do invalidate information in the interval table use the word killivls to mark the invalid intervals. Since updating the entire interval table after each editing operation would be prohibitively slow, the word fixivl is used once each time through the main editing loop. fixivl tries to fix one bad interval each time it is called. Only certain operations, such as XXXX?, require that the entire interval table be updated to function properly. The interval table is also used to expedite the process of finding formatting information for selected locations within the text. The word findchar fills the #ctrl array with the format information for a specific character in the text. The word findline fills the #ctrl array with format information for a specific line in the text. nextpage , prevpage , nextdoc , and prevdoc cause the #ctrl array to be filled with the format information for the page or document which is before or after a specified address in the text. All of these words use the interval table to get quick format information about a spot close to their desired destination. Then, wrap is used to wrap from a known location to the desired location in the text. _______________________________________________________________________________ The Top Four Intervals Four interval table entries have special significance to the editor: #1 The first text interval. #2 The interval which contains the gap. #3 The interval which contains the beot . #4 The last text interval. The first text interval never changes because the first line in the text cannot change, it always contains the start document character. The state at the start of the text is always known. The address of the interval table entry which corresponds to the text interval which contains the start gap address is kept in the gapivl system integer. The address of the interval table entry which corresponds to the text interval which contains the beot address is kept in the beotivl system integer. These two intervals are "problem-child" intervals since they are interrupted by the gap and may or may not contain complete lines of text. The addresses of these interval table entries are saved for fixivl . If fixivl has problems fixing an interval table entry, and it notices the interval it is trying to fix is either gapivl or beotivl , it will skip over the interval and move on. _______________________________________________________________________________ Routines Which Affect the Text and its Pointers: _______________________________________________________________________________ adjust ( a1 a2 n - ) Adjusts all text pointers which point within the range between address 'a1' and 'a2' by the delta distance 'n'. The pointers affected are: gap , bou , beot , bos , eos , savebos , extbos , bot , bor , eot , eor , oldop , oldpop , oldbos , oldeos , oldeos2 , oldbos2 , and oldpop2 . Also uses realign to adjust the positions of the op and pop pointers and the contents of the window table. clearundo ( - ) Empties the undo buffer by setting bou pointer equal to the eou pointer. eou ( - a ) ('end-of-undo-buffer') Calculates and returns the address of the end of the undo buffer. realign ( a1 a2 n - ) Adjusts pointers and data structures which point into the range of text starting at address 'a1' and ending at address 'a2' by the offset 'n'. The pointers which are affected by realign are op and pop . realign also affects all information in the window table entries. selsize ( - n ) ('selection-size') Calculates and returns the size in bytes 'n' of the current selection. The equation: gap bos - is used to determine the selection size. _______________________________________________________________________________ Window Table Routine: seenlines ( - n ) Returns the number 'n' of half-lines which are visible on the display. _______________________________________________________________________________ Update Table Routines: update! ( n - ) ('update-store') Sets the update bit in the update array entry which corresponds to the specified screen line number 'n'. update? ( n - f ) ('update-question-mark') Returns a true flag if the update table entry corresponding to the specified line in the window table indicates that the line requires updating. _______________________________________________________________________________ Intervals Words: badivl ( - 0 | If no bad interval entry is found. ) ( - a -1 | If a bad interval entry is found. ) ('bad-interval') Searches through the interval table looking for the first bad (read, not updated) interval. If a bad interval is found, returns the text address which corresponds to the interval and a true flag. If no bad interval is found, returns a false flag only. fixivl ( - ) ('fix-interval') Tries to fix one bad interval in the interval table. goodivl ( a1 - a2 ) ('good-interval') Returns the address 'a2' of the closest up-to-date interval table entry which corresponds to the specified text address 'a1'. hideivls ( a1 a2 - ) ('hide-intervals') Marks all "dead" intervals which correspond to text located within the specified address range (between 'a1' and 'a2') as potentially valid by clearing the high bit on the %wr field. A potentially valid interval is an interval whose control/format array has only incorrect page and line number information. killivls ( a1 a2 - ) ('kill-intervals') Marks all intervals which correspond to text located within the specified address range (between a1 and a2) as invalid by setting the high bit on the %wr field in the corresponding control/format array. knownplace ( a - ) Looks through the interval table to find the interval boundary which is closest to and prior to the desired text address 'a'. Loads the control/format information which corresponds to the interval into the #ctrl array. lastknownline ( - n ) Returns the line number of the last displayable line of text in the text. line>ivl ( n1 - ) ('line-to-interval') Looks through the interval table to find the interval boundary which is closest to and prior to the desired line number 'n1'. Loads the control/format information which corresponds to the interval into the #ctrl array. nearinterval ( a1 - a2 ) Returns the address 'a2' of the interval boundary in the text which is closest to and prior to the specified text address 'a1'. nearivl ( a1 - a2 ) ('near-interval') Returns the address 'a2' of the closest interval table entry which corresponds to the specified text address 'a1'. nextivl ( - a -1 | If a valid interval is found. ) ( - 0 | If a valid interval is not found. ) ('next-interval') Looks through the interval table to find the address 'a' of the next valid interval table entry which corresponds to an interval boundary address which is greater than the address found in the %wr field of the #ctrl array. If a valid interval is found, its interval table entry address and a true flag are returned. If no valid interval is found, a false flag is returned. partknown ( a - ) Uses hideivls to mark all text intervals between the address 'a' and the end of text, eot , as partially changed. previvl ( - a -1 | If a valid interval is found. ) ( - 0 | If a valid interval is not found. ) ('previous-interval') Looks through the interval table to find the address 'a' of a previous valid interval table entry which corresponds to an interval boundary address which is less than the address found in the %wr field of the #ctrl array. If a valid interval is found, its interval table entry address and a true flag are returned. If no valid interval is found, a false flag is returned. putivl ( - f ) Puts the control/format information found in the #ctrl array into corresponding interval table entry. The address in the %wr field is used to find the corresponding interval table entry. A true flag is returned if all interval table entries contain valid control/format information. _______________________________________________________________________________ Wrapping the Text: prevwrap ( - ) Copies the contents of the previous control array, #pctrl into the current control array, #ctrl . wrap ( - ) Used to recalculate line, page, and document numbers. Performs one wrap of one half line each time it is called unless a non-zero value is stored in wraplimit . Wraps the "current" line, i.e. the line whose format information is stored in the current #ctrl array. If a non-zero value is stored in wraplimit it is assumed to be the address at which wrapping should stop. Since wrap may have to cross the infamous gap , the skip characters (skip characters are described in the "What's in the Text" section) should be properly positioned on either side of the gap (i.e. preset should be called before using wrap). wrap does not affect the appearance of the display, it only affects the contents of the current line's control/format array (#ctrl). wrapthru ( a - ) Updates the interval table entries starting with the entry which is nearest to the specified text address 'a' and ending with the entry which corresponds to the interval which immediately follows the interval which "contains" the text address 'a' (is this really where it stops ?). _______________________________________________________________________________ Getting Format Information About Specific Places in the Text findchar ( a - ) Fills in the #ctrl array with the control/format information for the beginning of the line on which the character residing at the specified address 'a' is located. findline ( n - ) Fills in the #ctrl array with the control/format information for the specified line 'n'. nextpage ( a - ) Loads the #ctrl array with the control/format information which corresponds to the first pagebreak found in the text after the specified address 'a'. prevpage ( a - ) Loads the #ctrl array with the control/format information which corresponds to the first pagebreak found in the text before the specified address 'a'. nextdoc ( a - ) Loads the #ctrl array with the control/format information which corresponds to the first document break found in the text after the specified address 'a'. prevdoc ( a - ) Loads the #ctrl array with the control/format information which corresponds to the first document break found in the text before the specified address 'a'. _______________________________________________________________________________ Summary _______________________________________________________________________________ _______________________________________________________________________________ Text Maintenance Integers: text Address of absolute start of text area. bot Beginning of text. bos Beginning of selection. gap Address beyond first partition of text. bou Beginning of undo buffer. eou End of undo buffer ? beot Beginning of second section of text. eos Address beyond end of selection. eot Address beyond end of text. endtext Address of byte just past absolute end of text. _______________________________________________________________________________ Integers Used to Access the Contents of the #ctrl Array _______________________________________________________________________________ _______________________________________________________________________________ Transient Format Information Integers: %pg #ctrl + integer #pg The global page number in which this line is located. %pgl #ctrl + integer #pgl The local page number (within the current document) in which this line is located. %wr #ctrl + integer #wr Addr. of the first character in this line. %ln #ctrl + integer #ln The global line number for this line. %lnl #ctrl + integer #lnl The local line number (within the current document) for this line. %spr #ctrl + integer #spr Can hold one of four values: 0, 1, 2, 3. Used by the words which are responsible for displaying lines of text. z 0: Display 1 half-line. 1: Do nothing. 2: Display 1 half-line. 3: Display 1 half-line. _______________________________________________________________________________ Paragraph Format Information Integers: %lsp #ctrl + integer #lsp Local line spacing. Can hold one of three values: 2, 3, 4. Used by the words which are responsible for displaying lines of text. 2: Single-spaced text. 3: 1 1/2 spaced text. 4: Double-spaced text. %oldlsp #ctrl + integer #oldlsp Previous #lsp value. %left #ctrl + integer #left Current left margin width, expressed in half spaces. 0 <= n <= 158 %wide #ctrl + integer #wide Width of the text area, expressed in half spaces. 2 <= n <= 160 %indent #ctrl + integer #indent Indent distance for this line, expressed in half spaces. 0 <= n <= 158 %iwide #ctrl + integer #iwide Remaining width of the text on an indented line. %just #ctrl + integer #just Justification. 0: Normal, left justified. 1: Right justified. 2: Center justified. 3: Fully justified. %tabs #ctrl + integer #tabs Two, 80-bit bit arrays. The screen is 80 full spaces wide. The state of each bit in the first bit array indicates whether the corresponding space on the screen has a tab associated with it. The second bit array indicates whether the corresponding space has a decimal tab associated with it. _______________________________________________________________________________ Document Format Information Integers: %long #ctrl + integer #long The value in #long indicates how long a page can be before an implicit page break will occur. The page length is expressed in half lines. %above #ctrl + integer #above Holds the height of the top margin on a printed page, expressed in half-lines. %below #ctrl + integer #below Holds the height of the bottom margin on a printed page, expressed in half-lines. %lock #ctrl + integer #lock If this line is locked, will hold the ASCII value for the gray lock character. %ipage #ctrl + integer #ipage First page number in the document. %iprint #ctrl + integer #iprint Number of the first printable page in the document. _______________________________________________________________________________ Control/Format Array Offsets _______________________________________________________________________________ _______________________________________________________________________________ Line Offsets: 00 integer %page 02 integer %pgl 04 integer %wr 08 integer %ln 0A integer %lnl 0C integer %spr _______________________________________________________________________________ Format Offsets: 0E integer %lsp 0F integer %oldlsp 10 integer %left 11 integer %wide 12 integer %indent 13 integer %iwide 14 integer %just 16 integer %tabs _______________________________________________________________________________ Document Offsets: 2A integer %long 2C integer %above 2D integer %below 2E integer %lock 30 integer %ipage 32 integer %iprint _______________________________________________________________________________ Data Structures Integers #ctrl Holds the address of the start of the #ctrl array. #pctrl Holds the address of an array which holds the previous contents of the #ctrl array. #wtable Holds the address of the start of the window table. #update Holds the address of the start of the update array. #itbl Holds the address of the start of the interval array. _______________________________________________________________________________ Window Table Integers: Name Hex Value Description lastline 50 Lastline in window table. firstseen 10 First window table line visible on screen. lastseen 36 Last window table line visible on screen. middle - Offset to the middle line in the display. eosline - Line in window table containing the eos. topline - Global line number of the first line in the window table. gapline - eos line relative to window. _______________________________________________________________________________ Interval Table Integers: Name Hex Value Description esize 38 Size of a control/format array. isize 400 Size of the text interval which is represented by an interval table entry. itblsize esize isize * Size of the interval table. beotivl - Holds the number of the interval table entry which corresponds to the text interval which contains the beot address. endtextivl - Holds the number of the interval table entry which corresponds to the text interval which contains the endtext address. gapivl - Holds the number of the interval table entry which corresponding to the text interval which contains the gap. _______________________________________________________________________________ Wrapping Integers: markpoint Place beyond which to seek pb/ds in wrap. wraplim Stopping point for wrap. %pwrap Previous array wrap address. #nextwr Holds the start address of the line which immediately follows the line whose control/format information is currently stored in the #ctrl array. _______________________________________________________________________________ Unclaimed Integers: lines total text line count pages total text page count pagebase pgs number of pages disktext start of text area on disk _______________________________________________________________________________ 2. Text Display _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 5/11/87 Fixes LC 6/30/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: Text display is a low-level and high-level process. The low-level text display routines must convert encoded text data to displayable character strings, must work within the limitations of the screen and font sizes, and must actually draw the character data on the screen. The high-level text display routines must decide which regions of text are to be displayed and must be able to locate the formatting information for that region of text so that the lower-level display routines may be called upon to display the text. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 A Low-Level Look at Text Display 3 Preparing the Text for Display 5 Special Text Preparation Cases 6 Editor Character Sets 6 Text Character Set 7 Display Character Set 9 Screen and Font Dimensions 10 Drawing Text 11 A High-Level Look at Text Display 11 Obtaining Display Information 12 Drawing the Display 12 Line Spacing 15 Drawing the Entire Display 16 Drawing Selected Portions of the Display 17 Scrolling the Display 18 Text Display Routines 18 Low-Level Text Display Routines 19 Mid-Level Text Display Routines 20 Utility Words Used by High-Level Text Display Routines 21 High-Level Text Display Routines 19 Summary: Integers Used For Text Display _______________________________________________________________________________ List of Illustrations 2.1 Cat Text Character Chart 2.2 Cat Display Character Chart 2.3 Diagram which illustrates how the window array relates to the text on the screen. _______________________________________________________________________________ A Low-Level Look at Text Display _______________________________________________________________________________ The two lowest-level display words in the editor are build and disp . Both of these words are designed to display one line of text at a time. build prepares a line of text for display and disp draws the text on the screen. _______________________________________________________________________________ Preparing the Text for Display build is used to convert one line of encoded text data into a displayable format. build always prepares the current line, the line of text whose formatting information is currently stored in the #ctrl array, for display. build analyzes the text data which starts at the address found in the %wr field of the #ctrl array and stops at the address found in the #nextwr system integer. The #nextwr integer holds the address of the start of line which follows the current line. build is the probably the most complicated of the display words because it must understand how the format information found in the #ctrl array will affect the appearance of the text on the screen and must be able to describe the desired text appearance to a lower-level display routine, ~disp , which knows nothing about format codes. build's output is a character string (4 bytes per character) which build stores in the line input buffer. The address of the line input buffer is kept in the lbuff ('l-buff') system integer. Each character in the string is described with four bytes of information: ______________________________________________________________ | byte 1 | byte 2 | byte 3 | byte 4 | -------------------------------------------------------------- Byte 1: Extended ascii value (8th bit is used). Value can be in the range 0 -> CF. Byte 2: Modifiers byte. Four bits in this byte are used to specify special character styles: bold, underlined, dotted-underlined, inverse-video. Another bit is used to indicate that this character is the last character to be drawn. Byte 3: Currently unused. Byte 4: Overstrike character. This character, if any, will be OR'ed over the main character during display. Byte 1: The Cat editor has only one text font, but it can display the font in two styles: normal and bold. The data which describes how each character looks on the screen is kept in a font table. Byte 1 of an lbuff character description contains with the desired character's offset into a font table. Byte 2: The bold bit in byte 2 indicates which font table, normal or bold, should be used. The settings of the other 3 style bits determine whether additional styling data should be added to the main character data before it is drawn. The low-level drawing routine ~disp will continue drawing characters on the screen until a modifiers byte with the stop bit in the modifiers byte set is encountered. Byte 4: Byte 4 provides a little more information on how the character should be drawn on the screen. If the character is an overstrike character its data will be merged into the screen display (with the use of an OR operation) rather than laid over the current screen contents. _______________________________________________________________________________ Special Text Preparation Cases As build creates its output string in the line buffer, it must look for and handle the following special cases: 1. Page breaks or document separators in the text. In memory, the page break and document break characters are single byte characters. The screen representations of page breaks and document separators fill the entire usable width of the screen. Therefore, when build encounters a page break or document separator character in the text it must construct the corresponding screen representation in character form in the lbuff . The screen representation of a page break or document separator is composed of many small horizontal line characters, the page or document number character, and the special underline character which lies underneath the page or document number character. 2. Margins, indents, tabs, and text justification. build is responsible for (1) discerning the margin widths, indent widths, tab placements, and text justification styles which affect the line of text and (2) inserting spaces and half-spaces as necessary to make the text meet the desired format specifications. 3. Highlighted text. build must check to see if the text being decoded lies within the current selection range. If it does, build must specify that the character be displayed using inverse video. build must also handle the characters whose screen appearance is selection-dependent. For example, there are two choices for the display version of the carriage return character. If the carriage return is selected, it is shown as a white arrow on a black background. If the carriage return is not selected it is represented by a white space. 4. Locked regions of text. If the line to be displayed is part of a locked document, build is responsible for inserting the lock character (a vertical gray bar) into the front of the display string. 5. Special character styles. If any characters in the line have associated character styles, build must translate the editor style information to the disp-format style information. _______________________________________________________________________________ Editor Character Sets _______________________________________________________________________________ The following two diagrams, entitled "The Cat Text Character Set" and "The Cat Display Character Set", show all characters which build can place in the lbuff and also shows the decimal and hexadecimal character codes which have been assigned to the characters. _______________________________________________________________________________ Text Character Set Only the character codes for characters shown in the Cat text character set may appear in the text area. Of the characters shown in the Cat text character set, only those whose character codes range from $00 to $EF are actual typeable, displayable characters. The characters with codes in the $09 through $0D range are not usually visible but they can be entered from the keyboard. NOTE: The Universe character, which was included to allow the implementation of "universes" (a set of documents), which would be one level above the document, is currently not supported. The character codes in the $E0 through $EF range are codes which are allowed in the text but cannot be generated directly from the keyboard. The skip, paragraph format, calc, and locked calc characters are special characters which mark the start of a packet of non-text data within the text area. The characters corresponding the character codes in the $E9 through $EF range are modifier characters which indicate that the character which precedes them in the text has a special display attribute, i.e. the character has a solid underline, is bold, has a dotted underline, or has some combination of attributes. The backspace attribute character (character code = $E8) may be used in the future to allow any character to be used as an accent for any other character. If found in the text, the backspace attribute character would indicate that a backspace should be emitted before the character which precedes the backspace attribute is drawn on the screen, i.e. the character which precedes the backspace attribute should be laid over the previous character in the display. The extend attribute is currently not used or understood. Diagram 2.1 The Cat Character Set Diagram 2.2 The Cat Display Character Set _______________________________________________________________________________ Display Character Set The display character set includes those characters which may appear in the text display area but whose character codes, in most cases, do not appear in "the text" data area. The character codes corresponding to the display characters lie in the $02 through $1F range. The characters in the display character set are shown in the following diagram. Only the following four character codes are allowed to appear in the text as character data and in the line output buffer as character display data: tab ($09), document separator ($0B), page break (0C), and carriage return ($0D). The significance of these four character codes depends upon the environment in which they are found. When these characters are encountered in the text, they affect the appearance and organization of the text (a tab indicates a break in a line of text, a carriage return signifies the start of a new line, etc.). When these characters are encountered in the line output display buffer, they cause the visual representations of the characters to be drawn in the display (a return character code in the line output buffer causes the arrow graphic used to visually represent a carriage return to appear on the screen). The character codes for all other display characters will never be found in the text data. The display characters can be divided into four categories: display characters used to construct implicit/explicit page breaks lines and document separator lines, display characters used to represent in-line formatting characters (tab, carriage return, blanks), the display character used to represent locked documents, and the display characters used for screen diagnostic testing. Page breaks and document separators are composed of up to four types of display characters: 1) Horizontal line segments which compose the main body of the page break or document character ($0E, $0B, $0C). 2) Special smaller and slightly elevated numbers used for page numbering ($10-$19). 3) Thin horizontal line segment which goes underneath the page number ($02, $03, $04). 4) Horizontal line segment used to represent the selected version of an explicit page break. In-line formatting uses the following display characters: 1) Selected tabs consist of two parts, the horizontal tab tail ($08), and the tab arrow head ($09). 2) Selected carriage returns use an arrow display character which points downward and to the left ($0D). 3) Margins, and de-selected carriage returns use a mark blank character ($0A). 4) De-selected tabs use the blank tabspace character ($1C) Locked documents are displayed with a series of document lock characters ($07) displayed along the edge of the locked document. The diagnostic routines use the special diagnostic display characters ($1E and $1F) for the screen test. _______________________________________________________________________________ Screen and Font Dimensions The Cat screen is 672 (decimal) pixels wide by 344 pixels high. Each character in the Cat's non-proportional font is 8 pixels wide. Therefore, 672/8 = 84 ($54) 8-bit wide characters can fit side-by-side on the Cat screen. Since a two character (=16 pixels) margin is always used on both sides of the text, a Cat display line supports 84 - (2 for left margin) - (2 for right margin) = 80 columns of characters. Each character in the Cat character set fits within a 14 pixel by 8 pixel rectangle. The Cat text display area, which holds 22 lines of single-spaced text and has a 2 pixel high margin both above and below, is 22 x 14 = 308 + 2 + 2 = 312 pixels high. The ruler/status display area is 344-312=32 pixels high. 2 pixels high ------------ top margin ------------- | | | | | | | | | | 22 lines, | | 14 pixels per line, | | = 308 pixels high | text display area | | | | | | | | | | | | | 2 pixels high ----------- bottom margin ----------- | | 32 pixels high | ruler/status area | | | ------------------------------------- Total screen height = 2 + 308 + 2 + 32 = 344 pixels _______________________________________________________________________________ Drawing Text ~disp , disp , and halfdisp are the three words which are ultimately responsible for redrawing the text portion of the display. The next section will discuss the routines which draw the ruler and status portions of the display. ~disp , a highly optimized 7 page assembly routine, is the word which actually draws text on the screen. disp sets up the registers used to pass inputs to ~disp . halfdisp is a short, optimized assembly routine used to draw blank half lines. These routines have no knowledge of the encoded format used to store the text data. Therefore, the structure of the encoded text can change without affecting the display of the text. disp passes the following four pieces of information to ~disp: (1) the address of the normal font data table, (2) the address of the bold font data table, (3) the address of the line input buffer, and (4) the address of the location in the display memory where the text should be drawn. The addresses for the first three parameters are fixed. disp uses the screen half line number passed to it on the stack to calculate the location in screen memory where ~disp should start drawing. ~disp traverses twice through the lbuff . During the first pass, ~disp performs the following actions: 1) Checks the bold bit in the modifiers byte of the lbuff character information. If the bit is set it will use the bold font table, otherwise it will use the normal font table for step 2. 2) Loads the character data for the top half of the character into the data registers. The desired character data is found by using the character code in byte 1 of the lbuff character information to form an offset into the font table. 3) Checks the lbuff character information to see if an overstrike character is required. If it is, the data for the top half of the overstrike character is obtained, and AND'ed with the data in the data registers. 4) Checks the inverse video bit. If it is set, the data in the data registers is complemented with the use of the NOT operation. 5) Lays the data into screen memory. During its second pass through the lbuff disp processes and draws the lower halves of the characters. ~disp's actions during the second pass are very similar to those described above except for one exception. During the second pass, after any overstrike character has been handled, ~disp also checks the underlined and dotted underlined bits in the modifiers byte and modifies the character data accordingly before drawing it on the screen. _______________________________________________________________________________ A High-Level Look at Text Display _______________________________________________________________________________ From a high-level point of view, there are two steps required for text display: (1) the format information for the text to be displayed must be loaded into the window table, and, (2) the lines represented by the entries in the window table must be displayed. _______________________________________________________________________________ Obtaining Display Information To be expedient, all of the display routines expect the control/format information for each line of text to be displayed to be stored in the window table. Therefore, before the actual display routines can be called, format information must be found and placed in the window table. loadline and storeline are words which can be used to access and change selected window table entries. loadline places the format information found in a specied window table entry in the #ctrl array. storeline stores the format information found in the #ctrl array into a specified window table entry. rewindow is used to completely fill in the contents of the window table. rewindow is used when the display needs to be completely recalculated and redrawn. rewindow assumes that the system integer topline holds the global line number of the first line of text to be represented in the window table. rewindow finds the text address of the first character in the topline line and wraps from that address, using storeline to transfer format information from the #ctrl array to the window table, until each entry in the window table is filled up. rewindow also sets the update bit for each entry in the window table as it goes along: : rewindow ( - ) ( Install skip markers on both sides of the gap. ) preset ( Load the #ctrl array with format information about the ) ( line which should appear at the top of the screen. ) topline findline ( Wrap through the text enough times to fill in each entry ) ( in the window table. Also update the interval table. ) lastline 1+ 0 do ( Store the information which is currently in the #ctrl ) ( array into entry 'i' in the window table. ) i storeline putivl drop wrap ( Set the update bit for window table entry 'i'. ) i update! loop ; _______________________________________________________________________________ Drawing the Display refresh is the link between the low-level drawing routines build , disp , and halfdisp and the higher-level display routines. When called, refresh will check the update bits for all window table entries and will redraw, with the use of build , disp , and halfdisp , only those screen half lines whose update bits are set. If a higher-level routine wants to have only selected lines on the screen redrawn, it will set the update bits which correspond to those selected lines before calling refresh . If a higher-level routine wants to complete redraw the screen it will use rewindow before calling refresh . The definition of refresh , which is fairly straightforward, is included on the following page. _______________________________________________________________________________ Line Spacing rewindow uses wrap to get control/format information about text to be displayed and storeline to store that control/format information into the proper entry in the window table. wrap was designed for this purpose since it pays attention to line spacing as it goes through the text. wrap will generate control/format information for both real lines of text and display half-lines (if the text is 1 1/2 or double-spaced). Each time wrap is used, it decrements the contents of the %spr field of the #ctrl array by one. If the contents of the %spr are reduced to a negative number as a result of the operation (the %spr value will go from 0 to -1), wrap will reset the line spacing by replacing the contents of the %spr field with the value found in the %lsp field. If the result of the subtraction is a positive number, wrap will not finish wrapping the line. Instead, wrap will exit immediately after only having affected the contents of the %spr field. refresh differentiates between the window table entries which represent real text lines and those entries which represent display half lines by checking the contents of the %spr field in the window table entry. If a 0 is in %spr , refresh will display a line. If a 1 is in %spr , refresh will do nothing. If a 2 or 3 is in %spr , refresh will display a blank half-line. When text is single-spaced, the window table is filled with lastline/2 sets of control/format information. Each line of single-spaced text is represented by a pair of window table entries which are identical except for the contents of the %spr field. The first entry in the pair will have a 1 in its %spr field. refresh will do nothing when it encounters this entry. The second entry in the pair will have a 0 in its %spr field. refresh will draw a line of text when it encounters this second entry. _______________________________________________________________________________ : refresh ( - ) ( Index through all half lines in the window table. ) lastseen 1+ firstseen do ( Does this entry need to be redrawn ? Exit if not. ) i update? if ( If it does need redrawing, place its format ) ( information in the #ctrl array. ) i loadline ( Check the line spacing state, should this line be ) ( drawn ? 0 = real line of text , 1 = do nothing, ) ( 3 & 4 = draw blank half line. ) #spr c@ if ( If this is the first visible line, it must ) ( contain a blank half line. ) firstseen i = if 0 halfdisp then ( If the #spr contains a number which is ) ( greater than one then this text is 1 1/2 ) ( or double spaced. Insert blank half lines. ) #spr c@ 1 > if i 1+ firstseen - halfdisp then else ( The last visible half line must always be a ) ( blank half line. ) lastseen i = if ( Draw the blank halfline at the bottom ) ( of the screen. ) i firstseen - halfdisp else ( Construct the display output string ) ( for this line and display the text. ) build i firstseen - disp then then then loop ; _______________________________________________________________________________ Diagram 2.3 Line Spacing When text is 1 1/2 spaced, the window is filled with lastline/3 sets of control/format information. In 1 1/2 spaced text each line of text is represented by three almost identical window table entries. The first of the three entries will have a '2' in its %spr field, the second will have a '1', and the third will have a '0'. In double-spaced text, the window table is filled with lastline/4 sets of control/format information. Each line of text is represented with 4 window table entries. The first of the four entries has a '3' in its %spr field, the second has a '2', the third a '1' and the fourth a '0'. _______________________________________________________________________________ Drawing the Entire Display The goal of most of the high-level display routines is to get the current selection, as delimited by the bos and eos pointers, on the screen. new-display , eos-display , redisplay , and display are all display routines which share this goal. new-display and eos-display cause the entire screen to be redrawn. redisplay and display only resort to a complete redraw if they absolutely must. If possible, redisplay and display will only redraw those screen half lines which require updating. fit-display is the only high-level display routine which does not care about the current selection. fit-display's job is to display a specified region of text on the screen starting at a specified screen half line number. Here is the stack notation for fit-display : : fit-display ( halfline# start-text-rgn end-text-rgn - ) The majority of fit-display's work involves determining which line number should be displayed at the top of the screen. Once this number is determined, it is placed in the topline system integer and rewindow and refresh are used to redraw the display. fit-display uses findchar to get format information about the two locations in the text and extracts the line numbers for the two characters from the format information. Once the start and end text range line numbers are known, fit-display can determine how many screen lines the text range encompasses and can position the text range accordingly. eos-display uses fit-display to display the current selection with the selection start line located at a specified screen half line position: : eos-display ( halfline# - ) bos eos prevchar fit-display ; new-display calls eos-display and requests that the current selection be displayed with the selection start located at the middle visible line on the screen: : new-display ( - ) middle eos-display ; _______________________________________________________________________________ Drawing Selected Portions of the Display display and redisplay try to redraw as few lines as possible. display checks to see if both the bos and eos are visible in the screen and, if they are, marks only the bos line for updating and uses refresh to redraw the line. If the bos and eos are not visible in the window, display calls upon new-display to completely redraw the screen: : display ( - ) preset ( Is the character before the eos visible ? ) eos prevchar visible? if ( Is the bos line represented in the window table ? ) bos inwindow if ( Set the update bit for the bos line. ) update! then ( Redraw the bos line. ) else ( Redisplay the entire screen. ) new-display then ; redisplay is designed to be used after a text change has been made at the gap (which is usually where the cursor or selection is found). The system integer gapline holds the number of the screen half line which contains the gap (actually, the character which immediately precedes the gap). redisplay tries to redraw only those lines which could be affected by editing activity at the gap. The lines which are most likely to be affected by changes at the gap are the gapline and the line which precedes the gap line. Example: Take the case of a character being inserted at the end of a word which lies at the end of a wrapped line of text. The insertion could cause the word to be too large to still fit on its current line and it would have to be pushed down to the following line (the following line would then become the gapline). This means that at least two lines would need to be redrawn: the new gapline (because a new word was inserted at its start), and the previous line (because the last word was removed). If redisplay determines that the editing activity left the gapline in the window table, it will try to selectively fix the display. Otherwise, redisplay will call new-display to completely redraw the window. To selectively fix the display redisplay compares the window table format information for the gapline , the line which precedes the gapline , and for the lines which follow the gapline , to the current format information returned by wrap in the #ctrl array. If the format information has changed, redisplay places the correct format information in the window table and sets the update bit for the line and uses refresh to redraw all changed lines. _______________________________________________________________________________ Scrolling the Display Four words are used for scrolling of the display: scrollup , scrolldown , scrollback , and scrollfwd . scrolldown and scrollback work together to scroll the contents of the display down by one text line height. scrollup and scrollfwd work together to scroll the contents of the display up by one text line height. Scrolling involves three steps: 1. If the text is being scrolled, rather than un-scrolled, the information required for undo-ing the scroll must be saved away. 2. Check the selection position. If the selection is not on the top or bottom line of the display, it should be scrolled along with the text. If the selection is on the top line when the text is being scrolled up, or is on the bottom line when the text is being scrolled down, it should not scroll with the text but should stay pinned on either the top or bottom line. 3. The contents of the screen, window table, and update table must be shifted downwards or upwards in memory by one line or entry. 4. The lines which have been changed as a result of the scroll must be redrawn. 5. The parameters which will allow the system to undo this scroll operation must be set up. scrollfwd and scrollback perform steps 1 and 3 above. If a scroll-undo operation is not occurring they will save the selection/editor state operation into the special set of backup selection/editor state integers used only by the creeping and scrolling routines. Next the selection position is checked. If the scroll operation would cause the selection to be scrolled out of the display, the selection will be collapsed and repositioned. If the screen contents are being scrolled up and the selection is on the top line of the display, the collapsed selection point will be moved to the first character on the following line (by altering the bos , eos , and gap pointers). If the screen contents are being scrolled down and the selection is on the bottom line of the display, the collapsed selection point will be moved to the first character on the preceding line (also by altering bos , eos , and gap pointers). If the selection is not in danger of being scrolled off the screen it will simply be scrolled along with the line on which it resides. Scrolling only affects the top or bottom line on the screen, the screen image and data structures associated with the lines between the top and bottom lines are shifted, but not changed. scrolldown and scrollup , which are used to shift the screen, window table, and update table data in memory, take advantage of this characteristic of scrolling and use block moves to shift both the screen image and the corresponding window and update table entry data up or down in memory. After scrolldown or scrollup have finished, scrollfwd and scrollback must only get new information for, and display, the top or bottom line and the line which contains the cursor (the gapline ). After scrolldown or scrollup has finished, the scroll operation is almost complete. Now, scrollback and scrollfwd will selectively redraw those screen lines which have been altered. If the screen contents have been scrolled down, new formatting information has been placed in the firstseen entry in the window table. scrollfwd will set the update bit which corresponds to the firstseen line so that it will be redrawn when the screen is refreshed. If the screen contents have been scrolled up, new formatting information has been placed in the lastseen entry in the window table. scrolldown will set the update bit for the lastseen line. scrollfwd and scrollback will also both set the new gapline line number and the update bit which corresponds to the gapline . _______________________________________________________________________________ Text Display Routines _______________________________________________________________________________ _______________________________________________________________________________ Low-level Text Display Routines build ( - ) Scans through and converts the current line of text (text with embedded formatting, and other non-printable information) to a string of printable characters in a format suitable for use by ~disp (ascii value byte followed by 3 bytes of additional info). The string of printable characters is stored in the line output buffer lbuff . Any characters which lie within the current selection are marked as highlighted characters. Inserts the necessary spaces required for current justification. The bos (beginning of selection) and eos (end of selection) pointers should be properly set up prior to the use of build to ensure that any highlighted characters are properly encoded. build uses the address stored in the #wr field of the #ctrl field as the start address for its conversion process and uses the address found in the #nextwr integer (which should be the start address of the line which immediately follows the current line) as the end address. The word loadline , which is used to transfer the contents of the control/format array for a specified line on the screen into the #ctrl array (i.e. to make a line on the screen a "current" line), will set up the #nextwr integer contents. disp ( n - ) Draws the line found in the line buffer, lbuff , on the screen at the half line 'n'. halfdisp ( n - ) Draws a blank half line on the screen at position 'n' on the screen. ~disp ( code routine, parameter passed in registers ) ('tilda-disp') The word which actually puts data on the screen. Draws a single line of characters on the screen each time it is called. The characters to be drawn are located a buffer whose address is passed to ~disp . Each character to be drawn is represented by 4 bytes of information. ~disp will continue taking characters from the buffer and drawing them until it reaches a character which has the "end of buffer" bit set in its modifiers byte. Does not check to see if it is printing off the edge of the screen, this check is the responsibility of the caller. _______________________________________________________________________________ Mid-level Text Display Routines loadline ( n - ) Loads the control/format information about the specified line 'n' in the window table into the current control/format array (#ctrl). Also sets up the #nextwr integer so that it points to the start of the line which follows the "current" line (the line whose control/format information is stored in the #ctrl array). build requires that the #nextwr is properly set up. refresh ( - ) Steps through the update array and redraws any visible half lines which have their update bits set. If the half line is a half line with no text, halfdisp will be used. If the half line contains text, build is used to convert the formatted text to character text and disp is used to place the characters on the screen in the correct line position. When refresh has finished redrawing selected portions of the screen it will clear all positions in the update array. refresh can only be used to redraw lines whose formatting information is already stored in the window table. rewindow ( - ) Recompute the window array and mark all of lines for updating. Completely rebuilds the information in the window table. To reconstruct the window array rewindow makes the absolute line number found in the topline system integer the first line in the window array. Typically called after a situation where the entire display has been modified, i.e. after an explain message has completely overwritten the display. storeline ( n - ) Stores the current control/format information (found in the #ctrl array) into the window table field corresponding to the specified screen line number 'n'. _______________________________________________________________________________ Utility Words Used by the High-Level Text Display Words: collapse ( - ) Uses selected to set the update bits corresponding to all lines in the display which contain the current selection, then sets the bos to gap prevchar (which reduces the selection to one character) and uses refresh to redraw all lines which require redisplay. differs? ( n - f ) Returns a true flag if the control/format information which corresponds to screen line 'n' in the window table is different than the control/format information in the #ctrl array. inwindow ( a - n f | If flag returned is true. ) ( a - f | If the flag returned is false. ) Returns a true flag if the character residing at the specified address 'a' in the text belongs to one of the lines represented in the window table. If the flag returned is true, the screen line number which contains the character will also be returned. selected ( - ) Sets the update bits which correspond to the lines in the window table which contain the current selection so that the next time the screen is redisplayed the selection will displayed with proper highlighting. stepahead ( n1 - n2 ) Given the screen line number 'n1' of a line in the window table, returns the screen line number 'n2' of the next line in the window table which contains text. stepahead skips over blank lines and "do-nothing" lines. stepback ( n1 - n2 ) Given the screen line number 'n1' of a line in the window table, returns the screen line number 'n2' of the first previous line in the window table which contains text. stepback skips over blank lines and "do-nothing" lines. visible? ( a - f ) Returns a true flag if the character residing at the specified address 'a' in the text belongs to a line which is currently visible on the screen. _______________________________________________________________________________ High-Level Text Display Routines: bos-display ( n - ) ('beginning-of-selection-display') Causes the beginning of the current selection to be displayed at the half-line number 'n' on the screen. display ( - ) Completely redraws the display. If the format information about the selection range is already present in the window table, refresh is used to redraw only those screen lines which require updating. If the lines which contain the selection range are not represented in the window table, new-display is used to completely recalculate the window table and to completely redisplay the window contents. eos-display ( n - ) ('end-of-selection-display') Causes the end of the current selection to be displayed at the half-line 'n' on the screen. fit-display new-display ( - ) Causes the end of the current selection to be displayed on the middle line in the display. redisplay ( - ) redisplay is a less comprehensive version of display . redisplay should be used when a partial, rather than a complete display restoration, is required. redisplay will try to redraw only the section of the screen which has changed, but will redraw the entire display if necessary. redisplay 'wraps' the text starting one line above the line which has changed. redisplay will continue wrapping and redrawing lines on the screen until it encounters a line which was not affected by the change. redisplay was designed for use after insertions and deletions at the gap have occurred (normal typing input is an example of an insertion at the gap). scrollback ( - ) Tries to scroll the lines on the screen down by one line. If there are previous lines to be displayed, scrollback will first collapse the selection if it is extended. Next, the cursor is repositioned to the start of the line which precedes the line which currently contains the cursor (the #ctrl array is filled with information about the previous line and the bos is set to point to the address in the %wr field of the array). scrolldown is then used to scroll the entire screen image down. Finally, the update bits which correspond to the top and bottom visible lines in the display are set and refresh is used to redisplay them. The top line requires redisplay because it was just scrolled in. The bottom line could be left in a half visible state after the scroll operation. If this is the case, refresh will detect it and erase so that it will not be shown until it completely fits in the display. scrolldown ( n - ) Used by the higher-level scrolling word scrollback to scroll those lines which do not require redisplay downward on the screen. Moves the screen bit image down by 'n' lines and moves the entries in the window table down by 'n' entries so that each entry still corresponds to the the proper half-line on the screen. Fills the invalid entries at the top of the window table with new format information. scrollfwd ( - ) Tries to scroll the lines on the screen up by one line. If there are subsequent lines to be displayed, scrollup will first collapse the selection if it is extended. Next, the cursor is repositioned to the start of the line which follows the line which currently contains the cursor (the #ctrl array is filled with information about the following line and the bos is set to point to the address in the %wr field of the array). scrollup is then used to scroll the entire screen image up. Finally, the update bits which correspond to the top and bottom visible lines in the display are set and refresh is used to redisplay them. The bottom line requires redisplay because it was just scrolled in. The top line could be left in a half visible state after the scroll operation. If this is the case, refresh will detect it and erase so that it will not be shown until it completely fits in the display. scrollup ( n - ) Used by the higher-level scrolling word scrollfwd to scroll those lines which do not require redisplay upward on the screen. Moves the screen bit image up by 'n' lines and moves the entries in the window table up by 'n' entries so that each entry still corresponds to the proper half-line on the screen. Fills the invalid entries at the bottom of the window table with new format information. _______________________________________________________________________________ Summary _______________________________________________________________________________ _______________________________________________________________________________ Line Output Buffer integers: A0 integer &horiz ( Number of horizontal half-spaces on a line. ) 4 integer lbufwide ( Width of a character entry in the line buffer. ) lbuflen ( Length of lbuff, build sets, print uses. ) lbufwidth ( The width at the last real char in lbuff . ) bosptr ( Pointer into lbuff for bos . ) eosptr ( Pointer into lbuff for eos . ) _______________________________________________________________________________ disp integers: 0 integer invbit ( Inverse video bit. ) 1 integer boldbit 2 integer ulinebit ( Underline bit. ) 3 integer dlinebit ( Dotted underline bit. ) 4 integer stopbit ( When set, marks the end of the line output ) ( buffer contents. ) 7 integer smallbit 01 integer $inv ( Mask used to check inverse video bit. ) 02 integer $bold ( Mask used to check bold bit. ) 04 integer $uln ( Mask used to check underline bit. ) 08 integer $dln ( Mask used to check dotted underline bit. ) 10 integer $end ( Mask used to check "end of buffer data" bit. ) 80 integer $half ( Mask used to check half-wide character bit. ) _______________________________________________________________________________ Display-only Characters: 02 ( Thin, horizontal bar which goes under a ) ( document number. ) 03 ( Thin, horizontal bar which goes under an ) ( explicit page number. ) 04 ( Thin, horizontal bar which goes under an ) ( implicit page number. ) 07 integer lok ( Gray, vertical, locked text character. ) 08 integer tab0 ( Flat, horizontal part of the tab arrow. ) 0A integer markbl ( A white space character. ) 0E integer ( Horizontal component used to construct ) ( an implicit page line. ) 0F integer ( Horizontal component used to construct ) ( selected version of an explicit page line. ) 10 ( Special '0' used for page numbering. ) 11 ( Special '1' used for page numbering. ) 12 ( Special '2' used for page numbering. ) 13 ( Special '3' used for page numbering. ) 14 ( Special '4' used for page numbering. ) 15 ( Special '5' used for page numbering. ) 16 ( Special '6' used for page numbering. ) 17 ( Special '7' used for page numbering. ) 18 ( Special '8' used for page numbering. ) 19 ( Special '9' used for page numbering. ) 1C integer tabspace ( Blank character which represents an ) ( unselected tab in the line output buffer. ) 1E ( Special 'H' character used for diagnostic ) ( display testing. ) 1F ( Special '#' character used for diagnostic ) ( display testing. ) _______________________________________________________________________________ Display and Text Characters: 09 integer tab1 ( Arrowhead part of the tab arrow. ) 0B integer ds ( Text: Document separator character. ) ( Display: Horizontal component used to ) ( construct a document separation line. ) 0C integer pb ( Text: Page break character. ) ( Display: Horizontal component used to ) ( construct an explicit page break line. ) 0D integer rtn ( Text: Carriage return character. ) ( Display: Arrow used for display of selected ) ( carriage return character. ) _______________________________________________________________________________ Screen Size Integers: 50 integer width ( # of bytes in one display line ) 54 integer /scan ( bytes in a scan line ) 54 integer /lscan ( # of "visible" bytes in a scan line ) 54 integer active/scan ( number of active bytes per line ) 158 integer height ( scan lines per display ) 10 integer bytes/char ( bytes in a font table entry ) 4 integer logbytes/char ( since we shift a lot ) 0E integer scans/char ( scan lines per character ) /scan scans/char * integer bytes/line ( bytes in a text line ) 16 integer lines/screen ( char lines on screen ) 0E integer scans/image ( height of a character ) 07 integer tophalf ( height/2 of a character ) 10 integer bytes/image ( NOTE: code assumes this value ! ) _______________________________________________________________________________ 3. Ruler/Status Area Display _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 4/28/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The editor ruler/status area is divided into two parts: the ruler bar and the status line. The status line has four separate areas: the line number icon, the indicator lights, the mode icons (paragraph style, line spacing, and keyboard I/II), and the gas gauge. The graphics used in the ruler/status area are each comprised of characters from a special ruler font. The routines used to update and display the ruler/status area are discussed in this chapter. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 The Ruler Bar 3 The Ruler Buffer 4 Displaying the Ruler Bar 5 The Status Line 5 Display of the Status Line 5 Updating the Current Line Number 6 Updating the Gas Gauge 7 The Indicator Lights 8 Initializing the Ruler/Status Area 8 Update of the Ruler/Status Area in the Main Editor Loop 9 Ruler/Status Area Display/Update Routines 11 Summary _______________________________________________________________________________ List of Illustrations _______________________________________________________________________________ 3.1 Ruler Character Set _______________________________________________________________________________ The Ruler Bar _______________________________________________________________________________ The ruler bar is updated each time through the main editor loop. The word responsible for updating the ruler bar (and the status line - discussed later) is rule . rule performs three actions: 1) Decides what the ruler should look like by examining the current tab, margin, and indent settings, and sets up a "ruler buffer" with information about the ruler appearance. The ruler buffer is similar in function to the line buffer used for text display. 2) Uses ~showrule (described below) to display the ruler bar. 3) Uses ~showstatus (described below) to display the status line. _______________________________________________________________________________ The Ruler Buffer The ruler buffer is 84 decimal bytes long and is set up in the track buffer area during execution of rule . Each byte in the ruler buffer corresponds to one character position on the screen. The bits in each byte are used to indicate which types of ruler items, if any, should be included in the ruler display area corresponding to the character position in the ruler buffer. The following types of items are found in the ruler: - Left margin mark - Right margin mark - Indent mark - Short tick mark - Long tick mark - Decimal tab mark - Normal tab mark When rule sets up the ruler buffer it first goes through and marks the positions of all of the tick marks, then it goes through and marks the left/right margin and indent marks, and, finally, it marks all of the tab stops. _______________________________________________________________________________ Displaying the Ruler Bar After the ruler buffer has been set up, rule uses the lower level ruler display word ~showrule to draw the ruler on the screen. The display of the ruler requires three steps: 1) The ruler buffer information is converted to character data which is laid in bit format into an off-screen buffer. 2) Checks the contents of the system integer blackruler . If blackruler is "on", the ruler should be black. The ruler image is complemented. 3) The bit image of the ruler is transferred to the proper location in the screen memory. A 65 (decimal) byte look-up table named rulersmarts is used to convert the byte information found in the ruler table to the character code corresponding to the ruler character which should be displayed on the screen. There are characters in the ruler font for each possible combination of ruler marks which may co-exist in one character space on the ruler. For example, there can be a left margin mark, a tick mark, and a tab mark in one character position but there cannot (or should not!) be a left and right margin mark in the same character position. The following diagram (3.1) shows all characters in the ruler font which are used in the ruler bar display. ~showrule steps through the ruler buffer converting byte codes into character codes (with the help of the look-up table). The character code is used to find the data for the corresponding character in the ruler font. Once the data is found, it is "drawn" into an off-screen display buffer which is also located in the track buffer area during the execution of ~showrule . ~showrule also draws the lines which surround the ruler bar into the off-screen buffer. After the ruler has been drawn off-screen the image is transferred to the proper location in screen memory to make it visible. NOTE: The ruler is drawn offscreen first because it was found that drawing it directly into screen memory produced too much flicker in the display. Also, the author of the ruler code notes that ~showrule is very similar in function to ~disp (the routine used to draw text on the screen). Since the display of the characters in the ruler font is so much more straight- forward than the display of text (the ruler font char- acters can have no accents or attributes), it was decided that a separate routine, ~showrule , which was optimized for the fast display of the ruler, should be used instead of the more general case ~disp display routine. Diagram 3.1 The Ruler Character Set _______________________________________________________________________________ The Status Line _______________________________________________________________________________ The status line contains information about the current line number in the text, the line spacing, justification style, and keyboard currently in use, and the amount of available memory. The status line is updated each time through the main editor loop. The word rule , described above, uses the word ~showstatus to update the status line display. The characters in the ruler font which are used for the display of the status line are shown in the following diagram (DIAGRAM x.3). _______________________________________________________________________________ Display of the Status Line The method used to display the status line is very similar to the method used to display text. In fact, the low-level screen display routine ~disp is used to draw the status line on the screen. The main difference between text display and status line display is that the character information for the status line is kept in a special buffer called the statbuff ('status-buffer'). The information in the status buffer has the same format as the information in the line output buffer, each character is described with four bytes of data (see the discussion of disp in the "Text Display" chapter). The status line character information is kept in a separate buffer so that its entire contents will not have to regenerated each time through the editor loop. The only data which are updated by ~showstatus each time through the loop are the paragraph style, line spacing, and keyboard settings. The new values for these items is taken from the #ctrl array. The other status line information, the line number, gas gauge, and indicator lights, are only updated as necessary. _______________________________________________________________________________ Updating the Current Line Number The word checkline# is used to update the current line number in the status line. The line number is updated only if required. The system integer oldlnl is used to hold the line number currently displayed in the status line. checkline# compares the local line number found in the #ctrl array to the oldlnl value and, if the line numbers are different, will directly update the line number information in the status buffer. _______________________________________________________________________________ Updating the Gas Gauge The gas gauge is used to indicate how much memory is available for use. The following calculation is used to determine the amount of free memory: beot gap - ( Amount of free memory in the text area. ) applic here - ( Amount of free memory in the Forth area. ) + ( The sum of these two values is the amount ) ( of memory available for the user's text. ) The word checkgauge is used to update the gas gauge setting. The gas gauge is updated only when necessary. The system integer oldgauge is used to hold the free memory value used to generate the currently displayed gas gauge setting. If the gas gauge requires updating, checkgauge will see to it that the gas gauge information in the status buffer is altered. Note: The gas gauge now supports two memory sizes: 256K and 384K. A longer gas gauge is displayed when the system has 384K of RAM. The length (in pixels) to be used for the gas gauge is determined during initialization and is stored in the system integer gaugesize . _______________________________________________________________________________ The Indicator Lights The word indicate is used to update the indicator light data in the status buffer. indicate is passed the address of the string to be displayed in the light and the light position to be used. There are 5 available indicator light display positions. A table called , whose address is kept in the statuslights system integer, holds two pieces of information for each indicator light: the maximum string length (in characters) which may be displayed in the light, and the offset into the status buffer to the display information for the indication light. Here is a complete list of light positions and the strings which may appear in the light positions. Light0 Light1 Light2 Light3 Light4 PHONE LEARN ? LOCAL PRINT (Low Battery Icon) DISK LEARN 1 BACKUP LEARN 2 CALC (etc...) SORT SEND ADD SPELL CHECK SPELL DELETE SPELL FORTH TARGET GETFORWARD Light1 is used by the PHONE command. In the future, other strings relating to the PHONE command may appear in Light 1 (RINGING, BAUD300, etc.). Light 2 is used exclusively by the LEARN command. Once a number has been assigned to a learn sequence the "?" is changed to the number (1-9). Light 3 is used by the LOCAL LEAP command. Light 4 is called the "thinking" light. It is used by all commands which could have a slight response delay. FORTH appears in the thinking light when the ANSWER command is used. TARGET and GETFORWARD are only used during the editor development cycle. Here is a typical use of indicate : " Sort" 3 indicate rule " Sort" puts the address of the string to display on the stack. 3 specifies that light 3 should be used. indicate changes the light 3 display information in the status buffer. rule causes the ruler bar and status line to be redisplayed. Here is an example of how an indicator light is turned off: 0 0 3 indicate rule _______________________________________________________________________________ The Low Battery Light The low battery light is not a typical indicator light. It is triggered by a hardware test rather than by a user command. When, if ever, the low battery light comes on, indicate will be used to display the image of a battery lying on its side. _______________________________________________________________________________ Initializing the Ruler/Status Area _______________________________________________________________________________ The word initruler is used to initialize the status line display. The system integer goldbytes holds the address of the data for the default status line display. initruler copies this default data into the status buffer. _______________________________________________________________________________ Update of the Ruler/Status Area in the Main Editor Loop _______________________________________________________________________________ The following sequence is used to update the ruler/status area each time through the main editor loop: ... checkline# checkgauge rule ... _______________________________________________________________________________ Ruler/Status Area Display/Update Routines: _______________________________________________________________________________ _______________________________________________________________________________ Ruler Bar Display Routines: ~showrule ( code routine, passes parameters in registers ) Displays the ruler bar. Takes byte information from the ruler buffer, converts it to ruler font character information, and draw an off-screen image of the ruler bar in the track buffer area. Then transfers the bit image directly to the screen. rule ( code routine, passes parameters in registers ) Causes the ruler bar and status line to be redisplayed. Decides what the ruler bar should look like (based upon the current margin, indent, and tab settings) and creates an encoded description of the ruler bar in the status buffer. Uses ~showrule to display the ruler bar and ~showstatus to display the status line. _______________________________________________________________________________ Status Line Display Routines: ~showstatus ( code routine, parameters passed in registers ) Updates the paragraph style, line spacing, and keyboard information in the status buffer and then uses ~disp to redisplay the status line. >status ( a n1 n2 n3 - ) Places the string of information located at address 'a', which is 'n2' bytes in length, into the status buffer starting at an offset of 'n1' and uses 'n3' for the additional information required by disp . bl# ( n1 - n2 ) If 'n1' is non-zero, converts it to its corresponding ASCII value. If 'n1' is zero, converts it to the ASCII value for a space. Used by checkline# . checkline# ( - ) Updates the line number in the status line, and the contents of the oldlnl system integer, if necessary. newgauge? ( - f ) Checks to see if the gas gauge needs to be redrawn. If it does, newgauge? returns a true flag and updates the contents of the oldgauge system integer. checkgauge ( - ) Redraws the gas gauge if necessary. indicate ( a n1 n2 - ) Places the string located at address 'a' of length 'n1' into indicator light 'n2' and redisplays the status line. _______________________________________________________________________________ Ruler/Status Area Initialization: >lbuff ( a1 a2 n - ) Moves the 'n' bytes located starting at address 'a1' to memory starting at address 'a2'. Two bytes of zeros are appended to each two bytes of data transferred so that the final data is in the 4-byte format required by disp . initruler ( - ) Initializes the oldgauge , oldlnl , and gaugesize system integers and copies the default status line data to the status buffer. _______________________________________________________________________________ Summary _______________________________________________________________________________ _______________________________________________________________________________ Ruler/Status Area Data and Data Structures: statbuff ( Holds address of display buffer for status line. ) rulersmarts ( Holds address of look-up table for determining ruler ) ( display characters. ) statuslights ( Holds address of status lights table. ) goldenbytes ( Table of status line initialization data. ) goldbytes ( Holds address of status line initialization data. ) #goldenbytes ( Holds length of status line initialization data. ) #goldenmodes ( Holds length of mode initialization data. ) _______________________________________________________________________________ Offsets into Status Buffer: indichars ( Holds offset to indicator data in the status buffer. ) modechars ( Holds offset to mode icon data in status buffer. ) gaugepos ( Holds offset to gas gauge data in status buffer. ) _______________________________________________________________________________ Ruler/Status Screen Positioniong Information: rulerstart ( Scan line at which ruler area starts. ) ruleredge ( Left edge position for ruler/status area. ) _______________________________________________________________________________ Ruler/Status Area Update Information: oldlnl ( Holds current line number displayed in status line. ) oldgauge ( Holds current gas gauge value displayed in status ) ( line. ) blackruler ( Holds flag used to indicate whether ruler area ) ( should be black-on-white or white-on-black ) _______________________________________________________________________________ 4. The Cursor _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 9/25/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The cursor routines are used by almost all of the different editor commands. The cursor routines allow the cursor to be repositioned, give information about the cursor (state, size of selection,location), and turn the cursor on or off. All of the cursor routines, and their associated system integers, are gathered here for easy reference. _______________________________________________________________________________ _______________________________________________________________________________ Cursor Routines _______________________________________________________________________________ ( - ) Turns the cursor off. Puts a false flag in cursorstate to set the cursor state to off. Converts the pixel position of the cursor, held in the cx and cy system integers, to a position expressed in half-characters and half-lines. Moves the bits which were under the cursor, which were saved away in the cursorbuf memory buffer, back into place. Next, checks rulerblink? to see if the ruler cursor was also being flashed. If it was, removes the ruler cursor by filling the ruler cursor area in with the current ruler background color. ( - ) Turns cursor on. Puts a true flag in cursorstate to set cursor state to on. Converts the pixel position of the cursor, held in the cx and cy system integers, to a position expressed in half-characters and half-lines. Saves the bits which will be under the cursor in the cursorbuf memory buffer. Checks cwidth to see if the cursor is wide or narrow. Gets the proper cursor image either from ncursorimage ('narrow-cursor-image') or wcursorimage ('wide-cursor-image'), masks out the areas of the cursor which are not needed (so that the character shows through the cursor), and draws the cursor image on the screen. Next, checks rulerblink? to see if the ruler cursor should be flashed. If so gets the height of the ruler cursor from hrulercursor and the current ruler color from blackruler (to determine the color for the ruler cursor) and draws the cursor in the ruler area. ?expanded ( - f ) Checks the contents of the cstate system integer. If cstate holds a '3', if the cursor is expanded, a true flag is returned. ?extended ( - f ) Checks the contents of the cstate system integer. If cstate holds a '2', if the cursor is extended, a true flag is returned. ?split ( - f ) Checks the contents of the cstate system integer. If cstate holds a '-1', if the cursor is split, a true flag is returned. blink ( - ) If cursorblock holds a 0, which means the cursor is allowed to blink, blink will cause the cursor to flash. If the cursor is currently on, if cursorstate holds a -1, will be used to hide the cursor. If the cursor is currently off, if cursorstate holds a 0, will be used to show the cursor. The number of ticks until the next blink, either ontime (=$19), or offtime (=$19) is stored in the bticks system integer. If the text is clean, the ticks value is divided by 4 (to speed the cursor blink rate up) before being saved. cursorline ( - n ) Checks the character which the cursor is currently over (uses the contents of the cpos system integer) and the cursor state (checks the contents of the cstate system integer) to determine which line the cursor is on relative to the current line. Returns the relative line offset between the current line and the line which holds the cursor. If the cursor is split it will always be on the current line and a line offset of 0 will be returned. If the cursor is narrow and on a page break preceded by a break, the cursor is on the previous line and a line offset of '-1' is returned. If the cursor is wide and on a page break then the cursor is on the next line and a '1' is returned. cursoroff ( - ) Forces the cursor off and leaves the cursor in a deactivated state. To deactivate the cursor a false flag is placed in cursorblock . If the cursor is current on, is used to hide the cursor. cursoron ( - ) Forces the cursor on and leaves the cursor in an activated state. If the cursor is currently visible, if the cursor? integer holds a true flag, and the cursor is currently off, will be used to show the cursor and a false flag will be placed in cursorblock to activate the cursor. extend ( - ) Checks to see if the cursor is currently extended. If it is not, the selection is extended. First, extend checks to make sure the selection start and end points are in the right order. If the op is located before the beot the selection will be extended to the left. The bos is set equal to the op , op bos to ; the eos is already properly positioned. If the op is located after the beot the selection beginning and end points must be reversed. The eos is set to op nextchar , the op is set equal to the bos, bos op to , and the gap is adjusted in response to the new eos position. Now that the pointers are properly positioned the text is redisplayed to show the extended cursor position. forceop is turned on so that the op will follow the next character typed. extendedcursor ( - ) Uses widecursor to make the cursor wide and places a '2' in cstate to specify an extended cursor. findnarrow ( - ) Sets cx and cy for a narrow cursor. Uses cursorline to find the relative line position of the cursor. If a narrow cursor is not on the current line, then it must be on the end of the previous line. If the narrow cursor is on the previous line stepback is used to get control/format information about the previous line. cy , expressed in half-lines, is calculated by using inwindow to get the text line number in which the cursor is located and then firstseen is subtracted from the text line number to calculate the screen half-line number on which the cursor resides. If the cy position indicates that the cursor is visible in the window loadline and build are used to build the character display version of the line in the line output buffer and findwidth is used to calculate the width of the entire line in order to set cx . The cursor width is set to the width of the last character in the line. If the narrow cursor is on the current line or, if the narrow cursor becomes positioned above the top of the window, findsplit is used to set cx and cy . findsplit ( - ) Sets cx and cy for either a split cursor or for a narrow cursor which is in the middle of a line or is positioned above the top of the window. Saves the screen line number which contains the cursor in cy . Checks to see if the cursor is on a page break character. If it is on a page break, the cursor cx is set so that the cursor is placed at the indent and cwidth is set to narrow. Otherwise, the cursor is on a normal character. findwidth is used to find the x position at which the cursor should be placed and getwidth is used to determine how wide the cursor must be to cover the character which it is over. findwide ( - ) Sets cx and cy for a wide cursor. If the entire wide cursor is located on the current line, inwindow is used to find the line number which contains the cursor and then firstseen is subtracted from the screen line number to calculate cy , which is expressed in half-lines. If the end of selection is not off the end of the line (?) the value in the eosptr is used to find both the width of the character under the blinking portion of the cursor, cwidth , and to find the x position of the cursor, cx . If the end of selection is at the end of the line, the cursor width is always set to wide and cx is set to the width of the entire line. If the entire cursor is not on the current line then it must be located at the start of the following line. If the following line is a page break, the cursor will be placed at the indent (cx = #indent) and the cursor width is set to wide (cwidth = 1). Otherwise, the cursor is positioned either at the indent, if a valid character lies at the indent, or over the first valid character encountered, if there is not a valid character at the indent. findwidth ( a - n ) Given the address of a character in the line output buffer, findwidth will calculate the widths of characters in the line output buffer up to and including the specified character and will return the result, expressed in half-character widths, on the parameter stack. fixcursor ( - ) Checks to see if the cursor has gone off the bottom of the display: cy seenlines 2- > . If it has, the screen is scrolled up until the line the cursor is on is at the bottom of the display and the screen display is refreshed. getwidth ( a - n ) Given a pointer 'a' to a character in the line output buffer, returns the width of the character expressed in half-characters. The result can only be '1' (one half-character) or '2' (two half-characters). narrowcursor ( - ) Tries to force the cursor to a narrow state. If the cursor is located right after the start of a locked range of text, it is not allowed to be made narrow so widecursor is used to make it wide. Otherwise, cstate is set to 0 to indicate a narrow cursor, cpos is positioned at eos prevchar , and findnarrow is used to set the cursor cx and cy position. narrowcursor? ( - f ) Checks the contents of the cstate system integer. If cstate holds a '0', if the cursor is narrow, a true flag is returned. real? ( c - f ) Returns a true flag if the character 'c' is a "real" character, that is, a character which may appear in the text ($0B<=char<=$0D or $20<=char<=$DF). resetcursor ( - ) Repositions the cursor according to the cursor state. If cstate is negative, splitcursor is used to position the split cursor. If cstate is '0' narrowcursor is used to position the narrow cursor. If cstate is '1' widecursor is used to position the wide cursor. If cstate is '2', extendedcursor is used to position the extended cursor. splitcursor ( - ) Puts a '-1' in cstate to indicate a split cursor and uses findsplit to set up the cx and cy for the split cursor. widecursor ( - ) Tries to force the cursor to a wide state. If the end of selection is located right at the end of a locked range of text, it is not allowed to be wide so narrowcursor is used to make it narrow. Otherwise, cstate is set to 1 to indicate a wide cursor, cpos is positioned at eos prevchar , and findwide is used to set the cursor cx and cy position. widecursor? ( - f ) Checks the contents of the cstate system integer. If cstate holds a '1', if the cursor is wide, a true flag is returned. _______________________________________________________________________________ "Place" Placement Routines: pushpos ( - n1 n2 n3 n4 n5 n6 ) ('push-position') Push the contents of the key integers which define the editor's state onto the parameter stack. The integers pushed are: op , pop , bos , cstate , eos , and gapline . swappos2 ( - ) Swaps the saved and current state variables for the editor (needs more explanation). savepos ( - ) Saves the contents of the key editor state integers into a backup set of integers. The backup state integers are named: oldop , oldpop , oldbos , oldcstate , oldeos , and oldtopline . swappos ( - ) Swaps the contents of the backup editor state integers with the contents of the current editor state integers. savepos2 ( - ) Saves the contents of the current editor state integers in a special set of backup state integers used only by the creep and scroll routines. The names of these special backup state integers are: oldop2 , oldpop2 , oldbos2 , oldcstate2 , oldeos2 , and oldtopline2 . _______________________________________________________________________________ Cursor Integers _______________________________________________________________________________ blinktime Holds the number of ticks until the next blink. bosptr Holds the offset into the line output buffer to the bos character. cpos Holds the text address of the character over which the cursor is currently positioned. cursor? Holds a flag that, if true, means the cursor is visible. cursorblock System integer which controls the blinking of the cursor. If cursorblock holds a true flag, the cursor will not blink. cursorbuf System integer which holds the address of the memory buffer which is used to hold the bit image of the screen contents which are currently under the cursor. cursorstate System integer which holds the flag which represents the current state of the cursor. A true flag means the cursor is on (blinking) and a false flag means the cursor is off (not blinking). cwidth Holds the current width of the cursor expressed as: #half-spaces - 1 . A value of 0 means the cursor is is one half-space wide and a value of 1 means the cursor two half-spaces wide (full-width). cx Holds the x position of the cursor expressed in pixels. cy Holds the y position of the cursor expressed in pixels. eosptr Holds the offset into the line output buffer to the eos character. hrulercursor Holds the height of the ruler cursor expressed in pixels. ncursorimage Cursor image for narrow cursor. offtime How long to wait after turning cursor on (19 ticks). ontime How long to wait after turning cursor off (19 ticks). rulerblink? Holds a true flag if the ruler cursor should be flashed. wcursorimage Cursor image for wide cursor. _______________________________________________________________________________ Cursor Placement Integers _______________________________________________________________________________ _______________________________________________________________________________ Integers Which Hold the Current State of the Editor: op Holds the address of the old cursor place. pop Holds the address of the previous old cursor place. bos Holds the address of the beginning of selection (listed previously). cstate Holds the current state of the cursor: split (negative), narrow (0), wide (1), or extended (2), or expanded (3). eos Holds the address of the end of selection (listed previously). gapline Holds the number of the screen half line in which the gap is located (actually, where the 'gap 1-', or bos is located). p Place. po Pointer. _______________________________________________________________________________ Integers Which Hold the Previous State of the Editor: oldop oldpop oldbos Holds previous position of the bos. oldcstate oldeos Holds previous position of the eos. oldtopline _______________________________________________________________________________ Integers Which Hold the Previous State of the Editor: (Used by the creeping and scrolling routines) oldop2 oldpop2 oldbos2 oldcstate2 oldeos2 oldtopline2 _______________________________________________________________________________ 5. What's In the Text _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 5/15/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: This section should give you an idea of what types of data are stored in the text and which editor words may be used to locate and analyze the different types of data. The structure and locations of overstrike characters, character style bytes, paragraph format packets, and document format packets, are discussed. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 Standard ASCII Characters and Bare Accent Characters 3 Break Characters 3 Finding Character Data 5 Overstrike Characters 6 Text Marker Characters 6 Character Style Markers 7 Skip Markers 8 Paragraph Format Packets 9 Document Format Packets 9 Finding Data in the Text 10 Routines 18 Summary _______________________________________________________________________________ List of Illustrations _______________________________________________________________________________ 4 Cat Character Set 7 Skip Information _______________________________________________________________________________ Standard ASCII Characters and Bare Accent Characters _______________________________________________________________________________ The typeable characters in the Cat character set (see following diagram) have character codes which range from $09 to $C8. In the editor, any byte data which has a value of $CF or less is assumed to be a byte of character data. The characters with codes from $00 to &lastasc ('last-ascii', $AF) are considered to be a part of the standard ASCII character set. The characters with codes from $00 to &lastchr ('last-character', $BF), which are all of the ASCII characters plus a few accent characters which may be typed individually (bare accents), represent all characters which may be individually typed and displayed on the keyboard. _______________________________________________________________________________ Break Characters A "break" character is any character which can cause a new paragraph to be formed in the text. Carriage returns, document separators, and page breaks are all classified as break characters as well as standard ASCII characters. Four words are used to find break characters in the text: firstbreak , lastbreak , nextbrk , and prevbrk . firstbreak and lastbreak search a specified text region and return either the address of the first or last break character in the region. nextbrk and prevbrk return the address of the break character which either follows or precedes the break character located at a specified address. There is also a word called break? which analyzes a character code input to determine whether or not the character code belongs to a break character. _______________________________________________________________________________ Finding Character Data Because the text does not contain only character data, special words are included for moving from character data byte to character data byte in the text. prevchar takes the address of a character in the text and returns the address of the previous character in the text. nextchar takes the address of a character in the text and returns the address of the next character in the text. If prevchar or nextchar encounter a character with an overstrike in the text, they will always return the address of the overstruck character and never the address of the overstrike character (overstrike characters are not independent characters). Both of these words have the ability to recognize and skip over any non-character data they might encounter. Figure 5.1 The Cat Character Set _______________________________________________________________________________ Overstrike Characters _______________________________________________________________________________ The characters with codes from &firstacc ('first-accent', $C0) to &lastacc ('last-accent', $CF) are special overstrike (or accent) characters which are treated specially both in the text and on the display. Overstrike characters are treated specially because, although they can be typed from the keyboard, they cannot be indepently displayed on the screen; they must always be displayed with (on top of) another character. Overstrike characters do not exist independently in the text either, they are always associated with the character they accent. The character code for the overstruck character is combined with the character code for the overstrike character to form a two byte character code in the text (the overstrike character code always follows the overstruck character code). For example, the umlaut character, a 'u' with a '`' character over it, would be represented in the text with code for a 'u', $75, and the code for a '`', $C0; i.e. $75C0. NOTE: The double underline ($C4) is a special exception to the above description of the treatment of overstrikes. The only character the double underline may combine with is the permanent space character ($93). In fact, whenever a double underline is typed, it is automatically combined with the permanent space character. This means that the double underline will always appear to be one of the standard, typeable characters to the user. In the text, however, the double underline is really viewed as a permanent space character ($93) which is overstruck with a double underline accent character. The word accentable? will check a character code to determine whether the corresponding character is a character which may be accented (overstruck). A carriage return is an example of a character which cannot be overstruck. Any character with a character code between and including $20 and $AF can accept an overstrike character. The word accent will take the address of a character in the text and, if the character has an overstrike character associated with it, will return the address of the overstrike character. NOTE: Many of the overstrike characters have normal character counterparts which are called "bare accents". A bare accent character looks like an overstrike character but can be independently typed by the user and causes only a single byte character code to be placed in the text. The character codes for bare accent characters start at $B0 and go up to $B8. _______________________________________________________________________________ Text Marker Characters _______________________________________________________________________________ The codes from &firstcmd ('first-command', $E0) to &lastcmd ('last-command', $EF) correspond to text marker characters which are used to mark packets of character, paragraph, or calculation data in the text. The codes from &firsthid ('first-hidden', $F0) to $FF are all used to represent data in the text. _______________________________________________________________________________ Character Style Markers A Cat character can be displayed with up to four character styles: plain, bold, underlined, and dotted underlined. Any combination of these four styles may be used for any character. If a character has any style associated with it, other than plain, it will be followed by a byte of style information in the text. For example, if you were to look at an underlined 'a' in the text you would find a byte $61 character code value for the 'a' immediately followed by a $E9, which is the style byte value used for characters which are underlined only. The character style (attribute) markers have values in the range from $E9 to $EF. The bit representations for the style marker values are listed below: Style Hex Value Binary Value Underline E9 1110 1001 Bold EA 1110 1010 Dotted Underline EC 1110 1100 Underline+Bold EB 1110 1011 Underline+Dotted ED 1110 1101 Bold+Dotted EE 1110 1110 Underline+Bold+Dotted EF 1110 1111 The chart shows that bits #0, 1, and 2 are the real style bits in the style byte. Bit #0 (the leftmost bit) of the style byte is the underline bit, bit #1 is the bold bit, and bit 2 is the dotted underline bit. If a style bit is set (is a 1), the corresponding character will be displayed with that character style. _______________________________________________________________________________ Gap "Skip" Markers The gap area is a discontinuity in the text data. To let words which search through the text data know where the gap begins and ends, special information is stored in the text area on both sides of the gap. The special information is 4 bytes in length and contains the following information: Beginning of gap: _________________________________________________________________ | skip character | offset to the end of the gap | ----------------------------------------------------------------- 1 byte 3 bytes End of gap: _________________________________________________________________ | offset to the start of the gap | skip character | ----------------------------------------------------------------- 3 bytes 1 byte Figure 5.2: Skip Information The skip character is a text marker character with a character code of $E0. The skip character either follows or precedes 3 bytes of offset information. Note that the order of the information is reversed on the different sides of the gap. The following memory dumps, executed from within the tForth environment, show how the skip information looks in memory: gap 10 dump 61C7D 90 01 4E AD 64 61 74 61 20 69 73 20 73 74 6F 72 ..N.data is stor ok skip + offset beot 10 - 10 dump xxxxx 64 77 6F 65 73 65 01 4E B0 90 0D C0 C1 0D C0 C1 dwoese.N........ ok offset + skip When routines encounter a skip character while looking through the text data, they need to know how to get over to the other side of the gap. Two assembly language words are available for this purpose: ^sk> ('skip-to-beot') and ^sk< ('skip-to-gap'). Both routines should be accessed with the 68000 'JSR' ('jump-to-subroutine'). Given the address of a skip character in the text, these routines will extract the offset from the skip information and will return the address of the other side of the gap. _______________________________________________________________________________ Paragraph Format Data A paragraph is any sequence of characters which are surrounded on both sides by break characters (described above). Paragraph format data describes the style (margins, tabs, indents, justification, and line spacing) in which the paragraph of text should be displayed. Paragraph format packets, or just "format packets", are located in the text before the paragraph which they affect. A format packet contains data from the paragraph format information section of the control/format array in an simple encoded form. Each nybble ($x) of data in the paragraph format section is combined with a nybble with all bits set ($F) to form a byte of encoded format packet data ($Fx). The format packet data is marked in the text with a paragraph format marker whose character code value is $E2. The character code values for the format marker character, and for all bytes of data in the format packet, are greater than the highest allowable value for character data, so that neither the format marker or packet data will be treated as character data. _______________________________________________________________________________ Manipulating Paragraph Format Packets makepkt takes the paragraph formatting information from the #ctrl array, nybble-encodes the information, and places the new format packet at a specified location in the text. getpkt performs the converse action. It decodes the data in the format packet at a specified location in the text and places the information in the proper fields in the #ctrl array. brk+ , findpkt , fpkt? are words used to find/identify format packets in the text. samepkt? compares two format packets to determine if they are the same. copypkt , movepkt , rotatepkts , and swappkt are all used to move and insert format packets in the text. pktbytes examines a region of the text and returns the total number of bytes of format data in the region. This is usually used to determine how large the undo buffer needs to be in order to hold packet information required for any future undo operation. savepkts and swappkts are used to transfer format packets back and forth between the undo buffer and the text. _______________________________________________________________________________ Document Format Data A document is any sequence of characters which is surrounded on both sides by document separator characters. Document format data describes the printed and display appearance of the pages in the document (number of lines per page, number of blank lines above the top line on the page and below the bottom line on the page) and whether the document is alterable. Document format packets, are located in the text immediately after the document separator character which marks the start of the document which they affect. A document format packet contains data from the document format information section of the control/format array in an simple encoded form. Each nybble ($x) of data in the document format section is combined with a nybble with all bits set ($F) to form a byte of encoded document format packet data ($Fx). Every document separator character in the text is followed by a packet of document format data. _______________________________________________________________________________ Manipulating Document Format Packets nextdsorcalc ('next-document-separator-or-calc-marker') is used to locate document format packets in the text. makedpkt makes a new document format packet using the current document formatting information found in the #ctrl array and places it at a specified location in the text. getdpkt decodes a document format packet in the text and places the information in the relevant fields in the #ctrl array. getdocpkt is a special version of getdpkt which is used to transfer document format information from the set up (user configuration) variables to the #ctrl array. dpktbytes examines a region of the text and returns the total number of bytes of document format data in the region. This is usually used to determine how large the undo buffer needs to be in order to hold the document packet information required for any future undo operation. savedpkts and swapdpkts are used to transfer document format packets back and forth between the undo buffer and the text. _______________________________________________________________________________ Finding Data in the Text _______________________________________________________________________________ The generic text data search words prevmatch and nextmatch can be used to find the first previous or following occurrence of a specific data value in the valid text area. Both words are conscious of the location of the gap and will skip over it if necessary. _______________________________________________________________________________ Routines Which Interact with the Special Data in the Text _______________________________________________________________________________ _______________________________________________________________________________ Handling Skip Data: ^sk> a0: Address of skip character in the text. Given the address of the skip character which lies at the start of the gap region, ^sk> will extract the offset to the other side of the gap from the skip information, add the offset to the gap start address, and return the address of the beot . ^sk< a0: Address of skip character in the text. Given the address of the skip character which lies at the end of the gap region, ^sk< will extract the offset to the start of the gap from the skip information, add the offset to the gap end address, and return the address of the start of the gap. _______________________________________________________________________________ Finding ASCII Data: ^nextchar ( Uses the A0 and D0 registers. ) Lower-level word used by nextchar . Returns the text address of the character which comes after the character whose address is in the A0 register. ^prevchar ( Uses the A0 and D0 registers. ) Lower-level word used by prevchar . Returns the text address of the character which comes before the character whose address is in the A0 register. firstbreak ( a1 a2 - a3-or-0 ) Searches the text in the region which starts at address 'a1' and ends at address 'a2'. Returns the address of the first break character encountered. Returns a '0' if no break is encountered. lastbreak ( a1 a2 - a3-or-0 ) Searches the text in the region which starts at the address 'a1' and ends at the address 'a2'. Returns the address of the last break character encountered. Returns a '0' if no break is encountered. nextbrk ( a1 - a2-or-0 ) ('next-break') Given the address of a location in the text, 'a1', returns the address of the next successive break found in the text. A '0' will be returned if no successive break is found. nextchar ( a1 - a2 ) Returns the text address 'a2' of the character which comes after the character at the address 'a1'. prevbrk ( a1 - a2-or-0 ) ('prev-break') Given the address of a location in the text, 'a1', returns the address of the first previous break found in the text. A '0' will be returned if no previous break is found. prevchar ( a1 - a2 ) Returns the text address 'a2' of the character which comes before the character at the address 'a1'. _______________________________________________________________________________ Finding Data nextmatch ( n a - a' ) Searches forward in the text, starting from address 'a', until the next occurrence of the byte value 'n' is encountered. The address, 'a'', which contains the first occurrence of the byte value is returned on the stack. nextmatch will skip over the gap if encountered. WARNING: There are no boundaries on a nextmatch search. It will continue forever if the specified byte data value is not found. prevmatch ( n a - a' ) Searches backwards in the text, starting from address 'a', until the first previous occurrence of the byte value 'n' is encountered. The address 'a'' which contains the first previous occurrence of the byte value is returned on the stack. prevmatch will skip over the gap if encountered. WARNING: There are no boundaries on a prevmatch search. It will continue forever if the specified byte data value is not found. _______________________________________________________________________________ Analyzing ASCII Data: accent ( a - a'-or-0 ) Given the address 'a' of a character in the text, returns either the address 'a'' of the accent character associated with the original character , or zero if the original character does not have an accent. accentable? ( c - f ) Returns a true flag if the character 'c' is able to receive an accent ($20<=char code<=$AF). break? ( c - f ) Returns a true flag if the character 'c' is a character which would cause a new paragraph (a document separator, page break, or carriage return). page? ( c - f ) Returns a true flag if the character 'c' is a page break character. _______________________________________________________________________________ Handling Attribute Data: attribable? ( c - f ) Returns a true flag if the character 'c' can have an attribute (underlined, boldfaced, etc.). Characters with character codes in the range $20<=code<=$AF can accept attribute data. attribute ( a - n-or-0 ) Given the address 'a' of a character in the text, returns either the attribute byte 'n' for the character, or, if 0 if the character does not have an attribute associated with it. bare? ( c - f ) Returns a true flag if the character 'c' is a bare accent character. _______________________________________________________________________________ Getting Information About Format Packets: brk+ ( a1 - a2 ) Looks through the text, starting at the address 'a1' and returns the address 'a2' of the next possible (paragraph?) format packet in the text (why 'possible', why not just look for an 'E2' marker?). dpktbytes ( a1 a2 - n ) ('document-packet-bytes') Examines the text region between the start address 'a1' and the end address 'a2' and returns the total number of bytes 'n' of document format information found in the region. findpkt ( a1 a2 - a3 ) Searches the text range which starts at address 'a1' and ends at address 'a2' and returns the address 'a3' of the first paragraph format packet found (is this true?). fpkt? ( a - f ) Returns a true flag if a paragraph format packet follows the break character located at the address 'a'. getpkt ( a - ) ('get-packet') Decodes the format packet located at address 'a' in the text and places the format information in the #ctrl array. getdocpkt ( - ) Transfers document format information from the set-up array to the #ctrl array. getdpkt ( a - ) ('get-document-packet') Loads the information from the document format packet located at address 'a' in the text into the #ctrl array. nextdsorcalc ( a1 a2 - a3 ) Looks through the text region which starts at address 'a1' and ends at address 'a2'. Returns the address of the first document separator or calc marker encountered, if any. If no document separator or calc marker is found, return the end address of the region. pktbytes ( a1 a2 - n ) ('packet-bytes') Examines the text region starting at address 'a1' and ending at address 'a2' and returns the total number of bytes 'n' of format information found in the region. samepkt? ( a1 a2 - f ) Compares the paragraph format packets located at addresses 'a1' and 'a2' in the text and returns a true flag if they are the same. ^dfmt a0: Address in text where a document format packet is located. Decodes a document format packet in the text and places the format information in the #ctrl array. ^fmt> a0: Address in text where format packet is located. Decodes a format packet in the text and places the format information in the #ctrl array. _______________________________________________________________________________ Moving Format Packets Around: ( a1 a2 - ) ('bracket-swap-packet') Swaps the contents of the paragraph format packet which lies after the break located at address 'a1' with the contents of the paragraph format packet which lies after the break located at address 'a2'. copypkt ( a1 a2 - ) Copies the contents of the first paragraph format packet found on or after the text source address 'a1' over the contents of the first paragraph format packet found on or after the text destination address 'a2'. makedpkt ( a - ) ('make-document-packet') Encodes the document format information found in the #ctrl array and places the resulting document format packet at the specified address 'a' in the text. makepkt ( a - ) ('make-packet') Encodes the format information found in the #ctrl array and places the resulting encoded format packet at the specified address 'a' in the text. makespace ( a n - a' ) Tries to create a hole in the text at address 'a' of size 'n' bytes. If there is not enough room, an error message is issued. If there is enough room, makespace moves the text around, adjusts the text pointers, and returns the address where the desired space is located (the initial address could have been altered due to text movement). maxundo ( - n ) Returns the maximum capacity for the undo buffer expressed in bytes. movepkt ( a1 a2 - ) Creates a paragraph format size opening in the text at the destination address 'a2' and moves the paragraph format packet located in the text at address 'a1' into the opening. rotatepkts ( a1 a2 a3 - ) Rotates the contents of the three paragraph format packets located in the text at addresses 'a1' (=packet 1), 'a2' (=packet 2), and 'a3' (=packet 3). The rotation order is: packet 1 > packet 2 , packet 2 > packet 3 , packet 3 > packet 1. savedpkts ( - ) Move all document format packets located between the address found in the system integer prepkt and the start of the gap into the undo buffer. swapdpkts ( - ) Swap all document format packets located in the text between the address found in the system integer prepkt and the start of the gap with the document packets in the undo buffer. savepkts ( - ) ('save-packets') Move all format packets located between the address found in the system integer prepkt and the start of the gap into the undo buffer. swappkt ( a1 a2 - ) Checks to see if there are paragraph format packets at the text addresses 'a1' and 'a2'. If there are paragraph format packets at both locations, swaps the contents of the packets. If there is only a paragraph format packet at one of the locations, inserts a copy of the packet which does exist into the text at the location which did not contain a format packet. swappkts ( - ) ('swap-packets') Swap all format packets located between the address found in the system integer prepkt and the start of the gap with the corresponding format packets in the undo buffer. _______________________________________________________________________________ Summary _______________________________________________________________________________ _______________________________________________________________________________ Break Characters: 0B integer ds ( Document separator character code. ) 0C integer pb ( Explicit page break character code. ) 0D integer rtn ( Return character code. ) _______________________________________________________________________________ Text Markers: E0 integer &skip ( Skip token code. ) E2 integer &fmt ( Format packet code. ) E4 integer &calc ( Calculation packet code. ) E5 integer &lockedcalc ( Locked calculation packet code. ) E8 integer &attr ( Character attribute code. Used in ) ( arithmetic code words. ) EC integer &dln ( Dotted underline code. ) ( Used in arithmetic code words. ) _______________________________________________________________________________ Character Code Limit Values: AF integer &lastasc BF integer &lastchr C0 integer &firstacc CF integer &lastacc E0 integer &firstcmd EF integer &lastcmd F0 integer &firsthid _______________________________________________________________________________ Format Packet Values: 39 integer pktsize ( Size of a paragraph format packet ) ( in the text. ) 0E integer dpktsize ( Size of a document format packet in the text. ) _______________________________________________________________________________ 6. Inserting, Erasing, and Copying Text _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 5/20/87 Erase Discussion LC 7/87 Copy Discussion LC 8/24/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: Inserting (typing), erasing, and copying are the three most basic Cat editing operations. The inserting routines must decide what styles, if any, should be given to new characters being entered into the text. The insert routines will gather characters in the gap area until they get a chance to insert the block of characters into the text. The copy routines copy the current selection and insert the copied text into the text. The copying process is very similar to the text insertion part of the typing process. The erase routines will either erase forward or backward in the text depending upon the cursor state when ERASE is used. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 Inserting Text 3 Checking the Attribute State 5 Gathering Characters 7 Inserting Characters into the Text 8 Redisplaying the Text 9 Erasing Text 9 Preparing for Text Removal 10 Gobbling Text 10 Checking the Selection Length 11 The Relationship Between Break Characters, Paragraph Format Packets, and the Text 12 Checking for Format Packets in the Selection 14 Finishing Up the Gobble 14 Undo-ing a Gobble 15 Undo-ing an Ungobble 15 Removing a Selection 15 Copying Text 16 Routines Summary 16 Insert Routines 17 Erase Routines 21 Copy Routines _______________________________________________________________________________ List of Illustrations _______________________________________________________________________________ 6.1 Format Packets in the Text _______________________________________________________________________________ Inserting Text _______________________________________________________________________________ Characters typed at the Cat keyboard do not go directly to the editor. The interrupt routine responsible for scanning the keyboard array places key/character information in a low-level key event queue each time a key or keys changes its state (each time a key is pressed or released). When the editor is ready to receive characters it uses Forth keyboard I/O words to obtain key/character data from the queue. The Cat keyboard interface and the Forth words used to handle keyboard input are discussed in Chapter 13. Insert (with a capital 'I') is the editor word which takes typed character input and emplaces it in the text. The three main functions of Insert : (1) gathering characters, (2) inserting characters into the text, (3) redisplaying the text, are discussed below. ________________________________________________________________________________ Checking the Attribute State Before Insert can start gathering characters it must determine whether the characters currently being entered should be assigned any special character modifications (underlines, bold, etc.). If the cursor is narrow, the attributes associated with the two characters (the "attribute-determining characters) located before the character the cursor is over (before the gap) determine which attributes, if any, the new characters will inherit. If the cursor is not narrow, the two characters on either side of the cursor (the gap) are used to determine the new attribute. The algorithm used is the following: the attributes associated with the attribute-determining characters are AND'ed together to obtain the attribute value for the new character. This algorithm ensures that the new character will inherit only those attribute characteristics which are shared by the attribute-determining characters. If Insert finds that the resulting attribute value is '0' or $E8 (the result returned when two completely different attribute values, i.e. bold and underline, are AND'ed together), the new character will not be assigned an attribute. If the resulting value is a non-zero value which is not equal to $E8, it will be used as the attribute value for the new character. As an example, imagine that the cursor is wide and a new character is to be inserted between an underlined character and an underlined, boldface character. When the underline attribute value, $E9, and the underlined-bold attribute value, $EB are AND'ed together, the resulting value is $E9, the underlined attribute value. Since this value is not 0 or $E8, the new character, and all subsequent characters placed between the original attribute-determining characters, will be given the underline attribute. The following code excerpt from Insert shows the attribute assignment decision process: ... narrowcursor? if ( cursor is narrow, check 2 characters before gap ) gap prevchar attribute gap prevchar prevchar attribute and else ( cursor is not narrow, check characters on either ) ( side of gap ) gap prevchar attribute beot attribute and then dup e8 = if drop 0 then attrib to ( save attribute value or 0 in local variable ) ... _______________________________________________________________________________ Gathering Characters Insert uses continueinsert? (which uses the lower-level Forth word ) to obtain characters from the keyboard event queue. Insert will not terminate execution until the event queue is completely empty. As the characters are received (and assigned attributes if necessary), they are placed temporarily in the gap, immediately after the skip information. A local variable pointer named "place" is used to keep track of the offset into the gap to the position where the next received character should be placed. Before each character is placed in the gap area, Insert uses the word enoughtext to check the amount of available gap space. There must be at least enough room for current temporary insertion string and for a document format packet (in case the next character received is a document separator character) for Insert to proceed: ... place gap - ( length of the current insertion string ) dpktsize + ( the most gap space which could be ) ( required by the next character ) enoughtext not ( if there is not enough text, error ) if 2drop noerror error leave then ... If there is enough room, Insert will analyze the character just received to determine how to handle it. There are six possible types of characters Insert will have to handle: 1) Accented characters (2-bytes). 2) Accented character with an attribute (3 bytes). 3) Document separator characters (dpktsize bytes). 4) Normal character preceded by a bare accent character (2 bytes). 5) Normal character with attribute (2 bytes). 6) Normal character (1 byte). Accented characters are easy to recognize because their character value is greater than $FF, the maximum value which may be expressed with 1 byte. When an accented (2-byte) character is encountered it is stored, using w! , at the location currently pointed to by the place pointer. If the accented character is to receive an attribute, the accent part of the accent character is bumped over by 1 byte and the attribute value is inserted between the main character and its accent character. Whenever a document separator character is encountered, a document format packet must be created and inserted. makedpkt is used to construct a document format packet in the gap using the document formatting information currently found in the #ctrl array. A page break character is also inserted in the temporary gap string immediately after the document format packet. If the character was not an accented character or document separator character, it must be a normal character represented by a single byte character value. It is inserted, using c! , in the gap at the location pointed to by place . If the normal character is a character which can receive an accent and if the character which precedes the normal character in the gap is a bare accent character, the normal character and the bare accent character will be swapped and changed into a standard, accented (2-byte) character: ... swap c@ accentable? ( Can this character receive an accent ? ) place prevchar c@ bare? ( Is previous character a bare accent ? ) and ( If both of these cases is true... ) if place prevchar dup ( Duplicate address of bare accent. ) c@ ( Fetch the bare accent. ) place c@ ( Fetch the character to be accented. ) rot c! ( Store the character to be accented in ) ( the location where the bare accent was. ) 0F and C0 or ( Turn bare accent into a normal accent. ) place c! ( Store it immediately after the main ) ( character. ) else .... If the normal character is not turned into an accent character, Insert will check to see if the character should receive an attribute. If the character can and should receive an attribute the attribute value will be stored in the gap area, immediately behind the character. If the character does not receive an attribute Insert's character handling process is completed. The place pointer will be properly incremented and continueinsert? will be used to check for the availability of more characters. continueinsert? will return a true flag and a character code if a valid insertable character is available. If no valid character is available, a false flag will be returned and the Insert character handling loop will be terminated and the process of inserting the temporary character string into the text will be started. _______________________________________________________________________________ Inserting Characters into the Text The word insertblock is used to insert the characters in the gap into the text. Before inserting the string into the text, insertblock checks for the following cases: - Locked text. - Empty text. - Not enough room for insertion string. - Extended selection. If the text is locked, or if there is not enough room for the insertion, an error message is issued and no text is inserted. If the text is empty, the editor is initialized (using initedde , a word which has not yet been discussed) before the insertion. If the cursor is extended, it will be collapsed before the insertion. If the string is to be inserted, it will be placed either before or after the bos character. Since any extended selections were collapsed, the bos character will be the character located immediately before the start of the gap. If the cursor is narrow, or if the eos character is the last character in a range of locked text, AND if the bos character is not a bare accent character, the string will be inserted before the bos character. This means that the bos character must become the eos character and be moved to the other side of the gap: ... narrowcursor? eos eor = or gap prevchar c@ bare? not and if ( Make bos character the eos character ) ( and move it to the other side of the gap. ) then ... After the above test has been made, and the gap moved if necessary, the string is ready to be inserted. The selection is reset if necessary and the string is inserted starting at the gap location. If the character immediately before the gap was a bare accent, and if the first character in the inserted string is a character which may accept an accent, the two characters are swapped and turned into a real accented character pair. The gap position is incremented to just beyond the end of the inserted string and the text is marked as dirty. _______________________________________________________________________________ Redisplaying the Text After the text has been inserted, the bos pointer is reset: gap prevchar bos to the screen contents redrawn as is necessary: redisplay and the cursor is set to the wide cursor state. _______________________________________________________________________________ Erasing _______________________________________________________________________________ Erase (with a capital 'E') is the command associated with the ERASE key on the keyboard. Erase is used to remove character(s) from the text. The state of the cursor when Erase is invoked determines which characters Erase will remove. If the cursor is wide, Erase will remove all characters in the current selection from the text. If the cursor is narrow, each execution of Erase will cause the character under the narrow cursor to be removed from the text, "gobbled". _______________________________________________________________________________ Preparing for Text Removal Before text can be removed, three tests must be made: 1. Is the selection within a valid text range ? 2. Is the selection within a locked range of text ? 3. Does Erase undo preparation need to be performed ? trimselection is used to make sure the selection lies within a valid text range. If the user has used the LOCAL LEAP keys to reduce their working text area to a subset of the entire text, erasures can only occur within the local leap region of text. A local leap region of text will always be bounded on both sides by document characters. trimselection checks to see if the current selection flows over or includes either of the bounding document separator characters for the current local leap region and "trims" away any part of the selection which does not lie inside of the local leap region. If the entire selection lies outside of the local leap region, trimselection will return a false flag to indicate that the Erase operation cannot continue. Next, Erase checks to see if any part of the selection lies in a locked region of text. If any part of the selection is locked, the Erase operation is aborted. If the first two tests above were passed, the Erase operation will occur. First though, Erase checks to see if any undo preparation is required. If the previous operation was not an Erase operation, undo preparation must be performed. This involves clearing out the undo buffer (with clearundo), saving the current cursor state (with savepos), and clearing the contents of the format packet scratch area (by filling workpkt with zeros). If the previous operation was Erase , these undo preparations will have already been performed. Now we're ready to Erase . The text is marked as dirty (dirtytext? on) and the state of the cursor is checked. If the cursor is narrow, gobble will be used. Otherwise, removeselection will be used to remove text. _______________________________________________________________________________ Gobbling Text _______________________________________________________________________________ _______________________________________________________________________________ Checking the Selection Length gobble is the word used to remove the selection under the narrow cursor. The table below shows the possible contents and lengths of a selection under a narrow cursor: Selection Selection Contents (Size) Normal Character: ASCII code. (1 byte) Accented Character: ASCII code for char plus ASCII code for accent. (2 bytes) Attributed Character: ASCII code for char plus code for attribute. (2 bytes) Carriage return followed by paragraph format packet: ASCII code for CR plus format packet contents. (1 + paragraph format packet length) Document Separator: ASCII code for document separator plus document format packet contents plus paragraph format packet contents. (1 + document format packet length + paragraph format packet length) gobble checks for a selection length which is less than 1 byte in length. The only situation which can cause a narrow cursor selection length to be less than 1 byte is when the narrow cursor is positioned over the last document character in the text. Since the last document character cannot be erased, gobble responds to this situation by moving the bos back by one character and resetting the cursor to a wide state. That way, if the ERASE key is pressed again, the erasing will proceed backwards in the text, away from end of text document character. After the bos has been repositioned, redisplay is used to redraw necessary parts of the text: . . selsize 1 < ( Is the selection length less than 1 ? ) if eos prevchar bos to ( Move the bos back by one character. ) redisplay ( Redraw the screen contents. ) widecursor ( Make the cursor wide instead of narrow. ) exit ( Exit gobble immediately. ) then . . _______________________________________________________________________________ The Relationship Between Break Characters, Paragraph Format Packets, and the Text As you may recall from the section on "What's In the Text", paragraph format packets can only reside next to break characters (carriage return or document separator) in the text. A paragraph format packet controls the appearance of all text which follows it up to the next occurrence of a break character followed by a paragraph format packet. A paragraph format packet, and the break character which immediately follows the text which the paragraph format packet controls, are invisibly linked to each other. The diagram on the following page illustrates this relationship between paragraph format packets, break characters, and the text. As you can see, the paragraph format packet and its associate break character lie on opposite ends of the text they control. The confusing part about this arrangement is that when a break character is selected, any paragraph format information which follows it is also included in the selection. But, the paragraph format information which follows a break character is not the paragraph format information with which the break character is associated, it is the paragraph format information which controls the following paragraph ! As an example, refer to the left portion of the diagram on the following page. The carriage return which follows paragraph 1 has been selected for erasure. This means that both carriage return 1 and paragraph format packet 2 are included in the selection. We cannot simply dispose of the selection (paragraph format packet 2 is not associated with carriage return 1) nor can we remove only the carriage return (paragraph format packets must always be located after a break character). The solution is to remove carriage return 1 and to move paragraph format packet 2 immediately after the previous break character in the text, carriage return 0. In this example, since the carriage return being removed (#1) was also associated with a paragraph format packet (#1), paragraph format packet 2 will be moved into the location previously occupied by paragraph format packet 1 (which becomes invalid once its corresponding carriage return is erased). If carriage return 1 had not been associated with a paragraph format packet, paragraph format packet 2 would still have been moved after carriage return 0. The difference is that in the second case a space would have to made in the text after carriage return 0 for the insertion of paragraph format packet 2. Diagram 6.1 Format Packets in the Text _______________________________________________________________________________ Checking for Format Packets in the Selection Once gobble has checked the selection length, it uses pktbytes to check for paragraph format packets in the selection. The value returned by pktbytes , which is the number of paragraph format packet data bytes which where found in the specified region, is stored in the system integer fmtchrs. If the selection does contain paragraph format information, gobble is about to erase a break character. This means the break character, and its associated paragraph format packet, if any, must be removed and the paragraph format packet contained in the selection must be moved to a location immediately after the previous break character. This is the code which handles paragraph format packets in selections to be gobbled: . . bos gap pktbytes dup fmtchrs to ( Any format packets in selection ? ) if workpkt @ 0= if bos findchar ( Save the current paragraph format ) workpkt makepkt ( packet state in the workpkt . ) then ( Does the break being removed have an associated format packet ? ) ( Remember, the packet associated with the break being removed will ) ( be located after the previous break in the text. ) bos prevbrk dup fpkt? 0= if brk+ pktsize makespace 1- ( If not, make room after the ) ( previous break for the insertion ) ( of a format packet.) then ( Place a copy the packet contained in the selection after the ) ( previous break in the text. ) bos swap copypkt then . . Note that if the scratch workpkt contains no data, the state of the paragraph which the break character follows is calculated and stored in the workpkt . _______________________________________________________________________________ Finishing Up the Gobble Once any format packets have been handled, gobble is almost finished. findcalc and linkcalc are used to find any calc packets in the selection and to append them to a linked list (see the calc discussion). killivls is used to mark all intervals which correspond to the selection for updating. partknown is used to mark the second text partition, all text after the selection, as partially valid. movetext is used to append the selection to the end of the current undo buffer contents. The first character after the selection, the character at the beot location is moved to the front of the gap and is made the bos character--it becomes the next character under the narrow cursor (gobble is marching forward in the text). aftererase is used to redraw the display. narrowcursor is used to keep the cursor narrow and ungobble is set as the undo operation for gobble . _______________________________________________________________________________ Undo-ing a Gobble ungobble is the undo operation for gobble . ungobble takes text out of the undo buffer and places format packets back in the text. If the workpkt contains paragraph format information, and the previous break in the text contains a format packet, the format packet in the workpkt is copied over the format packet at the previous break. If the previous break does not contain a format packet, no action is taken. Next, findcalc and unlinkcalc are used to unlink all of the calc packets which were linked by Erase (or regobble , see below). killivls is used to mark all intervals which correspond to the selection for updating. partknown is used to mark the second text partition, all text after the selection, as partially valid. Now the contents of the undo buffer must be moved back into the text at the bos position. This is a three step process. First, the current bos character must be moved to the other side of the gap. Second, the first character in the undo buffer must be moved from the undo buffer to a location right before the front of the gap. Finally, the rest of the characters in the undo buffer must be moved to the far side of the gap. After these movements occur, the character before the gap is made the bos character and the first character after the gap is made the beot character. The remaining operations of ungobble involve using clearundo to clear the undo buffer, using aftererase to redraw the display, using narrowcursor to set the cursor to a narrow state, and setting regobble as the undo operation. _______________________________________________________________________________ Undo-ing an ungobble regobble is the undo operation for ungobble . If the UNDO key is toggled after an ERASE operation, ungobble and regobble will fight each other. ungobble will always place everything in the undo buffer back into place in the text and regobble will always place the entire selection into the undo buffer. _______________________________________________________________________________ Removing a Selection _______________________________________________________________________________ If the cursor is wide or extended when Erase is used, all text within the selection will be removed. The cursor moves backwards in the text when Erase is used in this manner. The two words used to implement this type of erasing are and removeselection . See the definitions of these words for further information. _______________________________________________________________________________ Copying Text _______________________________________________________________________________ The COPY command is very similar to the part of Insert which actually places a small section of text into the larger text area. The words used to implement the COPY command are Copy , insertcopy , , and Uncopy . Copy makes a copy of the current selection and tries to insert the copy into the text immediately after the end of the selection. Copy first checks the beginning and end of the selection to handle selections which may start or end on calc packets or which lie at the beginning or end of a LEAP range. After the selection endpoints have been adjusted, Copy checks to see if a valid selection is still left and exits if not. selected is used to redisplay the adjusted selection. Copy also checks to make sure there is enough room for the selection and exits if not. At this point, the procedure used to copy the selection depends upon whether the selection to be copied lies within a locked document. If the selection is not locked, insertcopy is used to place a copy of the selection in the gap, immediately after the skip information. insertcopy is then used to insert the copy into the text, immediately before the gap. If the selection is fully or partially locked, the copy must be placed immediately after the last document in the locked region. Copy checks to see where the last locked document ends and checks to see if there is any unlocked text following the last document. Once the destination for the copied text is determined, insertcopy is used to place a copy of the selection in the gap. killivls is used to mark all intervals in the insertion area as altered. insertblock is used to actually insert the copy into the text. _______________________________________________________________________________ Insert, Erase, and Copy Routines _______________________________________________________________________________ _______________________________________________________________________________ Inserting: continueinsert? ( - c -1 ) If key is available. ( - 0 ) If key is not available. Keys a key from the keyboard queue. If the key is not a special key, a true flag and the key information are returned on the stack. enoughtext ( n1 - f | If result is true. ) ( n1 - n2 f | If result is false. ) enoughtext checks the amount of memory available for text insertion (i.e. the amount of room in the gap) to see if there is enough room for 'n1' bytes to be inserted into the text. If there is enough room, a true (non-zero) flag is returned. If there is not enough room, the number of bytes which are available, 'n2', and a false (0) flag are returned. enoughtext uses this basic equation to determine the amount of available gap memory: available gap memory = bou gap - 4 - Insert ( c - ) Inserts characters into the text until there are no characters left to insert. insertblock ( a n - ) Inserts the string at address 'a' of length 'n' into the text. The text is inserted starting at the gap location. Before inserting the text, insertblock checks for a locked text or an empty text. If the text is locked, an error message will be issued and no characters will be inserted. If the text is empty, the editor will be initialized before the insertion. If the string is inserted, the text will be marked as changed. resetselection? ( - f ) Returns the flag held in the forceop integer and then places a 0 in forceop. If the flag is true, typing should force movement of the op . _______________________________________________________________________________ Erasing: ( - f ) Lower level word called by removeselection . Returns a true flag if there is no valid text to erase or a false flag if the erase process should continue. first checks to see if there is any text in the selection. If there is text, but no text in the selection, then the cursor must be at the beginning of text. The cursor is made narrow and placed just after the bot , the text is redisplayed, and is exited. If the text and selection are both empty, the cursor is made wide, placed at the beginning of the text, the text is redisplayed, and is exited. If the text or selection are not empty, links all calc packets in the selection and checks to see if the selection contains any format packets. If there are no format packets in the selection, killivls is used to mark the intervals corresponding to the bos through gap region for updating and partknown is used to mark all of the text past the beot as partially known and is exited. If there is a format packet in the selection, exchanges the last format packet in the selection with the packet at the first break before the selection. The format packet at the first break before the selection is saved in the workpkt scratch area. All intervals corresponding to the bos prevbrk through gap region are marked for updating and all text past the beot is marked as partially known. aftererase ( - ) Checks to see if the line which contains the first break before the bos is visible in the window. If it is, aftererase selectively updates the window table entries for the lines between and including the previous break line and the gapline . Later when redisplay is used, only these selectively updated lines will be redrawn. Otherwise, if the previous break line is not in the window, gapline is set to zero to cause redisplay to completely recalculate and redraw the window contents. Erase ( - ) Erase is the word executed when the ERASE key is pressed. Erase puts the %erase value in the curop system integer to identify itself as the current operation. trimselection is used to ensure a valid, erasable selection exists and lockedtext? is used to make sure the selection is not part of a locked portion of text. Next, Erase checks to see if the undo buffer has already been initialized for an erase operation. If the value in curop (%erase) is equal to the value in lastop then the last operation was an erasure and the undo buffer does not require initialization. Otherwise, clearundo is used to clear the undo buffer, savepos is used to save the current cursor state, and the work packet, workpkt , is filled with zeroes. After Erase has checked and handled any undo initialization required, the text is marked as dirty. Now Erase must determine which type of erase operation should be performed. If the cursor is narrow, gobble is used to erase forward in the text. If the cursor is not narrow, removeselection is used to remove whatever characters are in the current selection. After the correct text has been erased, the locations of the op and pop are checked. If either of them point into the gap area, they are changed to point to the current bos location. gobble ( - ) gobble is used by Erase when the cursor is narrow. If there is no text in the selection when gobble is called, the cursor must be at the end of text. The bos is set to eos prevchar , the text is redisplayed, the cursor is set to wide, and gobble is exited. If there is valid text, gobble next checks for format packets in the selection. If there are format packets, and the workpkt is empty, the state at the bos is used to create a format packet in the workpkt . Then, the break prior to the selection start is checked for a format packet. If the prior break has no format packet, a space is created for the insertion of a format packet. Then, the format packet in the selection is copied over the format packet or format packet space at the previous break location. gobble then uses killivls to mark all intervals corresponding to the text range between bos and gap as invalid, uses partknown to mark all intervals corresponding to all text after beot as partially known, moves the selection to the undo buffer, sets up the next character in the text as the new selection, uses aftererase to redraw the display, leaves the cursor narrow, and sets ungobble as the undo operation. movetext ( a1 a2 n - ) In general, movetext is used to remove the text region which starts at address 'a1' and is 'n' bytes in length from the text and insert it into the text starting at address 'a2'. movetext is primarily used for moving selections, which start at the bos location, to and from the undo buffer, which starts at the bou location. If text is not being moved between the bos and bou locations, movetext will first use enoughtext to make sure their is enough room for the move operation. If the destination address is in the second text partition, movetext will move enough text to create a hole which is 'n' bytes in size, just before the destination address and then will update all pointers affected by the text movement. If the destination is in the first text partition, room for the new text is created by moving the gap pointer position ahead by 'n' bytes. Now that space has been created for the text, move is used to move the text into place. Next, movetext takes care of closing up the hole created in the text when the source text was removed. If the source text was located in the second text partition move is used to close up the hole and all pointers affected are updated. If the source text was in the first text partition the hole is closed up by simply reducing the gap pointer by 'n' bytes. preset is used to reset the gap skip markers. regobble ( - ) regobble is the undo operation for ungobble. removeselection ( - ) First uses to check the selection, link the calcs in the selection, and to handle any format packets in the selection. If returns the false flag that indicates that the erase process should continue, removeselection will move the selection to the undo buffer, set bos and cpos equal to gap prevchar to reset the selection start, use aftererase to redraw the display, set the cursor to a widecursor, and make restoreselection the undo operation. restoreselection ( - ) This is the undo operation for removeselection . restoreselection moves the text in the undo buffer back into the text starting at the gap location and increments the gap and bou pointers by ubufsize bytes. If saved a packet in workpkt , restoreselection will place it back in the text after the break which immediately precedes the selection start. clearundo is used to clear the undo buffer, the workpkt area is filled with zeros, all calc packets in the selection are unlinked, all intervals between the break before the selection start and the selection end are marked for updating, the text after the beot is marked as partially known, the selection moved back into the text is reselected, the text is redisplayed, the cursor is made extended, and removeselection is set as the undo operation. trimselection ( - f ) If the selection begins on the document character that lies at either the start or end of the current local leap range, trimselection will "trim" the selection by bumping the start and/or end of selection forward/backward by one character since the document characters which start and end the local leap range are not allowed to change. Used by Erase . Returns a true flag if there is still a valid selection after trimming and a false flag if the selection contains no valid characters. If the only two characters in the selection region are the two document characters which mark the beginning and end of the current leap range, trimselection will return a false flag to indicate that there are no valid characters in the selection. ungobble ( - ) ungobble is the undo operation for gobble . ungobble first looks in workpkt to see if gobble removed a format packet. If workpkt holds a non-zero value, there is a format packet which needs to be put back into the text. If the first break before the bos has a format packet, the packet saved in workpkt will be copied over the current packet. If the prior break has no format packet, no action is taken. Next, unlinkcalc is used to unlink all calc packets in the selection to be placed back in the text (which currently resides in the undo buffer). _______________________________________________________________________________ Copying: ( a1 a2 a3 - a1' a2' ) Tries to copy the text bytes in the range from address 'a1' to address 'a3' to memory starting at address 'a2'. The copy process will stop if either (1) a document separator character is encountered, (2) a calc packet is encountered, (3) the end address 'a3' is reached. The current locations of the source address 'a1'' and destination address 'a2'' are returned when terminates. Copy ( - ) Copy makes a copy of the current selection and tries to insert the copy into the text immediately after the end of the selection. Copy first checks the beginning and end of the selection to handle selections which may start or end on calc packets or which lie at the beginning or end of a LEAP range. After the selection endpoints have been adjusted, Copy checks to see if a valid selection is still left and exits if not. selected is used to redisplay the adjusted selection. Copy also checks to make sure there is enough room for the selection and exits if not. At this point, the procedure used to copy the selection depends upon whether the selection to be copied lies within a locked document. If the selection is not locked, insertcopy is used to place a copy of the selection in the gap, immediately after the skip information. insertcopy is then used to insert the copy into the text, immediately before the gap. If the copy contains any paragraph formatting information, the format at the start of the copy must be preserved. If the break just before the copy contains a paragraph format packet, its contents are changed to match the formatting of the original. If the previous break has no format packet, a packet is inserted. If the selection is fully or partially locked, the copy must be placed immediately after the last document in the locked region. Copy checks to see where the last locked document ends and checks to see if there is any unlocked text following the last document. If there is no unlocked text after the locked range, Copy issues and error and exits. If there is an unlocked area after the locked range, Copy proceeds with the copy process. insertcopy is used to place a copy of the selection in the gap. killivls is used to mark all intervals in the insertion area as altered. insertblock is used to insert the copy into the text, immediately before the gap, which has been repositioned to right after the locked text region. Now, Copy takes care of preserving formats. A format packet which reflects the original format of the text following the locked text region is created and either copied over a format packet that is before the copy or is inserted after the first break before the copy. Likewise, a format packet which reflects the format at the beginning of the original selection is either copied over the first format packet after the copy or is inserted after the first break after the copy. is then used to swap the two packets so that both the copy and the text after the copy have the correct format. After the copy has been inserted into the text, the text and cursor are redisplayed and Uncopy is set as the undo operation. insertcopy ( a1 a2 n - a2' ) Copies the 'n' bytes located starting at address 'a1' to address 'a2'. Uses the lower level word repeatedly to actually move the data. If returns without having moved all of the data, then either a document separator or calc token was encountered. If a document separator character was encountered, getdpkt is used to read the contents of the packet into the #ctrl array. The markbl value is stored in the #lock field to specifically mark the document as unlocked. makedpkt is then used to insert the modified document format packet into the copy and is called again to continue moving the data after the document separator. If a calc token was encountered, copypocket is used to copy the calc packet and is called to continue moving the data after the calc packet. insertcopy will continue calling until all data has been moved. The address of the end of the copy is returned. Uncopy ( - ) Uncopy uses to remove the copy, fixes the gap pointer, marks the text as dirty, makes Copy the undo operation, and redraw the display. _______________________________________________________________________________ 7. Character Style Commands _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 6/10/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The character style commands available on the Cat are: Bold, Underline, and Caps. All three commands affect the characters in the current selection. The Bold and Underline commands affect the style/attribute byte which may or may not be associated with any character in the text (see "What's in the Text"). The Caps command affects only the ASCII value for the character. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 2 Preparing to Change the Character Style 3 To Style or Not to Style 5 Changing the Character Style 6 Undoing a Character Style Command 7 Routines Summary _______________________________________________________________________________ Preparing to Change the Character Style Before any actions which affect the text are taken, the character style commands (Bold , Under , Caps) use extend to extend the selection and lockedsel to check to see if the selection lies in a locked document. If the selection lies in a locked document, the operation will be aborted. After extending the selection and checking for locked text, the character style commands check to see if undo preparation actions need to be taken. If any of the tests below are true, the character style commands will not initialize the undo buffer: 1) Is Uncformat the current undo operation ? If Uncformat is the current undo operation it means the user is trying to undo the affect of a previous character style command execution. The original selection affected by the character style command is already in the undo buffer. Undo buffer initialization is not required. 2) Does the undo buffer contain text or data ? If the undo buffer contains text or data, if ubufsize returns a undo buffer length which is greater than zero, the undo buffer should not be initialized. 3) Is the USE FRONT key currently up ? If the USE FRONT key is up, then the [UNDO] key, not the [USE FRONT] [character style command] key combination, was used to execute this character style command. The original selection affected by the character style command is already in the undo buffer. Undo buffer initialization is not required. If undo preparations are required, clearundo will be used to clear out the undo buffer, selsize needtext will be used to check for memory availability, and cformat1 will be used to copy the current, "un-styled" selection to the undo buffer. _______________________________________________________________________________ To Style or Not to Style All of the character style commands are toggle commands. Each time a character style command is executed, it must either style, or "un-style" the current selection. The action taken depends upon the answers to the following questions: 1) Should the selection be styled ? Each of the character format commands analyzes the contents of the selection to determine if the selection should be styled or un-styled. The bold and underline commands check the following cases: (1) Can any of the characters in the selection receive an attribute ? (2) Are there any characters in the selection which do not already have the chosen character style ? If both of these conditions are true (if there are attributable characters which do not yet have the desired attribute), all characters in the selection should be given the chosen character style. If there are no characters in the selection which can receive a character style, or if all characters in the selection already have the chosen character style will be removed from all characters in the selection. The caps command performs one test: (1) Are there any lowercase characters in the selection ? If there are, the entire selection should be capitalized. 2) Will the selection be styled ? If the current operation and the last operation are not both character format commands and a learning operation is occurring, the selection will always be styled, regardless of whether or not the selection should be styled. To understand why, imagine the following scenario: During the recording of a learn sequence you press [USE FRONT][BOLD]. This means that in the future, when the learn sequence is played back, you want the selection to be bold (no matter what its current style may be). The only way to cause a character format command to be un-styled in a learn sequence is to press the command key twice when you are recording the learn sequence. That action will force the current operation and the last operation to be character format operations. _______________________________________________________________________________ Changing the Character Style Adding a character style attribute to each character in the selection is a four step process: 1) Determine how much extra memory will be required for the new attribute bytes. extramods looks through the selection and returns a count of how many new attribute bytes will be required. 2) Check memory availability. There must be enough room in the gap for both the characters in the current selection and for all of the new attribute data. 3) Move the selection into the gap area. 4) Move the selection, with new attributes installed, back into the text. The phrase: gap extrasize + 5 + bos &uln movenotwith will move the selection characters, one-by-one, back into the text and will add the underline attribute to each character which requires it. Removing a character style attribute from each character in the selection is a three step process: 1) Check memory availability. There must be enough room in the gap for a copy of the selection. 2) Move the selection into the gap area. 3) Move the selection, with the attribute removed, back into the text. The phrase gap 5 + bos selsize $uln movenotwith will move the selection characters, one-by-one, back into the text and will remove the underline attribute from each character which has it. If the only attribute the character has is the underline attribute, movenotwith will remove the entire attribute byte. The words uppercase and lowercase are used by Caps to capitalize and un-capitalize characters in a selection. _______________________________________________________________________________ Undoing a Character Style Command All of the character style commands set Uncformat as their undo operation. Uncformat uses cformat3 to swap the current selection with the contents of the undo buffer, to set the new bou and bos values, to mark the intervals which correspond to the selection are for updating, and to redraw the display with the help of cformat2 . Uncformat sets itself as the undo operation. _______________________________________________________________________________ Routines Summary: _______________________________________________________________________________ _______________________________________________________________________________ Bold and Underline Command Routines: attribregion ( a n1 n2 - f ) Returns a true flag if any attributable character within the region of text of length 'n1' which starts at address 'a' does not yet have the attribute 'n2'. Bold ( - ) Checks the current selection. The bold attribute will be removed from all characters in the selection if: - None of the characters in the selection can receive an attribute, OR - If all of the characters in the selection already have the bold attribute, AND - The current operation is the same as the last operation, OR - A learn activity is not occurring, The bold attribute will be added to all characters in the selection if: - Any characters in the selection can receive an attribute, AND - If there are characters in the selection which are not bold, OR - The current operation is not the same as the last operation, AND - A learn activity is occurring. Copies the current selection to the gap. Then copies the selection back to the text and inserts the bold character attribute at the same time. If the selection lies within a locked area of text or if there is not enough memory to create a copy of the selection, the operation will be aborted. If the selection is not already extended, it will be extended before the operation continues. Under ( - ) Checks the current selection. The underline attribute will be removed from all characters in the selection if: - None of the characters in the selection can receive an attribute, OR - If all of the characters in the selection already have the underline attribute, AND - The current operation is the same as the last operation, OR - A learn activity is not occurring, The underline attribute will be added to all characters in the selection if: - Any characters in the selection can receive an attribute, AND - If there are characters in the selection which are not underlined, OR - The current operation is not the same as the last operation, AND - A learn activity is occurring. Copies the current selection to the gap. Then copies the selection back to the text and inserts the underline character attribute at the same time. If the selection lies within a locked area of text or if there is not enough memory to create a copy of the selection, the operation will be aborted. If the selection is not already extended, it will be extended before the operation continues. _______________________________________________________________________________ Caps Command Routines: capregion ( a n - f ) Returns a true flag if a lowercase character is found in the region of text of length 'n' which starts at address 'a' in the text. lowercase ( a n - ) Changes the 'n' characters located in memory starting at address 'a' to lowercase. Upper ( - ) Checks the current selection. All characters in the selection will be capitalized if: - The selection contains one or more lowercase letters, OR - The current operation is not the same as the last operation, AND - A learn activity is occurring. All characters in the selection will be changed to lowercase if: - The selection contains only capital letters, OR - The current operation is the same as the last operation, AND - A learn activity is not occurring, The routines uppercase and lowercase are used to capitalize or "un-capitalize" the selection. uppercase ( a n - ) Capitalizes the 'n' characters located in memory starting at address 'a'. _______________________________________________________________________________ Words Which Alter the Character Data: extramods ( a n1 - n2 ) Examines the 'n1' bytes of text starting at address 'a' and returns a count 'n2' of how many characters within the range can be modified (can be underlined or bold-faced). movewith ( a1 a2 n1 n2 - a3 ) Moves the 'n1' bytes of text starting at address 'a1' to memory starting at address 'a2' and inserts the desired modifier information 'n2' after each character which can accept a modifier. Returns the address of the end of the newly modified selection. movenotwith ( a1 a2 n1 n2 - a3 ) Moves the 'n1' bytes of text starting at address 'a1' to memory starting at address 'a2'. If a character has the modifier 'n2', the modifier is removed. If 'n2' was the only modifier appended to a character, the entire modifier byte is removed. Returns the address of the end of the newly modified selection. cformat1 ( - ) ('character-format-one) Prepares a selection for character modification. Expands the undo buffer (by repositioning the bou pointer) so that it is just large enough to hold the current selection. Moves the selection to the undo buffer. cformat2 ( a - ) ('character-format-two') Refreshes the selection after the characters in the selection have been modified. Updates the gap system integer with the address 'a' which lies just after the last modified character. Recalculates the control/format information for those lines in the window record which were affected by the character modification. Uses refresh to redraw the changed lines. Sets the cursor to a wide cursor. cformat3 ( - ) ('character-format-three') Places the modified selection text found in the undo buffer back in its previous spot in the text and uses cformat2 to update the display area which contains the new text. Uncformat ( - ) Calls cformat3 and makes itself the undo operation. _______________________________________________________________________________ 8. Paragraph Format Commands _______________________________________________________________________________ _______________________________________________________________________________ Modification History: 1st Draft - Routine Desc. LC 6/15/87 2nd Draft - General Desc. LC 6/17/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: There are twelve paragraph formatting operations: Left Margin, Right Margin, Indent , Paragraph Style, Line Spacing, Set/Clear Tabs, and the shifted versions of the previous six. The shifted versions of the paragraph formatting commands restore the default setting for the corresponding format operation. Each operation changes either all paragraph format packets in the current selection or, if there is no selection, just the paragraph format packet which controls the paragraph which contains the cursor. The text is redisplayed to show the affects of the new paragraph format. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 General Discussion 3 Paragraphs and Paragraph Format Packets 4 The Paragraph Formatting Routines 5 Four Steps to a New Paragraph Format 6 Four Steps to a Default Paragraph Format 6 Obtaining New Format Settings 6 Obtaining a New Line Spacing Setting 7 Obtaining a New Paragraph Style Setting 8 Obtaining a New Left/Right Margin or Indent Setting 8 Obtaining New Tab Settings 9 Example of a Command which Uses the Vertical Bar 10 Paragraph Format Commands Routines Summary 18 Paragraph Formatting Integers 18 Scan Codes for the Paragraph Format Keys 19 Default Paragraph Format Settings _______________________________________________________________________________ General Discussion _______________________________________________________________________________ In order to change a particular aspect of a paragraph's format, the corresponding field in the paragraph format packet which controls the paragraph must be changed and the display updated. The table below shows which paragraph format information fields in a control/format array are affected by each paragraph formatting command: Paragraph Format Operation Field in Paragraph Format Packet Line Spacing %lsp Left Margin %left Right Margin %wide and %left Indent %indent Paragraph Style %just Set/Clear Tabs %tabs The 6 paragraph formatting commands are "linked" together so that, as long as the USE FRONT key is held down, several paragraph format commands may be used in succession without forcing the user to re-highlight the selection after each individual operation. When several commands are used in this manner, a single press of the UNDO key will undo the effects of all of the commands. _______________________________________________________________________________ Paragraphs and Paragraph Format Packets A paragraph is defined as a group of characters which starts with the first character after a break character and ends with the following break character. The paragraph format packet which defines the format for a paragraph of text is located before the first character in the paragraph, immediately after the break character located at the end of the previous paragraph. Thus, when the format of a paragraph is changed, the previous paragraph format packet must be found and updated. If a paragraph does not have its own paragraph format packet, it is controlled by the first previous paragraph format in the text. _______________________________________________________________________________ The Paragraph Formatting Routines There are two versions of each of the six main paragraph formatting functions. The main version sets a format to the user's specifications. The default version restores the default settings for the particular format operation. Main Version Default Version Indent Defindent Justify Defjustify Left Defleft Right Defright Spacing Defspacing Tabs Deftabs A table which lists the default paragraph format settings for each country is included at the end of this section. _______________________________________________________________________________ Four Steps to a New Paragraph Format The definition for the main version of the paragraph style command provides a general outline of the 4 steps required to change a paragraph format. Step 1: Prepare selection. All of the paragraph formatting operations use preform for selection preparation purposes. preform checks for a locked selection and loads the current control/format information into the #ctrl array. Step 2: Get the new format setting. There are two methods used to determine new settings. The paragraph style and line spacing operations each allow only a small, fixed set of possible choices. Each time one of these operations is invoked it presents the user with the next available choice in its set. A new left/right margin, indent, or tab setting is chosen by the user with the aid of a graphical positioning tool. The listing below shows how the Justify command, a command with a fixed set of possible choices, performs step 2. Step 3: Save the selection information. The new format setting(s) was determined in Step 2. In Step 3, the new setting information is stored into the appropriate field in the ##ctrl array. Step 4: Update the Text and the Display. In Step 4 the formatting operations use reform to complete the formatting operation. In the example below, the token of fixjustify , which transfers the value previously stored in the %just field of the ##ctrl array to the %just field of the #ctrl array, is passed to reform . reform executes fixjustify , creates a paragraph format packet using the new paragraph format information in the #ctrl array, replaces all paragraph format packets in the selected area with the new format packet, and causes the screen to be redisplayed. The words fixspacing , fixindent , fixleft , fixright , and fixtabs perform functions similar to fixjustify for the other formatting operations. : Justify ( - ) preform ( 1. PREPARE FOR FORMATTING OPERATION. ) #just c@ 2+ ( 2. CALCULATE NEW FORMAT SETTING. ) dup 4= ( Determine what the new value for the ) if ( paragraph style should be. ) drop 1 then dup 5 = if drop 0 then ##ctrl %just + c! ( 3. SAVE NEW SETTING(S) INFO. ) ( Store the new value in the %just ) ( field of the ##ctrl array. ) ['] fixjustify reform ; ( 4. UPDATE THE TEXT AND SELECTION. ) _______________________________________________________________________________ Four Steps to a Default Paragraph Format The definition for the default version of the paragraph style command provides a general outline of the 4 steps required to change a paragraph format. The default version of Justify , Defjustify , stores the default paragraph style setting, found in an array of default paragraph format settings, in the ##ctrl and then also uses reform to finish the reformatting job. The default paragraph format settings are found in the #defaults array. : Defjustify ( - ) preform ( 1. PREPARE FOR FORMATTING OPERATION. ) #defaults %just + c@ ( 2. CALCULATE NEW FORMAT SETTING. ) ##ctrl %just + c! ( 3. SAVE NEW SETTING(S) INFO. ) ['] fixjustify reform ; ( 4. REDISPLAY THE SELECTION. ) _______________________________________________________________________________ Obtaining New Format Settings _______________________________________________________________________________ The methods each paragraph format command uses to determine the user's new desired format setting are described below. _______________________________________________________________________________ Obtaining a New Line Spacing Setting The editor can handle three different line spacing settings: Line Spacing Field Value Single-spaced 2 1 1/2-spaced 3 Double-spaced 4 Spacing calculates a new line spacing setting by (1) obtaining the current line spacing value from the %lsp field in the #ctrl array, (2) adding 1 to the value, and (3) subtracting 3 from the result if it is equal to 4. _______________________________________________________________________________ Obtaining a New Paragraph Style Setting The editor supports 4 paragraph styles: Paragraph Style Field Value Left justified 0 Right justified 1 Center justified 2 Fully justified 3 In the ruler display area, the paragraph style icons are arranged from left to right as: left, center, right, full. When multiple selections of the paragraph style command are used, the icon highlight should flow smoothly from left to right, without skipping any icons. This means the %just field must be fed this sequence of values: 0, 2, 1, 3 (see table above). So, to calculate a new paragraph style value Justify (1) obtains the current paragraph style value from the %just field of the #ctrl array, (2) adds 2 to the value, and (3) if the result is 4 (invalid) sets the result to 1 or if the result is 5 (invalid) sets the result to 0. _______________________________________________________________________________ The Vertical Formatting Bar The left/right margin, indent, and tab commands use a graphical aid -- a vertical formatting bar -- to help the user select new settings. After the user chooses one of these commands, the vertical bar is placed on the screen, in a command-specific location. At this point the command will wait in a loop, moving the vertical bar according to the user's inputs, until either an invalid key is pressed or until the USE FRONT key is released. An invalid key is any other paragraph formatting command key aside from the command key which caused the loop to be entered. So, as long as the USE FRONT key is depressed and an invalid key is not entered, the vertical bar will remain on the screen and the command word will be watching for positioning directions. _______________________________________________________________________________ Obtaining a New Left/Right Margin or Indent Setting If the left/right margin or indent commands are chosen, the word marginloop will be used for vertical bar positioning. While marginloop runs it watches for presses of either leap key. When the left or right leap key is pressed, the vertical bar is moved one ruler increment to the left or right, selected fields in the #ctrl array are updated, and the ruler is redrawn to reflect the new left/right margin or indent position. When the left margin command is used, the %left , %wide , %indent , and %iwide fields must be updated after each movement of the vertical bar. When the right margin command is used, the %wide and %iwide fields must be updated after each vertical bar movement. If the indent command is used, the %indent and %iwide fields must be updated with each movement. _______________________________________________________________________________ Obtaining New Tab Settings When the tab command is chosen, the word tabloop is used for vertical bar positioning. While tabloop runs it watches for 6 keys. The six keys, and the actions taken when they are pressed, are: Key Action Left Leap Vertical bar is moved one ruler increment to the left. Right Leap Vertical bar is moved one ruler increment to the right. Tab Either sets, changes, or removes a tab at the current Shift-tab vertical bar location. The %tabs field in the #ctrl array is updated and the ruler redrawn. Space The vertical bar is positioned at the next tab stop to the right of its current position. If there are no tabs to the right, the vertical bar will wrap around to the leftmost tab stop. If there are no tabs at all, the vertical bar will not be moved. Erase All of the tab stops are removed. _______________________________________________________________________________ Example of a Command which Uses the Vertical Bar The definition of Left shows how a paragraph format command which uses the vertical format bar for new format selection is constructed: : Left ( - ) preform ( 1. PREPARE FOR FORMATTING OPERATION. ) %setl curop to ( Set the current operation. ) ( uses curop to check for valid keys ) ( received. ) 2 #left c@ #wide c@ + 2/ #indent c@ 2/ 2+ initset ( Initialize integers used by ) ( marginloop.) marginloop ( Watch while the user chooses their ) ( settings. ) #indent c@ ##ctrl %indent + c! ( Place new format info ) #left c@ ##ctrl %left + c! ( in ##ctrl array. ) ['] fixindent reform ; ( Update the text and display. ) Refer to the routines summary for more information on words used in the above listing. _______________________________________________________________________________ Paragraph Format Commands Routines Summary _______________________________________________________________________________ _______________________________________________________________________________ Paragraph Format Commands Words: Defindent ( - ) Moves the default indent data from the #defaults table to the ##ctrl array and uses reform to reformat the entire selection according to the default indent setting. Defjustify ( - ) Moves the default paragraph style data from the #defaults table to the ##ctrl array and uses reform to reformat the entire selection according to the default paragraph style setting. Defleft ( - ) Moves the default left margin data from the #defaults table to the ##ctrl array and uses reform to reformat the entire selection according to the default left margin setting. Defright ( - ) Moves the default right margin data from the #defaults table to the ##ctrl array and uses reform to reformat the entire selection according to the default right margin setting. Defspacing ( - ) Moves the default line spacing data from the #defaults table to the ##ctrl array and uses reform to reformat the entire selection according to the default line spacing setting. Deftabs ( - ) Moves the default tab setting data from the #defaults table to the ##ctrl array and uses reform to reformat the entire selection according to the default tab settings. fixindent ( - ) Transfers the contents of the %indent field in the ##ctrl field to the %indent field in the #ctrl array. Defindent and Indent pass the token of fixindent to reform which, after executing the token, reformats the selection according control/format data found in the #ctrl array. fixjustify ( - ) Transfers the contents of the %just field in the ##ctrl field to the %just field in the #ctrl array. Defjustify and Justify pass the token of fixjustify to reform which, after executing the token, reformats the selection according control/format data found in the #ctrl array. fixleft ( - ) Transfers the contents of the %left field in the ##ctrl field to the %left field in the #ctrl array. Defleft and Left pass the token of fixleft to reform which, after executing the token, reformats the selection according control/format data found in the #ctrl array. fixright ( - ) Transfers the contents of the %right field in the ##ctrl field to the %right field in the #ctrl array. Defright and Right pass the token of fixright to reform which, after executing the token, reformats the selection according control/format data found in the #ctrl array. fixspacing ( - ) Transfers the contents of the %lsp field in the ##ctrl field to the %lsp field in the #ctrl array. Defspacing and Spacing pass the token of fixspacing to reform which, after executing the token, reformats the selection according control/format data found in the #ctrl array. fixtabs ( - ) Transfers the contents of the %tabs field in the ##ctrl field to the %tabs field in the #ctrl array. Deftabs and Tabs pass the token of fixtabs to reform which, after executing the token, reformats the selection according control/format data found in the #ctrl array. Indent ( - ) Sets the current operation to %seti . Uses initset to set the iposit integer to the current indent location (#indent half spaces), the rbound integer to the current right margin location (#left+#wide half spaces), the lbound integer to 2 half spaces, and then sits in a loop, marginloop , and moves the indent line around while the user selects a new indent position. When the user has finished setting the indent position, Indent moves the new user indent data to the ##ctrl array and uses reform to reformat the entire selection according to the new indent setting. Justify ( - ) The paragraph style field in a control/format array, %just , can accept four values: 0 for left justified, 1 for right justified, 2 for center justified, and 3 for fully justified. Each time Justifiy is executed it takes the contents of the %just field in the #ctrl array, increments the value by 2, checks and handles overflows (if the value becomes 4, it is changed to 1, and if the value becomes 5 it is changed to 0), and stores the new value in the %just field of the ##ctrl field. reform is then used to reformat the entire selection according to the new paragraph style setting. Left ( - ) Sets the current operation to %setl . Uses initset to set the iposit integer to the current left margin location (#left half spaces), the rbound integer to the current right margin location (#left+#wide half spaces), the lbound integer to 2 half spaces and then sits in a loop, marginloop , and moves the left margin line around while the user selects a new left margin position. When the user has finished setting the left margin, Left moves the new user left margin data to the ##ctrl array and uses reform to reformat the entire selection according to the new left margin setting. Right ( - ) Sets the current operation to %setr . Uses initset to set the iposit integer to the current right margin location (#left+#wide half spaces), the rbound integer to &horiz half spaces, the lbound integer to either the left margin location (#left half spaces) or the indent location (#indent half spaces), whichever is greater, and then sits in a loop, marginloop , and moves the right margin line around while the user selects a new right margin position. When the user has finished setting the right margin, Right moves the new user right margin data to the ##ctrl array and uses reform to reformat the entire selection according to the new right margin setting. Spacing ( - ) The line spacing field in a control/format array, %lsp , can accept three values: 2 for single-spaced text, 3 for 1 1/2-spaced text, and 4 for double-spaced text. Each time Spacing is executed it takes the contents of the %lsp field in the #ctrl array, increments the value by 1, checks and handles overflows (if the value becomes 4, 3 is subtracted), and stores the new value in the %lsp field of the ##ctrl field. reform is then used to reformat the entire selection according to the new line spacing setting. Tabs ( - ) Sets the current operation to %sett . Uses initset to set the iposit integer to the ruler increment location which is closest to the current cursor position, the rbound integer to 0 half spaces, the lbound integer to 0 half spaces, and then sits in a loop, tabloop , and moves the vertical tab line around as the user sets up tab stops. When the user has finished setting tab stops, Tabs moves the new user tab data to the ##ctrl array and uses reform to reformat the entire selection according to the new user tab settings. _______________________________________________________________________________ Low-Level Paragraph Formatting Words: #defaults ( - a ) Pushes the address 'a' of a table of default paragraph format settings on the stack. pformat1 ( - f ) Prepares the selection for a paragraph formatting operation by emplacing paragraph format packets at the start and end of selection if necessary. A false flag means the operation was successful and a true flag means an error occurred. First, checks to see if there is enough room in the undo buffer to hold at least two paragraph format packets. If there isn't enough room, pformat1 exits immediately and returns a true flag. Next, encodes the current state (which was previously found by preform) in the workpkt scratch area. To ensure that the format of the text before and after the selection is not affected by the formatting operation, the format state before and after the selection must be found and saved. To find the previous format state, prevbrk and brk+ are used to find the address of the first break (or first paragraph format packet) before the bos position, or before the bos nextchar position if the cursor is wide. The address is saved in the prepkt system integer. To find the format state after the selection, nextbrk and brk+ are used to find the address of the first break (or paragraph format packet) after the beot prevchar position, or after the beot prevchar prevchar position if the cursor is is narrow. The address is stored in the postpkt system integer. Now pformat1 checks both prepkt and postpkt to see whether they contain addresses of breaks or format packets. The address belongs to a paragraph format packet if the first byte at the address contains the paragraph format packet identification marker, &fmt . If the preceding break is not followed by a format packet, prepkt prevchar findchar is used to get the previous formatting state information and prepkt pktsize makespace makepkt is used to encode the information and insert the resulting format packet after the break. If the succeeding break is not followed by a format packet, postpkt prevchar findchar is used to get the formatting state information for the text which follows the selection and postpkt pktsize makespace postpkt to postpkt makepkt is used to encode the formatting information and to insert the resulting format packet after the break. pformat2 ( n - ) Executes the token 'n' to modify the paragraph format data structure fields which are affected by the current formatting operation and causes the text to be redisplayed. Uses killivls to mark all intervals between prepkt and postpkt beot max for updating. Uses partknown to mark all intervals between postpkt beot max and the end of text as intervals whose interval table data is mostly correct. Next, pformat2 spins in a loop, finding paragraph format packets in the selection and updating specific fields in the format packet by executing the token provided. After the format packets have been updated, pformat2 finds the best way to redisplay the newly formatted text. If the entire selection fits in the window, refresh is used to selectively redraw the window contents. Otherwise, new-display is used to completely redraw the window contents. The text is marked as dirty and the selection is reduced to the single character which was at the beginning of the selection. preform ( - ) Performs preparation tasks before the start of a paragraph formatting operation. Uses lockedsel to see if the current selection lies within a locked region of text. If it does, the operation is aborted. Loads control/format information which corresponds to the beot address, if the cursor is wide, or the beot prevchar address if the cursor is not wide, into the #ctrl array. reform ( n - ) The main word used by the paragraph formatting operations. Uses pformat1 to prepare the selection for formatting. If the USE FRONT key is not currently pressed or if the last operation was not a paragraph formatting operation (%pfmt , %setl , %seti , %setr , or %sett), reform performs these undo preparations: checks for enough memory for the undo buffer, saves copies of all format packets in the selection in the undo buffer, and uses savepos to save the current current state of the editor. Next, reform turns the ufpressed? system integer off, uses pformat2 to update the text and redraw the display, sets unformat as the undo operation, sets %pfmt as the current operation, and uses rule to update all of the paragraph format indicators in the ruler display. Now reform waits in a loop to see what operation, if any, the user will perform next. As soon as the USE FRONT key is released, or a non-special key is pressed, reform will exit the loop and examine the key press scan code data. If the keyboard information indicates that another paragraph format operation is to be performed next, reform will leave the selection extended so that the next operation acts upon the same selection. Otherwise, reform will collapse the selection before returning. unformat ( - ) This is the undo operation for reform . Uses swappkts to exchange the format packets saved by reform in the undo buffer with the packets in the text which were changed by reform . Marks all interval entries which correspond to the changed region of text for updating and all intervals beyond postpkt beot max as partially known intervals (same as pformat2 ). Swaps the contents of the oldbos and bos , and the oldcstate and cstate , uses eos-display to redisplay the text, and uses resetcursor to fix up the cursor. _______________________________________________________________________________ Tab Routines: addtab ( n f - ) Adds a tab of type 'f', where f = -1 means decimal tab and f = 1 means normal tab, to the #ctrl tab array at position 'n'. deltab ( n - ) Deletes the tab at position 'n' in the tab array from the #ctrl tab array. getkey ( - f ) Used by tabloop and marginloop to get the keys used for user specification of new tab, margin, or indent settings (to move the left/right margin line>, indent line, and tab line around). Returns a true flag if a valid key was pressed. A valid key is any USE FRONT key combination which is (a) not a paragraph format command key combination, or (b) is equal to the paragraph format command key which caused getkey to be called. initkey ( - ) Stores a $FF in the lastkey system integer. initset ( n1 n2 n3 - ) Initializes the system integers used during the setting of tabs, left margin, right margin, or indent. Sets the value of iposit to 'n3', rbound to 'n2', and lbound to 'n3'. Stores a 0 in the first four bytes of the vtbuff . Uses repos to position the vertical tab line at the location specified by iposit and uses initkey to set the initial lastkey value to $FF. marginloop ( - ) This is the loop used to help the user specify a new left margin, right margin, or indent position. While the USE FRONT key is held down and while getkey returns a true flag (indicating that a valid margin/indent positioning key has been pressed), marginloop checks for the occurrence of two keys: the left and right leap keys. If the left leap key is pressed, repos is used to position the vertical tab line one ruler increment to the left. If the right leap key is pressed, repos is used to position the vertical tab line one ruler increment to the right. nextab ( n1 - n2 f | If the next tab is found. ) ( n - 0 | If no next tab is found. ) Looks through the %tabs field in the #ctrl array for a tab stop that is to the right of the specified ruler position 'n'. If there are no tab stops to the right of the specified position, nextab starts looking again starting from the left margin. If no tab is found, a '0' is returned. If a decimal tab is found, a '-1' is returned. If a normal tab is found, a '1' is returned. _______________________________________________________________________________ Tab Routines: repos ( n - ) Repositions the vertical tab line at position 'n' (specified in units of whole spaces) and updates the necessary fields in the #ctrl array. If left is the current operation, repos will update the contents of the #left , #wide , #indent , and #iwide fields. If Indent is the current operation repos will update the contents of the #indent and #iwide fields. If Right is the current operation repos will update the contents of the #wide and #iwide fields. tabloop ( - ) This is the loop used to help the user specify new tab settings. While the USE FRONT key is held down and while getkey returns a true flag (indicating that a valid tab positioning key has been pressed), tabloop checks for the occurrence of five keys: the left leap key, the right leap key, the tab key, the space key, and the erase key. If a left leap key is pressed, repos is used to position the vertical tab line one ruler increment to the left. If a right leap key is pressed, repos is used to position the vertical tab line one ruler increment to the right. If the tab key is pressed, tabloop will either add a tab (addtab), or delete (deltab) or change (nextab) the tab at the current vertical tab line position. If the space bar is pressed, repos will be used to position the tab line at the next tab stop location. If the erase key is pressed all tabs are removed. unvtline ( - ) Restores the bytes under the vertical screen line used for tab, left margin, right margin, and indent positioning. The saved copies of the image underneath the vertical line are stored in the vtbuff . The first 4 byte location in the vtbuff holds the byte position where the first saved byte image should be placed. vtline ( n - ) Places a full screen vertical line at byte position 'n' on the screen. Saves copies of the image bytes under the vertical line in the vtbuff memory buffer. The byte position 'n' is saved in the first byte of the vtbuff . _______________________________________________________________________________ Paragraph Formatting Integers _______________________________________________________________________________ lastkey Holds previous key processed in formatting loop. lbound Holds left bound for vertical format line. posit Holds instantaneous position of vertical line (expressed in pixels?). rbound Holds right bound for vertical format line. thiskey Holds key most recently processed in formatting loop. vbheight Holds the height of the vertical tab line expressed in pixels. vbheight is defined as: scans/image lines/screen * vtbuff Holds the address of the buffer used to hold the bits behind the vertical tab line. The size of the buffer is vbheight 6 + _______________________________________________________________________________ Scan Codes for the Paragraph Format Keys Paragraph Format Function Scan Code Character Paragraph Style $02 t Line Spacing $0B u Indent $29 - Right Margin $31 = Set/Clear Tab $32 tab Left Margin $38 \ _______________________________________________________________________________ Default Paragraph Format Settings Left Margin: 7 Right Margin: 73 Paragraph Style: Left Justified Line Spacing: Single spaced Indent: 0 Default Tab Settings: Country Tab Code Country Stops 00 USA 13,18,28,38,48,58 01 Canada 13,18,28,38,48,58 02 United Kingdom 13,22,32,42,52,58 03 Norway 13,18,28,38,48,58 04 France 12,22,32,42,52,62 05 Denmark 13,18,28,38,48,58 06 Sweden 13,18,28,38,48,58 07 Japan 13,18,28,38,48,58 08 West Germany 11,17,27,37,47,57 09 Netherlands 13,18,28,38,48,58 0A Spain 17,27,37,47,57,67 0B Italy 13,18,28,38,48,58 0C Latin America 13,18,28,38,48,58 0D South Africa 13,18,28,38,48,58 0E Switzerland 13,18,28,38,48,58 0F ASCII 13,18,28,38,48,58 _______________________________________________________________________________ 9. Document Commands _______________________________________________________________________________ _______________________________________________________________________________ Modification History: 1st Draft LC 8/23/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The Cat has two commands which operate on whole documents only: DOCUMENT LOCK and LOCAL LEAP. The words used to implement these commands are discussed here. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 The DOCUMENT LOCK Command 4 The LOCAL LEAP Command 4 Updating Document Format Packets _______________________________________________________________________________ The DOCUMENT LOCK Command _______________________________________________________________________________ The DOCUMENT LOCK command is used to lock/unlock the text within a document or range of documents. When a document is locked, no changes can be made to the document. A character-wide gray bar is displayed along both edges of a locked document. _______________________________________________________________________________ DocLock DocLock is the word used to implement the DOCUMENT LOCK command. The actions of the DOCUMENT LOCK command are based upon the current selection. Since "lock" attribute information is kept in the document format packet, only whole documents can be locked. The first action of DocLock is to expand the region defined by the current selection to the smallest region which contains both the entire selection and a whole number of documents. The documents in this expanded region are the documents which will be affected by the DOCUMENT LOCK command. If any of the documents with the new region are locked, DocLock will unlock the entire region. Likewise, if all documents in the new region are unlocked, DocLock will lock all documents in the new region. _______________________________________________________________________________ Document Format and Calc Packets are Affected by DOCUMENT LOCK The two items in a document which are affected by the lock state of the document are document format packets and calc packets. If a document is locked its #lock field will contain the value lok . If a document is unlocked, its #lock field will contain the value markbl . lok and markbl are two Cat display characters. lok corresponds to a gray box character which is one character width wide and one character height high. markbl is a white space character which is one character width wide and one character height high. When text is displayed, the value in the #lock field is always placed in the first and last character position of each line of text. This explains why a locked document always has a one character wide gray bar along the left and right edges of the screen display area. If a calc packet is locked, it is marked by a &lockedcalc token in the text. If a calc packet is unlocked, it is marked by a &calc token in the text. _______________________________________________________________________________ Undo-ing the DOCUMENT LOCK Command Before changing a document's lock state, DocLock saves the document's current lock state, as found in the #lock field of the document's document format packet, in the undo buffer. When undoclock is used to undo a DocLock operation, it uses the saved state to determine whether a particular document, and any calc packets in the document, should be locked or unlocked. _______________________________________________________________________________ Words that Check the Lock State The words lockedtext? , lockedsel , and lockedrange? are used to check the lock state at a particular location in the text. lockedsel checks for a locked selection range, lockedtext? checks for a locked character, and lockedrange? checks for a locked range of characters. _______________________________________________________________________________ The LOCAL LEAP Command _______________________________________________________________________________ The LOCAL LEAP command is used to define the range over which the LEAP command can operate. The LOCAL LEAP command is a toggle command. local/global is the word used to implement LOCAL LEAP. If the current LEAP range extends over the entire text range when LOCAL LEAP is used, local/global will shrink the LEAP range to the smallest whole document range which entirely contains the current selection. If the current LEAP range does not include the entire text range when LOCAL LEAP is used, local/global will expand the LEAP range to include the entire text. bor ('beginning-of-range') and eor ('end-of-range') are the two system integers used to hold the start and end addresses of the current LEAP range. Whenever local/global expands or reduces the LEAP range, it also adjusts the the op and pop pointers to fit within the newly defined LEAP range. The undo operation for local/global is undolocal/global . _______________________________________________________________________________ Updating Document Format Packets _______________________________________________________________________________ The word redoc can be used to integrate document format information specified by the SETUP command into all document format packets contained within the current selection range. _______________________________________________________________________________ Locked Document Routines _______________________________________________________________________________ Doclock ( - ) Locks or unlocks all documents contained within or which contain the current selection. Currently, only whole document(s) may be locked/unlocked. DocLock's first action is to define the range of text to be locked/unlocked. The first document separator located before the start of the selection will be used as the start of the locked/unlocked range and the first document separator located after the end of the selection will be used as the end of the locked/unlocked range. Next, DocLock must determine whether the range of text should be locked or unlocked. If any of the documents within the range are currently locked, DocLock will unlock all documents in the range. Otherwise, all documents in the range will be locked. The exception to this rule occurs when DocLock is used for the first time during a pre-recorded learn sequence. If this is the case, the documents in the range will always be forced to the locked state. Now DocLock may begin the locking/unlocking process. DocLock steps through the range looking for all occurrences of calc packets and document format packets. If a calc packet is to be locked, a &lockedcalc token will be placed in the packet. Otherwise, a &calc token will be placed in the packet. If a document format packet is encountered, getdpkt is used to read its contents into the #ctrl array and the current value of its #lock field is stored in the undo buffer. Then, the #lock value is changed to either lok (if the document is being locked) or markbl (if the document is being unlocked), and makedpkt is used to write out the changed document format packet. After all calc and document format packets have been properly updated, the undo operation is set to undoclock , all intervals which correspond to the affected range are marked as changed, the display is redrawn to show or hide the lock bars, and the text is marked as dirty. lockedrange? ( a1 a2 - f ) Checks to see if any characters in the range specified by the addresses 'a1' and 'a2' lie in a locked region of text. If they do, a true flag is returned. lockedsel ( - f ) Checks to see if the selection lies within, or contains, a locked range of text. If it does, an abort message is issued. lockedtext? ( a - f ) Uses findchar to read the control/format information for the character at address 'a' into the #ctrl array. Then checks the contents of the %lock field to see if the character lies in a region of locked text. If the text is locked, a true flag is returned. undoclock ( - ) This is the undo operation for DocLock . undoclock's first action is to define the range of text to be locked/unlocked. The first document separator located before the start of the selection will be used as the start of the locked/unlocked range and the first document separator located after the end of the selection will be used as the end of the locked/unlocked range. Now undoclock may begin the locking/unlocking process. undoclock steps through the range looking for all occurrences of calc packets and document format packets. If a document format packet is encountered, the previous value of the packet's #lock field is obtained from the undo buffer and is used to set a local lock/unlock flag. getdpkt is used to read the packet contents into the #ctrl array and the current value of its #lock field is stored in the undo buffer. Then, the #lock value is changed to either lok (if the document is being locked) or markbl (if the document is being unlocked), and makedpkt is used to write out the changed document format packet. If a calc packet is to be locked, a &lockedcalc token will be placed in the packet. Otherwise, a &calc token will be placed in the packet. After all calc and document format packets have been properly updated, the undo operation is set to undoclock , all intervals which correspond to the affected range are marked as changed, the display is redrawn to show or hide the lock bars, and the text is marked as dirty. _______________________________________________________________________________ Local Leap Routines _______________________________________________________________________________ adjustleaprange ( - ) Shrinks the allowable leap range according to the current selection. The start of the leap range is set to the first document break before the start of the selection and the end of the leap range is set to the first document break after the end of the selection. checklocallight ( - ) Checks the current leap range and sets the LOCAL indicator light accordingly. If leaping over the entire text range is currently possible ( bor bot = eor eot = and ), the LOCAL indicator is turned off. Otherwise, the "LOCAL" string is displayed in the indicator. local/global ( - ) Toggles between local and global leaping. The current bor and eor values are saved in the undo buffer. The undo operation is set to undolocal/global . If the leap range is currently fully expanded, or if this is the first use of local/global in a pre-recorded learn sequence, then adjustleaprange is used to shrink the leap area to the smallest allowable leap range which contains the current selection. Otherwise, the leap range is fully expanded by setting bor bot = and eor eot = . The op and pop values are adjusted to fall within the new local leap area, checklocallight is used to handle the LOCAL indicator light, and the text is marked as dirty. undolocal/global ( - ) Undo operation for local/global . The current bor and eor values are swapped with the bor and eor values saved in the undo buffer, the op and pop values are adjusted to fit within the new leap range, checklocallight is used to handle the LOCAL indicator light, and the text is marked as dirty. _______________________________________________________________________________ Document Format Packet Update Routines _______________________________________________________________________________ findds ( a1 a2 - a3-or-0 ) Searches through the range of text which starts at address 'a1' and ends at address 'a2' for the first occurrence of a document separator character. If a document separator character is found in the specified text range, its address, 'a3', is returned. Otherwise, a 0 is returned. Used by redoc . redoc ( - ) Updates all document format packets in the current selection. For each document format packet encountered, getdpkt is used to place its current document state information into the #ctrl array and then getdocpkt is used to update the document state information with values from the set-up version of the document format information. makedpkt is used to write the updated document information in the #ctrl array back over the original format packet. After all document format packets have been updated, the affected intervals are marked as changed and the text is redisplayed. _______________________________________________________________________________ 10. Leap _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 8/30/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The LEAP mechanism, which makes use of two LEAP keys (LEAP forward and LEAP backward) is used to perform six basic operations: (1) cursor placement (LEAPing around), (2) display scrolling (using LEAP-SHIFT), (3) creeping (tapping a LEAP key to bump the cursor forwards or backwards), (4) SPELL CHECK LEAP (press UNDO while holding a LEAP key down), (5) text selection (press both LEAP keys to select), and (6) text movement (dragging, discussed in the next chapter). Once started, three of these operations are repeatable: (1) LEAP again, (2) scroll again, and (3) SPELL CHECK LEAP again. The routines used to implement the LEAP mechanism are primarily text search routines. In order to place the cursor at the string-specified location in the text, the LEAP routines must be able to find the strings in the text which match the user's leap string. An optimized text string search algorithm called the Boyer-Moore algorithm is used by the LEAP search routines to achieve maximum LEAP performance. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 The Boyer-Moore Fast String Search Algorithm 3 The Pattern Table 5 The Character Equivalence Table (maptable) 6 A Step-by-Step Explanation of the Algorithm 7 Handling Accent Characters 8 The LEAP Mechanism 8 Initializing a LEAP Operation 9 Leaping Around the Text 9 Low Level Search Routines 10 Searching for Single Page Breaks 10 Scroll Again 10 SPELL CHECK LEAP Again 11 Search Again 11 Finishing a LEAP Operation 11 SHIFT - LEAP Scrolling 11 Creeping 12 Other LEAP Terminations 12 Highlighting a Selection 12 Dragging a Selection 12 Successful SPELL CHECK LEAP 12 Successful LEAP _______________________________________________________________________________ The Boyer-Moore Fast String Search Algorithm _______________________________________________________________________________ The Boyer-Moore algorithm provides an optimal method for performing fast string searches in a body of text. Basically, the Boyer-Moore algorithm eliminates the need to look at each successive character in order to find an occurrence of a particular character sequence. When presented with a character from the text, the algorithm determines the distance to the next significant character position. The characters between the current character and the next significant character need not be analyzed and are skipped. To make decisions about significant character positions, the algorithm depends upon information from a pre-constructed "pattern table". _______________________________________________________________________________ The Pattern Table The pattern table is a 256 byte table. There is one byte-length entry in the table for each of the 256 possible ASCII characters. The pattern table is constructed prior to the start of a search with the use of the word buildtable . The data required to build the pattern table are (1) the address of the character string, or pattern, being searched for, (2) the length of the pattern and (3) the direction of the search. The address of the pattern table is kept in the ptable integer. The address of the pattern string is kept in the pattern integer and the length of the pattern is kept in the patlen integer. If a forward search is being used, the direction integer will hold a true flag. The data to be used for this discussion are shown below. We are searching for the 3 character string "hij". The text we are searching through contains the first 16 characters of the alphabet: Pattern: hij Pattern Length: 3 Text Being Searched: abcdefghijklmnop Search Direction: Forward (start of text to end of text) Given this information, buildtable will construct the pattern table shown below. Note that the entries for all characters which are not included in the pattern contain either a 3, the length of the pattern, or a 1. In a forward search, if a character is the first character in the pattern, its entry will hold the value 'length-1'. In this case, 'h' is the first character in the pattern so its entry contains '3-1=2'. The second character in the pattern, 'i', receives 'length-2' or '3-2=1'. The last character in the pattern, which turns out to be the most important character in a forward Boyer-Moore search, receives the value 'FF'. Pattern Table for a Forward Search for the String "hij": (Second) (First Hex Digit ->) (hex) 0 1 2 3 4 5 6 7 8 9 A B C D E F (digit) 0 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 3 3 3 3 3 3 3 2 1 FF 3 3 3 3 3 5 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 6 3 3 3 3 3 3 3 3 2 1 FF 3 3 3 3 3 7 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 8 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 9 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 A 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 B 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 C 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 D 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 E 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 F 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 _______________________________________________________________________________ The Character Equivalence Table (maptable) Note that both 'h' and 'H' received the value 2. This is because 'H' is considered to be 'equivalent' to 'h'. When buildtable creates the pattern table it refers to a table of character equivalents (shown below). The address of the character equivalence table is kept in the maptable integer. This is another 256 byte table. Each entry in this table contains the ASCII code for the character, if any, which is considered to be equivalent to the character to which the entry corresponds. The general rule is: the uppercase version of any character is considered to be equivalent to its lowercase version. The converse of this general rule is not true. The search used by LEAP is not case sensitive unless the SHIFT key is pressed along with a character. If the SHIFT key is pressed with a character the character will be considered strictly uppercase. If a character has no equivalent, its entry in the maptable will contain a 0. Character Equivalence Table: (Second) (First Hex Digit ->) (hex) 0 1 2 3 4 5 6 7 8 9 A B C D E F (digit) 0 0 0 93 0 0 70 0 0 87 0 0 0 0 0 0 0 1 0 0 0 0 61 71 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 62 72 0 0 0 91 0 0 0 0 0 0 3 0 0 0 0 63 73 0 0 84 0 0 0 0 0 0 0 4 0 0 0 0 64 74 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 65 75 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 66 76 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 67 77 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 68 78 0 0 89 0 0 0 0 0 0 0 9 0 0 0 0 69 79 0 0 0 0 0 0 0 0 0 0 A 0 0 0 0 6A 7A 0 0 0 0 0 0 0 0 0 0 B 0 0 0 0 6B 0 0 0 0 0 0 0 0 0 0 0 C 0B 0 0 0 6C 0 0 0 0 0 0 0 0 0 0 0 D 0 0 0 0 6D 0 0 0 8C 0 0 0 0 0 0 0 E 0 0 0 0 6E 0 0 0 0 0 0 0 0 0 0 0 F 0 0 0 0 6F 0 0 0 86 0 0 0 0 0 0 0 _______________________________________________________________________________ A Step-By-Step Explanation of the Algorithm Pattern: hij Text Being Searched: abcdefghijklmnop 1. The search starts, more or less, at the current cursor position. abcdefghijklmnop ^ 2. Since our string is 3 characters long, the first character to be examined by the search routines will be the third character in the text, the 'c'. abcdefghijklmnop ^ 3. The ptable data for the character 'c' is used to determine the location of the next character to check. The entry for 'c' in the ptable contains a 3. What we know at this point. We know that 'c' is not the last character in the pattern because its ptable entry does not contain a $FF. Since we are on the third character in our text and it is not the last character in the pattern, there is no way any of the characters we skipped over could contain the pattern. Because we do know that we could not have skipped over a possible match, we can skip ahead another full pattern length number of characters (3). 4. The next character we encounter is an 'f'. abcdefghijklmnop ^ Its ptable entry contains a 3 also. For the same reasons described in part 3 above, we will skip ahead another 3 character positions. 5. The next character we encounter is an 'i'. abcdefghijklmnop ^ The entry for 'i' in the ptable contains a 1. Whenever the search routines encounter an 'i', which is the next to last character in the pattern, they must be sure to check the character which follows the 'i' . 6. After advancing by one character, we encounter a 'j'. abcdefghijklmnop ^ 'j' is the last character in the pattern because its ptable entry contains a $FF. Now the search routine knows it has a possible match. Only at this point will it take the time to explicitly compare each character in the pattern with the possible match in the text. 7. The text string matches the pattern so the search is finished. The cursor is placed over the 'h'. If a normal "compare-each-character-in-the-text-to-the-pattern" search had been used, 8 character comparisons would have been performed before the match was located. With the Boyer-Moore method, only 3 numeric comparisons and 2 character comparisons were required. On the negative side, the Boyer-Moore search does require extra time to create the ptable . As the length of the search pattern increases, the speed of the Boyer-Moore search surpasses the speed of the conventional string search, even when the table-building time is taken into account. ________________________________________________________________________________ Handling Accent Characters In the ptable you will notice that all of the entries from $B0 up contain a 1. In this range, the only entries which correspond to characters found in the text are the entries from $B0 - $B8 and from $C0 - $C8. These are the entries for the accent characters. As you may recall from previous explanations of accents and accented characters, an accented character such as n is stored as a two byte value in the text. The first byte holds the character code for the main character, the 'n', and the second byte holds the code for the accent. The search routines will only pay attention to the "main" characters in the text unless an accent is specifically included in the pattern. If the search routine happens to land upon the data for part of n, the 1 in the ptable entry will cause the search to be advanced by one, effectively skipping over the accent character. This means that both the word "Canada" and "Canada" will be found when the pattern "can" is used to leap through the following sentence: "Is it Canada or Canada ?" If the more specific pattern "can" is used, only "Canada" will be found by the search routines. _______________________________________________________________________________ The LEAP Mechanism _______________________________________________________________________________ do-lex is the word called when either LEAP key is pressed or another key is pressed while a LEAP key is down. As was stated previously, many operations are supported through the LEAP mechanism. do-lex has the responsibility of deciding which of the many operations it should perform. _______________________________________________________________________________ Initializing a LEAP Operation _______________________________________________________________________________ do-lex's first action will be to check the contents of the lexxing integer to see if a leaping operation is already in progress. If leaping is not already in progress, init-lex is used to perform general leaping preparations. During leaping, all characters typed are considered to be lowercase unless they are typed with the SHIFT key down. The LOCK key has no effect. To reinforce this rule with the user, the LOCK light is forced off during leaping. The previous state of the LOCK key is saved in the oldshiftlock integer. savepos is used to save the state of the current cursor state integers. newlex and lexxing are turned on to indicate that a new LEAP is being initiated and that leaping is occurring. matched is turned off to indicate that a match has not yet been found. leftlex? is turned on if the left LEAP key (LEAP backwards) is pressed. The p (place) integer is set to point at the end of the current selection. The extbos and savebos integers are prepared in case a drag operation occurs. ________________________________________________________________________________ LEAPing Around the Text ________________________________________________________________________________ Whenever a character is pressed while a LEAP key is down do-lex is called with the character on the stack. If the character is not a special key character, the word searching will be used to try and build up the pattern and start the search. If the character passed to searching corresponds to the UNDO key and pattern holds a '-2' value or a new LEAP operation has just started, searching is being asked to pass control to the SPELL CHECK LEAP command. searching will discard the character and call spellcheckleap . First, searching must use the character to update the pattern. If the character corresponds to the ERASE key, pattdel will be used to remove a character from the end of the pattern. If the character is any other text character, pattadd will be used to append the character to the current pattern. pattadd is smart enough to handle accent-character merging (like Insert). pattadd contains a begin...again loop and, once called, will continue calling and adding/deleting characters to/from the pattern until no valid characters are available. Once pattadd completes, the ptable is built and the search is started. If the search is not successful, the pattern is checked for the special case where the pattern consists of three or more characters, all of which are identical. The discovery of the reason why the Boyer-Moore search would fail with such a pattern is left as an exercise for the reader. If this pattern occurs, searching will resort to the use of a standard string comparison search. The search for a repeated single character pattern is the worst case search on the Cat. When the search has completed, end-search is used to update the cursor position and display if necessary. ________________________________________________________________________________ Low Level Search Routines Text searches on the Cat will wrap around from the start search position, to the end of the text, to the start of the text, and then back to the start position if necessary. The low level routines used to perform the searching are < , > , search< , and search> . < is used to search in a forward direction through a specified range of text. > is used to search in a backward direction through a specified range of text. Both of these routines are aware of the gap area and are able to skip over it. search< and search> are the slightly higher level search words which directly use < and > . ______________________________________________________________________________ Searching for Single Page Breaks The case where the user leaps from page break to page break in the text is a special one. When leaping in this manner, the cursor stops on both explicit and implicit page breaks. This is unusual because implicit page breaks have no corresponding character in the text and would not normally be found using the leap search algorithm described previously. This special leap case is handled within search> and search< . If these routines find that the pattern contains only a single page break character, they will use the page routines page? and prevpage to find the next implicit or explicit page break. Although these page break searches are not as optimized as the Boyer-Moore search, the largest area they will be asked to search is approximately 2K bytes, the maximum amount of data which can be held on a single page. _______________________________________________________________________________ Scroll Again _______________________________________________________________________________ If do-lex detects that the key passed to it corresponds to the press of one of the USE FRONT keys (which indicates that a repeat operation is being requested), it will first check to see if pattern holds a '-1'. If pattern does hold a '-1', lex-scroll will be used to scroll the screen once and do-lex will then terminate execution. As we will learn later when finish-lex is discussed, whenever the display contents are scrolled up or down by pressing a LEAP key while a SHIFT key is held down, a 4-byte '-1' value will be placed in the pattern integer. If a USE FRONT key press is subsequently passed to do-lex , do-lex will know that the scroll operation is the operation to be repeated. Before do-lex terminates execution, auto repeating will be turned on for the USE FRONT LEAP key combination so that the screen will continually scroll as long as USE FRONT and LEAP are pressed. _______________________________________________________________________________ SPELL CHECK LEAP Again _______________________________________________________________________________ If pattern holds a '-2' value when a USE FRONT key press is passed to do-lex , it means a SPELL CHECK LEAP command is being repeated. The first time SPELL CHECK LEAP is used it stores a '-2' into pattern. do-lex will turn on the SPELLCHECK indicator light and call spellcheckleapagain . After auto repeating is turned on for the USE FRONT - SPELL CHECK LEAP key combination do-lex will be exited. _______________________________________________________________________________ Search Again _______________________________________________________________________________ If a USE FRONT key press is received and pattern contains none of the special flag values described above, do-lex knows that it is being asked to repeat a search for the current leap pattern. The ptable will be constructed for the current pattern and research will be used to perform the searching. _______________________________________________________________________________ Finishing a Leap Operation _______________________________________________________________________________ After checking for the start of a new LEAP operation, do-lex performs an opposite test and checks for the termination of a LEAP operation. If neither the left or right LEAP key, or the left or right USE FRONT keys are down, leaping is terminating and finish-lex is executed. finish-lex will restore the previous shift LOCK key state, re-enable the cursor, and set the lexxing integer to false to indicate that LEAPing is no longer occurring. Next, finish-lex must determine which type of LEAP operation is being completed. _______________________________________________________________________________ SHIFT - LEAP Scrolling If newlex is true (new LEAP operation), matched is false (no pattern was successfully matched during this LEAP operation), and a SHIFT key is down, finish-lex is being executed because SHIFT and LEAP were pressed and released. The termination of LEAP under these conditions will cause lex-scroll to be executed and a '-1' to be placed in pattern. This makes lex-scroll the current LEAP repeat command. _______________________________________________________________________________ Creeping Otherwise, if newlex is true (new LEAP operation), matched is false (no pattern was successfully matched), and a SHIFT key is not down, finish-lex is being executed because a LEAP key was pressed and released and no pattern was entered. This will cause lex-tap to be executed. No flag will be placed in pattern because creeping is not a repeatable LEAP operation. lex-tap will cause the cursor to "creep" (move) one character position to the right or left. _______________________________________________________________________________ Other LEAP Terminations _______________________________________________________________________________ If newlex is false (not a new LEAP operation) or matched is true (successful search), then finish-lex was called because either: (1) the LEAP keys were used to highlight a selection and now they are being released, (2) the LEAP keys were used to choose a new location for a section of text and are now being released so the drag operation can be performed, (3) a successful LEAP or SPELL CHECK LEAP is finished, or (4) an unsuccessful LEAP or SPELL CHECK LEAP is finished. _______________________________________________________________________________ Highlighting a Selection If finish-lex is called when the selection is expanded, leave-extended is used to ensure that the newly highlighted selection is left highlighted and then finish-lex is exited. _______________________________________________________________________________ Dragging a Selection If finish-lex is called when the cursor is either split or extended, dragging is desired. If the drag destination location is still within the highlighted text to be moved, the text cannot be dragged and the selection is collapsed and a narrow cursor is placed at the drag destination. If the drag destination is valid, drag is used to move the text and finish-lex is exited. _______________________________________________________________________________ Successful SPELL CHECK LEAP If finish-lex is executed when pattern contains a -2, a successful SPELL CHECK LEAP has finished. p is saved in pop and the op is set to point to the end of the misspelled word so that if the user presses both LEAP keys, the misspelled word will be selected and could be conveniently added to the user dictionary with ADD SPELLING. _______________________________________________________________________________ Successful LEAP If finish-lex is executed when pattern did not contain -2, a successful LEAP has finished. The cursor state integers are updated. _______________________________________________________________________________ Leap Routines Summary: _______________________________________________________________________________ ( a1 a2 - a3-or-0 ) ('brac-search-forward') Searches forward through the range of text which starts at address 'a1' and ends at address 'a2' looking for a string in the text which matches the leap string whose address is in the pattern system integer. If a matching text string is found, the address 'a3' of the string is returned on the stack. Otherwise, zero is returned. uses the Boyer-Moore table pointed to by the ptable system integer to locate potential matches and then performs a string comparison to explicitly validate the match. > ( a1 a2 - a3-or-0 ) ('brac-search-backward') Searches backward through the range of text which starts at address 'a1' and ends at address 'a2' looking for a string in the text which matches the leap string whose address is in the pattern system integer. If a matching text string is found, the address 'a3' of the string is returned on the stack. Otherwise, zero is returned. uses the Boyer-Moore table pointed to by the ptable system integer to locate potential matches and then performs a string comparison to explicitly validate the match. ( - ) Toggles the effect of a lex-tap operation. Uses swappos2 to swap the saved contents of the cursor state integers with the current cursor state integers. The next time the cursor is drawn, the previous cursor state (as represented by the saved cursor state integer values) will be reflected. Adjusts the gapline if necessary and sets itself as the undo operation. advanceptr ( a n - a' ) Uses nextchar to advance the address 'a' by 'n' text character positions. 'n' must be a positive value. The new address 'a'' is returned on the stack. buildtable ( a n f - ) Builds the 256 byte Boyer-Moore search table used during leap searches. 'a' is the address of the string to be search for. 'n' is the length of the string and 'f' is a flag which indicates the search direction. If the flag is true, the search will proceed forward in the text. First, buildtable will fill all table entries between offset $00 and $B0 with the length 'n' of the string. All entries from offset $B0 to $FF will be filled with $01. Next, buildtable will selectively alter the entries corresponding to characters found in the search string. If the search is a forward search, the first character in the string, and all equivalent characters (as determined by the maptable ) will be given the value 'n-1'. The second character will be given the value 'n-2' and so on. The last character in the string will be given the special value $FF. If the search is a backward search, the last character in the string, and all equivalent characters (as determined by the maptable will be give the value 'n-1'. The second to last character will be given the value 'n-2' and so on. For information on the usage of this table, refer to the discussion of the LEAP search algorithm in this chapter. do-lex ( c - ) This is the word called when a LEAP key is pressed. Since the LEAP keys are used for several types of operations, do-lex must carefully check to determine how it should respond. The cases do-lex must handle are (1) the start of a new LEAP operation, (2) the end of a LEAP operation, (3) both LEAP keys have been pressed to highlight a selection, (4) the addition of a new character to the search pattern, (5) USE FRONT - LEAP scrolling, (6) SPELL CHECK LEAPing, (7) re-searching for a previous pattern. If (1) occurs, init-lex will be used to initiate a new LEAP operation. If neither the left or right LEAP key or a USE FRONT key is down, (2) has occurred. The character 'c' will be discarded, finish-lex will be used to terminate the leap process, and do-lex will be exited. If (2) didn't occur, the %lex value is placed in the curop integer to indicate that LEAP is the current operation. If (3) occurs, both LEAP keys are down and the cursor is not split, the character 'c' will be discarded, the selection will be expanded, and do-lex will be exited. If (3) didn't occur, a true flag is placed in direction if a forward LEAP is occurring and the character 'c' is checked. If it is not a special key, (4) is occurring, searching will be executed. searching will add the character to the LEAP pattern and search the text. If the character is a USE FRONT special key and it is not being released, then do-lex is being asked to repeat a previous action. If do-lex is being asked to repeat an action for the first time (newlex holds a true flag), do-lex will set newlex to false. Now, do-lex will check to see if (5) is occurring. If the first 4 bytes of pattern hold a -1, do-lex will execute lex-scroll will terminate execution. A -2 in pattern means case (6) is occurring. do-lex will turn on the SPELLCHECK indicator light, call spellcheckagain to handle the request, check for a panic key and set auto repeat accordingly, and then will terminate execution. If none of the other cases are occurring, do-lex is being asked to repeat the search of a normal leap string. The selection, if any, will be collapsed, start-search will be used to prepare for the repeat search, auto repeat is properly set up for the search, buildtable is used to create the Boyer-Moore table for the search, and research is used to perform the search. end-search ( a - f ) Terminates a LEAP or drag operation. The address 'a' on the stack is the result returned by the leap search routines. A copy of this value is placed in the matched integer. If this address is non-zero, a leap string match was found in the text. end-search will reposition the cursor position cpos to 'a' if it is non-zero or to gap prevchar (the original leap start position) if 'a' is zero. Then, if the cursor state was not extended or split, as it would be if a drag operation were being performed, the bos will be updated with the new cursor position and eos will be updated with the bos nextchar address. The screen display is updated and the cursor is set to a narrow state. Finally, if matched indicates that the pattern was not matched by the LEAP search routines and a LEARNing operation is occurring (either recording or playing back), end-search will abort the LEARN operation. end-search performs other actions which are related to the dragging of text. These actions will be discussed in the following section on dragging. finish-lex ( - )This routine is called when a LEAP operation is completed, when either the left or right LEAP key and the USE FRONT keys are released. The previous shift LOCK key state will be restored, the cursor will be reenabled, and the lexxing integer will be set to false to indicate that LEAPing is no longer occurring. Next, finish-lex determines which type of LEAP operation is being completed. If newlex is true, matched is false, and a SHIFT key is down, then finish-lex is being executed because SHIFT and LEAP were pressed and released. This will cause lex-scroll to be executed. Otherwise, if newlex is true, matched is false, and a SHIFT key is not down, finish-lex was called because a LEAP key was pressed and released and no pattern was entered. This will cause lex-tap to be executed. If newlex is false or matched is true, then finish-lex was called because either (1) the LEAP keys were used to highlight a selection and now they are being released, (2) the LEAP keys were used to choose a new location for a section of text and are now being released so the drag operation can be performed, (3) a successful LEAP or SPELL CHECK LEAP is finished, or (4) an unsuccessful LEAP or SPELL CHECK LEAP is finished. If (1) has occurred, leave-extended is used to ensure that the newly highlighted selection is left highlighted and then finish-lex is exited. If (2) has occurred, the drag destination location is still within the highlighted text to be moved, the selection is collapsed and a narrow cursor is placed at the drag destination. If the drag destination is valid, drag is used to move the text and finish-lex is exited. If (3) has occurred, and the search pattern contains a -2, a successful SPELL CHECK LEAP has finished. p is saved in pop and the op is set to point to the end of the misspelled word so that if the user presses both LEAP keys, the misspelled word will be selected and could be conveniently added to the user dictionary with ADD SPELLING. If (3) occurred and the search pattern did not contain -2, a successful LEAP has finished. The previous old selection range, marked by the op and pop integers, is updated. The current op value is placed in pop and the current p value is placed in op . The forceop integer is set to true and unmove is set as the undo operation. If the bos lies within the current leap range, the cursor is set to narrow. If the bos is at the start of the leap range, the cursor is set to wide. If (4) occurred, clearlearn is used to abort any LEARN activity and the cursor is reset to its pre-LEAP state. init-lex ( - ) Performs initialization at the start of a new LEAP operation. The state of the shiftlock key is saved and then the shiftlock is turned off. The undo buffer is cleared if the previous operation was not an uncreep or unscroll . savepos is used to save the state of the current cursor state integers. newlex and lexxing are turned on to indicate that a new LEAP is being initiated and that leaping is occurring. matched is turned off to indicate that a match has not yet been found. leftlex? is turned on if the left LEAP key (LEAP backwards) is pressed. The current beot prevchar address is saved in the p and savebos integers. The current bos address is saved in the extbos integer. leave-extended ( - ) Leaves a LEAP operation with the cursor extended. lex-scroll ( - ) This word is called when the [SHIFT][LEAP] key combination is used. Depending on which leap key is pressed, lex-scroll will either scroll the text up or down by one line. lex-scroll will store a -1 in pattern to indicate to do-lex that a shift-scroll operation occurred. %scroll will be placed in the curop integer and forceop will be turned on to indicate that the op should be advanced. lex-tap ( - ) Tries to advance the cursor forward or backward by one character. If the current undo operation is not uncreep (if we are not undoing a previous lex-tap), the undo buffer is cleared and cursor state information is saved. Next, lex-tap checks the current cursor state and advances the cursor accordingly. If the cursor is extended and the left leap key is pressed, tapmove will be used to collapse the cursor on the first character of the selection. The cursor will be narrow. The previous end of the extended selection (eos prevchar) will be saved in the op integer. If the cursor is extended and the right leap key is pressed, the cursor will be collapsed on the last character in the selection. The cursor will be wide. The previous start of the extended selection (bos) will be saved in the op integer. Otherwise, if the cursor is not extended, lex-tap will make the cursor narrow if necessary and then will use tapmove to move the narrow cursor one character to the left or right. pattadd ( c - ) Adds the character 'c' to the existing pattern pointed to by the address in the pattern system integer. Before adding the character to the pattern, pattadd checks to make sure the addition of the character will not cause the string to become longer than the maximum allowable LEAP string length (patternsize = 256 bytes). If there is enough room, and the character value is greater than $ff, w! is used to place the character into the string. If the character value is less than $ff, c! is used to place the character in the string. If the character is a single byte value, pattadd will also check to see if the current character is an accentable character which is preceded in the string by a bare accent character. If so, the 2 characters will be swapped to form a single accented character. After the character is added, the pattern length, kept in patlen , is incremented by one. Before terminating execution, pattadd will use to see if another key has become available. If a key is available and a LEAP key is down, and the key is not a special key or UNDO or ERASE, pattadd will immediately add the character to the leap string pattern. pattadd will continue adding characters to the leap string until indicates that no more characters are available. pattdel ( - ) Removes a character from the leap string pattern by decrementing the contents of the patlen integer by one. pattdel will only decrement patlen if patlen holds a non-zero value. pbpat ( - f ) Returns a true flag if the leap string pointed to by pattern contains only pagebreak characters. Returns a false flag if any character in the leap string is not a pagebreak character. research ( - ) This word is executed when USE FRONT and a LEAP key are used to repeatedly search for a previously specified pattern. If a forward search is being used, research must double check its current position before starting the search. If the cursor is currently sitting on the pattern and a forward search for the pattern is started, the search routines will endlessly find the pattern at the start position. In this case, the start position must be advanced by patlen before the search begins. The text is then searched back to the start position if necessary. If a backward search is used, research does not have to worry about the start position. The text is searched towards the start of text and back to the start position if necessary. search< ( a1 a2 - a3-or-0 ) ('search-forward') High level LEAP search word. Searches forward through the range of text which starts at address 'a1' and ends at address 'a2' looking for a string in the text which matches the leap string whose address is in the pattern system integer. If the search range is invalid (if the end address is less than the start address) or if there is no leap string to match (patlen = 0), will place a zero on the stack and exit immediately. If the pattern contains a single page break character, the words page? and prevpage are used to find the next page break in the text. Otherwise, < is used to perform the search. If < cannot match the pattern, pbpat is used to see if the pattern contains any page break characters. If it does, something to do with the bor and eor occurs. search> ( a1 a2 - a3-or0 ) ('search-backward') High level LEAP search word. Searches backward through the range of text which starts at address 'a1' and ends at address 'a2' looking for a string in the text which matches the leap string whose address is in the pattern system integer. If the search range is invalid (if the end address is greater than the start address) or if there is no leap string to match (patlen = 0), will place a zero on the stack and exit immediately. If the pattern contains a single page break character, the words page? and prevpage are used to find the next page break in the text. Otherwise, > is used to perform the search. If > cannot match the pattern, pbpat is used to see if the pattern contains any page break characters. If it does, something to do with the bor and eor occurs. searching ( c - ) Searches for the current leap pattern in the text. If the key is the UNDO key and pattern holds a -2 (SPELL CHECK LEAP occurring) or newlex is true, the character will be dropped, spellcheckleap will be executed, and searching will be exited. If the key is the ERASE key and newlex is true, the character is dropped and searching is exited (cannot search for an erase character). If newlex is true (new leap occurring) or pattern holds a -1 (previous operation was a shift-leap scroll), newlex is set to false and the old pattern is omitted (starting fresh). Now searching is almost ready to start the search. unexpand is used to collapse the selection and start-search is used to prepare for the search. If the character was an erase character, the last character is removed from the search pattern and the search start point is set to just after the current cursor position. If the character was not an erase character it is appended to the search pattern and the search start point is set to just after the current cursor position. Now that the pattern has been checked and adjusted, buildtable is used to build the pattern-specific Boyer-Moore search table. Now the search occurs. If a forward search is used, the second half of the text is searched first, then, if necessary, the first half of the text is searched. start-search ( - ) Prepares for the start of a search operation. If the cursor is extended or split, the contents of savebos are moved to bos and bos nextchar is moved into eos (why?). Otherwise, start-search performs no actions. tapmove ( a - ) Tries to move the cursor to text position 'a'. If the text position is not within the current leap range, tapmove is exited. Otherwise, the eos is set to 'a', bos is set to eos prevchar, the gap is adjusted, the display is fixed, the cursor is set to narrow at the new position, and forceop is set to true. uncreep ( - ) Executes . Used to indicate that the last operation was a creep. unmove ( - n1 n2 n3 n4 n5 n6 ) Uses pushpos to push the contents of the current cursor state integers onto the stack (why and how are they disposed of?) and then swappos to swap the contents of the current cursor state integers with the previous cursor state values. The undo buffer is cleared an unmove sets itself as the undo operation. unscroll ( - ) Executes . Used to indicate that the last operation was a scroll. _______________________________________________________________________________ Leap Integers _______________________________________________________________________________ direction Holds a true flag if a LEAP forward is occurring. leftlex? Holds a true flag if a LEAP backward is occurring. lexxing Holds a true flag if leaping is occurring maptable Holds a pointer to a table which maps invalid text characters to their valid text equivalents. matched Holds either the address of a leap string match in the text or 0 if a leap string was not matched. newlex Holds a true flag if this a new LEAP operation. Set by init-lex . oldshiftlock Holds the saved state of the shift lock key during a LEAP operation. patlen Holds the length of the current leap string. pattern Holds a pointer to the current leap string characters. patternsize Holds the maximum length of a leap string pattern (256). ptable Holds a pointer to the 256 byte Boyer-Moore search table. savebos Holds a saved copy of the bos pointer during a leap operation. _______________________________________________________________________________ 11. Drag _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 9/23/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The drag routines are used to move sections of text to different locations within the text. To move a section of text the user (a) selects the section of text to be moved and (b) uses the LEAP mechanism to move the cursor to the desired destination location for the text. When the user releases the [USE FRONT] and [LEAP] keys the text will be moved from the old to new location. The section on LEAPing should be read before this discussion of dragging. _______________________________________________________________________________ _______________________________________________________________________________ Drag Routines: _______________________________________________________________________________ drag ( - ) drag is the word called by the LEAP routine finish-lex when leaping is terminated and an extended selection exists. drag will first check to see if the drag destination location, found in the savebos system integer, lies within a locked region of text. If so, the original, highlighted selection is redisplayed if necessary and the operation is aborted. Otherwise, start-drag is executed. start-drag prepares the selection and destination location for a drag operation. The flag returned by save-drag , which indicates whether format packets must be adjusted, is stored in a local variable. If the destination location is currently represented in the window table, the screen line number in which the destination is located is saved in another local variable. If the destination is not represented in the window table this local variable will be set to 0. Next, drag checks to see where the destination location is relative to the selection location (the gap). If the destination is before the gap, drag-forward is used to implement the drag. If the destination lies after the gap, drag-backward is used. Both drag-forward and drag-backward are passed the size of the piece of text they will have to move in order to implement the drag operation. See the individual descriptions of drag-backward and drag-forward for a discussion of their operation and how the text pointers are adjusted by drag after text has been moved. After the text has been moved and the text pointers readjusted, drag uses preset to fix the gap skip markers and then passes the local packet and text-in-window flag to end-drag . Finally, undrag is set as the undo operation and the dirtytext? integer is turned on. drag-backward ( n - ) drag-backward is used to drag a selection to a destination in the second text partition. The value passed to drag-backward is the size of the text between the beot and the drag destination location. All intervals between the bos and destination are marked for updating and all intervals after the destination are marked as partially known. Next, the selection text is moved into place. If the system has enough room, the selection is moved forward 'size' bytes and then the text between the beot and destination is moved into place, right before the new selection location. If the system does not have enough room to perform this straightforward text movement, a series of reverse operations will be used to reposition the text. drag will reposition the text pointers after drag-backward completes. The old bos location will be saved in the pop integer. bos size + will be saved in the op integer. The gap , beot , and extbos integer contents will all be decremented by 'size' bytes. Before: <--size--> |___________XXXX|__________|_________________| ^ ^ ^ ^ bos gap beot dest After: <--size--> |_______________|______XXXX|_________________| ^ ^ ^ ^ bos gap beot dest drag-forward ( n - ) drag-forward is used to drag a selection to a destination in the first text partition. The value passed to drag-forward is the size of the text between the destination and the bos location. All intervals between the destination and beot are marked for updating and all intervals after the beot are marked as partially known. Next, the selection text is moved into place. If the system has enough room, the 'size' bytes between the destination location and the selection start are moved right before the beot . Then the selection text is moved back to the destination location. If the system does not have enough room to perform this straightforward text movement, a series of reverse operations will be used to reposition the text. drag will reposition the text pointers after drag-forward completes. The old beot address will be saved in the pop integer. The old savebos (destination) address will be saved in the op integer. The gap , beot , and extbos integer contents will all be incremented by 'size' bytes. Before: <--size--> |______________XXXX|____________|_________________| ^ ^ ^ ^ dest bos gap beot After: <--size--> |___XXXX___________|____________|_________________| ^ ^ ^ ^ dest bos gap beot end-drag ( f n - ) end-drag fixes up format packets if necessary and redisplays the text in the window if necessary. The selection which was dragged is left highlighted. start-drag ( - f ) start-drag prepares the selection and destination location for a drag operation. start-drag is responsible for adjusting the destination location if necessary, trimming the selection to be dragged if necessary, and for adjusting selection format packets if necessary. A selection cannot be dragged before the first document separator (savebos bor = cannot be true). If this case exists, the savebos location is incremented by one so that it will be located just past the first document separator character. The first document separator cannot be dragged with a selection. If the first document separator character is included in a selection, bos bor = , the bos will be incremented by one so that the first document separator is not included in the selection. Likewise, the last document separator cannot be dragged with a selection. If the last document separator character is included in a selection to be dragged, eos eor = , the eos will be decremented by one so that the last document separator character is not included in the selection. Next, start-drag deals with format packets. If there are no format packets or breaks in either the selection to be dragged or the piece of text which will be moved to execute the drag, format packets do not need to be adjusted and start-drag will complete execution. If all of the above cases are not true, start-drag will check the break immediately before the selection and the last break in the selection. If either of these breaks does not have an associated format packet, start-drag will make one and emplace it in the text. start-drag will also save the formatting state at the destination location in the workpkt if the formatting cases were not all true. The flag returned by start-drag is true if the format packets had to be checked. undrag ( - ) undrag uses drag to redo the drag operation in the reverse order. The pop contents are moved into savebos before the new drag operation is performed. The window is redisplayed as necessary. _______________________________________________________________________________ 12. Copyup _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 9/20/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The Copyup routines allow the user to transfer text from on disk to another. Copyup is not a command that is explicitly executed by the user. To transfer text, the user selects the section of text to be transferred, places the destination disk in the disk drive, and uses [USE FRONT][DISK] to load the contents of the destination disk into memory. During the loading process, the disk code will notice that the previous text contained selected text. The selected text will be put in safe place during loading of the new text and will be inserted into the new text at the current cursor location once the new text has been loaded. _______________________________________________________________________________ _______________________________________________________________________________ Copyup Routines _______________________________________________________________________________ copyup ( - ) If, when the user presses [USE FRONT][DISK] to move the contents of the disk in the drive to machine memory, the text currently in the machine has an extended selection, copyup will be executed to transfer a copy of the current highlighted text into the text about to be loaded into memory. The text copied up will be inserted into the new text starting at the new text's current cursor location. copyup will save a copy of the highlighted text in a safe space (at ramend) before the process of moving the new text into memory begins. copyup performs the following 7 actions. (1) If the disk in the drive, which contains the new text to be loaded in, is locked, a "copyuplock" error will be issued. (2) Determines the size of the highlighted selection ( gap bos - size to ). (3) If the selection contains a break, the selection size is incremented (pktsize 2* size +to) to account for packets which will be inserted into the selection to preserve the format of the selection during the move. (4) If there will not be enough room to hold the selection during the loading of the new text, a "nocopyuproom" error will be issued. (5) If the selection contained a break, format packets are inserted into the selection if necessary. (6) The selection is moved up to its safe position near the end of RAM memory. (7) The location of the safe selection is stored in the copyuptr system integer. move&adjusttext ( a1 a2 - ) Moves and compresses/expands the current text area to a new region of memory which starts at address 'a1' and ends at address 'a2'. After the text has been moved, text will be located at address 'a1' and endtext will be located at address 'a2'. All of the affected text pointers are adjusted accordingly and the interval table is updated. Used by the disk code as it prepares to move text to and from disk. unpackcopiedup ( a1 a2 a3 - ) unpackcopiedup is used to merge text saved by copyup into the text of the new disk. unpackcopiedup first checks to make sure there is enough room in the new text for the copied up selection. If so, the copied up text will be moved temporarily to the end of the text. Then the copied up text is moved into the undo buffer and the text is moved around to make room for the copied up text. If any of format packets need to be adjusted/inserted in the copied up text, the adjusting/inserting is performed while the copied up text is in the undo buffer. After the copied up text is ready, insertblock is used to insert the copied up text into the new text at the current cursor location. Any calc packets in the copied up text are adjusted, the text is redisplayed, the undo buffer is cleared, and removeselection is set as the undo operation. _______________________________________________________________________________ 13. The Keyboard Interface and the Learn Command _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 4/87 Second Draft LC 5/27/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: There are two possible sources of key events in the Cat system. Real key events generated directly from the keyboard are spotted by the interrupt service routine responsible for scanning the keyboard and reporting keyboard state changes as key events which are added to the key event queue. Recorded key events are generated when a learn sequence is played back. Only the lowest-leve Forth keyboard I/O words know the difference between real and recorded key events. This section discusses the terminology and data structures associated with the Cat keyboard interface, the Forth words involved with key press handling, and the close tie between the keyboard interface and the Learn command. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 Keyboard Interface Terminology and Data Structures 3 Scanning the Keyboard 3 Keyboard Event Queue 4 Special Keys 5 Keyboard Translation Table 7 Pricessing Key Press Information 8 Returning Character Information 9 Types of Key Information 9 Real Key Information 9 Recorded Key Information 11 Obtaining Key Information 11 The Key State Environment 11 Setting Up the Editor Key State 12 Getting the Character 13 The Learn Command 13 Learn Strings 13 Important Learn Integers 14 Recording a Learn Sequence 14 Terminating a Learn Recording 15 Phrase Storage 15 Playing Back a Learn Sequence 16 Inserting Stored Phrases 17 Forth Keyboard Routines Summary 21 Learn Routines Summary 24 Keyboard Integers Summary 26 Learn Integers Summary 27 Learn Strings Creation _______________________________________________________________________________ List of Illustrations 13.1 The Keyboard Event Queue 13.2 Special Key Bit Assignments for shiftstate and modifiers 13.3 Layout of a Keyboard Translation Table 13.4 Format of a Translation Table Entry 13.5 Format of Character Data Returned in kval 13.6 Format of a Character Entry in a Learn String _______________________________________________________________________________ Keyboard Interface Terminology and Data Structures _______________________________________________________________________________ _______________________________________________________________________________ Scanning the Keyboard At the lowest level of the "Cat" keyboard interface lies the keyboard interrupt service routine. Every time a key is pressed, a 68000 Level 1 autovector interrupt occurs. Part of the code fragment used to service a Level 1 interrupt is responsible for polling the keyboard. During the keyboard polling process 8 bytes of information are received. These 8 bytes of data are placed in an 8 byte buffer whose start address is stored in the tempkey integer. This is the current keyboard scan information. The information received during the previous polling process is stored in another 8 byte buffer, whose start address is stored in the newkey integer. Each time the keyboard service routine is called it polls the keyboard and compares the information received to the information received during the last execution of the service routine. If the information in the tempkey and newkey buffers is the same, the service routine will take no action. Only when the tempkey and newkey buffers hold different information, which indicates that a change in the state of the keyboard has occurred, does the keyboard service routine report a keypress to the rest of the system. When a change in keyboard state occurs, the 8 bytes of information in tempkey are transferred to the newkey buffer and 1 byte of scan information, condensed down from the 8 bytes of information actually received, is placed into the keyboard event queue. _______________________________________________________________________________ The Keyboard Event Queue The "Cat" keyboard event queue is a $20 hex byte circular queue (see following diagram). The start address of the queue memory is located in the cbuff system integer. The two pointers used to maintain the circular queue are kept in the inptr and outptr system integers. When the keyboard service routine adds an event byte to the queue, the byte is placed in the address pointed to by inptr and then the inptr address is incremented by one. When an event byte is removed from the queue, the byte is taken from the address pointed to by outptr and then the outptr address is incremented by one. inptr always points to the next available location in the queue and outptr always points to the next available event byte in the queue. If inptr and outptr hold the same address, the queue is empty. Each entry in the keyboard event queue is 1 byte long. Diagram 13.1 The Keyboard Event Queue Diagram 13.3 The Keyboard Translation Table _______________________________________________________________________________ Special Keys A special key is a key on the keyboard which does not generate a character code when typed. A special key will either affect the way subsequent key presses will be interpreted or will cause an editing command to be executed. The special keys on the Cat keyboard are listed below: KB1/2 Left shift Right shift Shift lock Left USE-FRONT Right USE-FRONT Left LEAP Right LEAP Special key up/down state information is kept in two 8-bit bit arrays. One bit array is held in the shiftstate system integer and the other is kept in the modifiers system integer. NOTE: Really, shiftstate should be called "specialstate" because its contents actually represent the current states of all of the "special" keys, not just the state of the "shift" keys. Each bit in the bit array is used to represent one of the 8 special keys. These are the bit assignments: 7 6 5 4 3 2 1 0 _________________________________________________________ | | | | | | | | | --------------------------------------------------------- ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | KB2 | | | | | | left shift | | | | | right shift | | | | shift lock | | | left use front | | right use front | left leap right leap Figure 14.2: Bit Assignments in a Special Key Bit Array The shiftstate bit array always represents the actual physical state of the special keys, with one exception. The shift lock bit is not cleared until a key up event for either the left or right shift key is received. Normally, a special key bit is cleared as soon as a key up event for that special key is received. The modifiers bit array holds the state of the special keys as viewed by the editor. The modifiers array will be the same as the shiftstate array except during the playback of learn sequences. During learn playbacks the modifiers array will be artificially altered to simulate the pressing and releasing of special keys. At the end of a learn sequence, the contents of the shiftstate array, which always holds the current physical states of the special keys, is copied into the modifiers integer. _______________________________________________________________________________ The Keyboard Translation Table The "Cat" editor supports the keyboard layouts of approximately 17 different countries. All of the keyboards have the same number of keys arranged in the same layout. It is only the assignment of character to key which varies from country to country. The keyboard service routine returns position specific information about a keypress, i.e. which key was pressed. A keyboard translation table is used to translate key position information to character information for any keyboard layout. There is a keyboard translation table for each of the 17 keyboard configurations supported. Every translation table consists of 4 subtables. The first subtable contains the data for characters found on the the unshifted version of KB1, the second contains the character data for the shifted version of KB1, the third contains data for the unshifted version of KBII, and the fourth contains data for the shifted version of KBII. There are 59 keys on the Cat keyboard. Each subtable contains 64 16-bit character data entries, one for each key on the keyboard with a few entries left blank. The following diagram shows how each translation table, and the subtables within, are arranged: Offset Translation Table Subtables _________________________________________________ $00 -> | | | KBI, non-shifted character data | |_________________________________________________| $40 -> | | | KBI, shifted character data | |_________________________________________________| $80 -> | | | KBII, non-shifted character data | |_________________________________________________| $C0 -> | | | KBII, shifted character data | |_________________________________________________| Figure 14.3: Layout of a Keyboard Translation Table The scan code returned in the lower 6 bits of the event code is actually the offset into a translation table subtable to the desired character data. The translation table used by a particular Cat is determined by a hardware switch. The translation table subtable used is determined by information found in the current shiftstate bit array. _______________________________________________________________________________ Processing Keypress Information _______________________________________________________________________________ do-event is the Forth word which removes the next available event code from the event queue and translates the key scan code information in the event code to keyboard-specific character information. If the event code does not reflect a change in state of one of the special keys, do-event uses the following algorithm to find the character information which corresponds to the event queue information: 1) First, do-event checks to see if any of the shift key bits in the SHIFTSTATE array are set. The state of the special keys determines how the character information is treated. If a shift key bit is set, $40 is added to the scan code offset (this bumps the offset into the shifted portions of the character data, see translation table diagram). 2) Next, the state of the KBI/II bit is checked. If the KBI/II bit is set, if keyboard II is selected, $80 is added to the scan code offset. This bumps the offset into the keyboard II part of the translation table. Now that the subtable to be used has been determined, do-event can index into the subtable, using the offset in the scancode information, and fetch the 2-bytes of translation table character data. If the key event code does correspond to a special key change in state, do-event will alter the shiftstate information. A special key down event will usually cause the special key's corresponding bit in the shiftstate array to be set. The exceptions are (1) a shift lock key down event is not accepted when a USE FRONT key is down, and (2) a left or right shift key down event will cause the shift lock bit to be cleared in addition to the left or right shift key bit being set. A special key up event will clear the special key's corresponding bit in the shiftstate array. The exception is that shift lock key up events are ignored. _______________________________________________________________________________ Returning Character Information When do-event has completed execution, 32 bits of character information will be stored in the kval ('key-value') system integer and a true ("character available !") flag will be stored in the kstat ('key-status') system integer. The character information returned in kval has this format: ______________________________________________________________ | 0 | shiftstate | data from translation table | -------------------------------------------------------------- 31 24 23 16 15 0 Figure 14.5: Format of the Character Information Returned in kval If a key up event for a non-special key is received, and the new translation table character data is the same as the current character data found in kval, (if the non-special key currently held down is being released) auto-repeating will be disabled. Before do-event finishes it will check the shift lock bit and turn the shift lock light on or off. _______________________________________________________________________________ Types of Key Information _______________________________________________________________________________ Key information can be obtained from two sources, (a) from the keyboard event queue, or (b) from a pre-recorded learn string which is currently being played back. Key information from the event queue is called "real" key information because it was generated directly from the keyboard. _______________________________________________________________________________ Real Key Information The Forth words <> and @k are used to check for and obtain real keypress information. <> spins in a loop calling do-event until a key event is returned (until the kstat integer contains a true flag) or until the event queue is empty (?ev is used to check for an empty event queue). <> returns a true flag if new key information is available (in kval). @k is used after it is determined that real key information is available. @k turns auto repeating on if the character is not a special key, sets up the next auto-repeat time, stores a false flag in kstat (to indicate that the current character is no longer available), and returns just the character value (the lower byte of the translation table data found in kval ) on the stack. _______________________________________________________________________________ Pre-Recorded Key Information The words playback? and playback are the Learn equivalents to <> and @k . playback? returns a true flag if the character information for a pre-recorded key event is available. The contents of the learnbuff and learning? system integers are used to determine whether a learn sequence is being played back. learnbuff will return a true flag if any type of learn operation, recording or playing back, is occuring. learning? will hold a true flag if a learn sequence is currently being recorded and a false flag if a learn sequence is being played back. If these integers indicate that a learn playback is occurring, and if there are still characters in the string which must be played back, playback? will return a true flag. If a pre-recorded character is available, playback is used to obtain the pre-recorded character information. A character entry in a learn string contains essentially the same data found in the kval integer. The difference is that scancode information is placed in the upper byte: ______________________________________________________________ | scancode | shiftstate | data from translation table | -------------------------------------------------------------- 31 24 23 16 15 0 Figure 14.6: Format of a Character Entry in a Learn String playback unpacks the key event information recorded in the learn string. The actual character data, the data from the translation table, is returned on the stack. To simulate the environment in which the key was originally typed, the scancode and shiftstate information is placed in the corresponding key state system integers. _______________________________________________________________________________ Obtaining Key Information _______________________________________________________________________________ is the main Forth word used to check for available keypress information of any type, real or recorded. If a real key is not available, will check for a recorded key. If either type of key is available will set up a duplicate version of the key state environment and will return a true flag. If no key is available, a false flag is returned. _______________________________________________________________________________ The Key State Environment Complete information about a key press is stored in two sets of system integers. Both sets hold equivalent information: kval < - - - - > char kstat < - - - - > char? shiftstate < - - - - > modifiers The integers on the left have already been introduced. kval , kstat , and shiftstate always contain current key information about the most recent keyboard keypress. The integers on the right contain key information which corresponds to the state of the keyboard as viewed by the editor. Usually char , char? , and modifiers contain the same information as their left column counterparts. During the playback of learn sequences however, the integers on the right will be deliberately modified by the learn routines. When playback of a learn sequence is terminated, the contents of the integers on the left are copied into the integers on the right to get the system back into sync. _______________________________________________________________________________ Setting Up the Editor Key State The word !char ('store-char') is used by to set up the editor version of the key state information. !char is very similar in function to do-event . The differences are, (1) do-event is an assembler routine, !char is written in Forth; (2) !char is passed translation table data on the stack, do-event obtains the translation table data itself; (3) if !char is passed special key event information it will alter the special key bit array kept in the modifiers system integer instead of the shiftstate bit array; and (4) !char returns character information in the char and char? system integers instead of in the kval and kstat integers. _______________________________________________________________________________ Getting the Character If indicates that a character is available, is used to get the character and, if a learn sequence is being recorded, appends the key information to the learn string. spins in a loop until indicates that a key of any type is available. When a key is available will take the character value from char and leave it on the stack and will set the char? flag to false to indicate that this key is no longer available. _______________________________________________________________________________ The Learn Command _______________________________________________________________________________ The learn command is used to record sequences of keypresses which may be played back at a later time. Since the playback and recording of learn strings is connected to the keyboard interface at a very low level, the rest of the system never needs to be concerned with the actual source of key information received. As far as the rest of the system is concerned, there is always a typist at the keyboard. _______________________________________________________________________________ Learn Strings The Cat system can store up to 10 learn sequences at once. The learn sequences, which are actually just strings composed of 4 byte packets of key press information, are stored in 10 string variables named learn0 , learn1 , etc. A 20 byte table in memory holds the 10 2 byte tokens for each of the learn string variables. Execution of the word learnstrings will place the address of this table on the stack. _______________________________________________________________________________ Important Learn Integers learnbuff returns a true flag if any type of learn operation, recording or playing back, is occuring. learning? holds a true flag if a learn sequence is currently being recorded and a false flag if a learn sequence is being played back. curlearn holds the number of the learn string currently being played-back/recorded. learnpos holds the offset into the current learn string to either the next key press to be played back (during play back) or to the location where the next key press information received will be stored (during recording). maxlearn holds the maximum allowable length of a learn string, which is currently 256 bytes. _______________________________________________________________________________ Recording a Learn Sequence [USE FRONT] [LEARN] is pressed to initiate the recording of a learn sequence. When [USE FRONT] [LEARN] is pressed, the word Learn is executed. Learn will perform one of two actions. If Learn is executed when no learn activity is currently occurring (learnbuff holds a zero), Learn will first use indicate to display the "Learn ?" indicator light. Next, Learn must wait in a loop until it receives the digit that indicates to which learn string the upcoming learn sequence should be assigned. If the next keypress received does not correspond to a digit (0-9), Learn will turn the indicator light off and will terminate execution. If a digit is received Learn uses showlearn to replace the "?" in the indicator light with the number received and newlearn is used to set the system up for learn string recording. newlearn places the chosen learn string number in curlearn , tries to expand the chosen learn string to the maximum allowable learn string length, sets learnpos (the offset into the learn string) to zero, turns learning? on (to indicate that recording is occuring), and turns learnbuff on (to indicate that a learn activity is occuring). Now the system is ready to record any subsequent key press information received. Whenever is used to obtain key press information, the last word it executes is record . record checks the learning? and learnbuff integers to see if recording is on. If recording is on, and if there is enough room in the learn string for one more key press entry, record will store the current scancode contents in the upper byte of the current key press information and will place all 4 bytes in the next position in the current learn string. _______________________________________________________________________________ Terminating a Learn Recording The recording of a learn sequence is terminated by pressing [USE FRONT] [LEARN] again. If a learn activity is occurring when Learn is executed, Learn will terminate recording with the use of clearlearn . clearlearn turns the indicator light off and uses clr-kbd to clean up. If recording is on, clr-kbd will reduce the current learn string size to its actual size, will transfer the contents of shiftstate to modifiers (to synchronize the two special key bit arrays), will turn learning? and learnbuff off, will make sure that any leaping activities properly terminate (with the use of finish-lex), and will use rule to redraw the ruler bar/status area. At this point, if the selection is not extended, Learn will terminate execution and recording will be stopped. _______________________________________________________________________________ Phrase Storage The learn command also supports phrase storage. If the selection is extended and the learn string is empty when a recording is terminated, the selected phrase will be stored in the current learn string, with a 4 byte zero header, before Learn terminates execution. When the learn string is played back, it will cause the stored phrase to be placed in the text starting at the current cursor location. To store a phrase, a phrase in the text must be highlighted and the recording started as described above. Immediately after the learn string number is specified by pressing a digit key, [USE FRONT] [LEARN] should be pressed again. This second press of [USE FRONT] [LEARN] will cause Learn to be executed. Learn will note that recording is on and will stop recording in the manner described above. Next, if the current learn string is empty, and if the selection is extended, Learn will proceed with the phrase storage process. This process has up to three steps. First, the phrase will be temporarily stored in the gap area while Learn checks to see if a format packet needs to be inserted into the phrase. Learn will check to see if there is enough room in the gap for the selection and a paragraph format packet. If there is enough room, a 4 byte zero flag followed by the selection text will be placed in the gap. Next, Learn checks to see if a format packet should be inserted into the phrase. If a format packet is requred, Learn inserts one in the correct position. Finally, the phrase string is moved into the current learn string and execution of Learn terminates. _______________________________________________________________________________ Playing Back a Learn Sequence To initiate playback of any of the recorded learn sequences, [USE FRONT] is held down while the digit key associated with the learn string is pressed. When [USE FRONT] [digit] is pressed, lrncmd will be passed the number of the desired learn string and executed. If a learn sequence is currently being recorded, the current learn string is closed down (reduced to its proper length). After lrncmd has terminated any recordings in progress, it uses showlearn to display the string number of the learn sequence selected for playback and uses newplayback to initiate playback of the selected string. newplayback sets the current learn string and checks the contents of the first four bytes in the current learn string. If the first four bytes hold a zero the learn string holds a phrase. The handling of stored phrases is discussed below. If learn string does not hold a phrase, newplayback puts the system in the playback state by clearing all bits in the modifiers bit array, setting the offset into the learn string, learnpos , to zero, turning learning? off to indicate that playback is occurring, and turning learnbuff on to indicate that a learn activity is underway. Now, when is asked for key information it will return playback characters until all characters in the learn string have been played back. _______________________________________________________________________________ Inserting Stored Phrases If a learn string with a stored phrase is selected for playback, newplayback will insert the phrase into the text at the gap and will adjust the format packets as necessary. _______________________________________________________________________________ Forth Keyboard Routines Summary _______________________________________________________________________________ _______________________________________________________________________________ Preparing Keypress Information !char ?kval do-event ( - ) do-event is the only Forth keyboard I/O word which interacts directly with the keyboard event queue. do-event performs four main activities: (1) gets the next keyboard event from the event queue and adjusts the queue accordingly, (2) gets the translated value of the event code from the keyboard codes table, (3) uses the translated value to set up the data to be returned in kval , and (4) puts a true flag in the system integer kstat to indicate that key information is available. _______________________________________________________________________________ Obtaining Keypress Information: <> ( - f ) ('bracket-bracket-question-key') Returns a true flag if a real key event, from the event queue, is available. ( - f ) ('bracket-question-key') Returns a true flag if a character is available, either a real character from the event queue or a simulated character from a learn sequence. ( - c ) ('bracket-key') Waits in a loop until a character is available, gets the character data from char , and puts a false flag in char? to indicate that the character has been taken. If the system is currently recording, records the character. ?ev ( - f ) ('question-event') Returns a true flag if there is key event information in the keyboard event queue. If the inptr and output contain different addresses, the queue is not empty. ?k ( - f ) ('question-key') If a non-special key is down returns a true flag and disposes of the key. ?t ( - f ) ('question-terminal') Polls the keyboard for an ASCII keypress value (0<=value<=$7F). Returns a true flag if an ASCII key was available. The key is discarded. @k ( - char ) ('fetch-char') Fetches the key data for the next available character from kval and, if the character is not a special character, enables auto-repeating. Sets up the next auto-repeat time in ktime and turns kstat off. Only called when a key is available. key ( - c ) Waits in a loop until a non-special key is available. Returns the ASCII code (0<=code<=$7F) for the key. _______________________________________________________________________________ Auto Repeat Routines: ?auto ( - f ) ('question-auto-repeat') Returns true flag if it is time to auto-repeat a character. If the number of ticks has exceeded the next scheduled auto-repeat time, stored in ktime , it is time to repeat. clear-auto ( - ) Disables auto-repeating for the last key returned by storing a false (0) flag in auto . set-auto ( - ) Turns on auto-repeating. Stores a true flag in auto and calculates and stores the next scheduled time for an auto-repeat in ktime . _______________________________________________________________________________ Words Which Check and Affect the shiftkey and modifiers States: ?ctl ( - f ) Returns a true flag if the editor thinks one of the USE FRONT keys is down. ?kb2 ( - f ) Returns a true flag if the editor thinks the KBI/II is down, i.e. that KBII is currently in use. ?keystep ( - f ) If a space is currently available, waits in a loop for the next real key event. Returns a true flag if a carriage return becomes available. ?lex ( - f ) Returns a true flag if the editor thinks the left leap key is down. ?rex ( - f ) Returns a true flag if the editor thinks the right shift key is down. ?shift ( - f ) Returns a true flag if the editor thinks one of the shift keys is down. ?shifted ( - f ) Returns a true flag if the editor thinks one of the shift keys, or the lock key is down. ?shiftlock ( - f ) Returns a true flag if the editor thinks the lock key is down, i.e. that shift lock is currently in effect. down? ( n - f ) Returns a true flag if bit 'n' in the modifiers bit array is set. Since each bit in the modifiers array corresponds to one of the special keys, this routine is used to check whether a certain special key is considered to be down. : down? ( n - f ) 1 swap ( Put bit number on top. ) shl ( Shift the '1' into the specified bit position.) modifiers and ( Isolate the specified bit position in the ) ( modifiers , or shiftstate bit array. ) 0= 0= ; ( Return true flag if bit was set. ) Kb1/2 ( - ) Checks the state of the KBII bit in the modifiers bit array. If the bit is set, the KBII bit is cleared in both the modifiers and shiftstate bit arrays and vice-versa. sync-shiftkeys ( - ) Puts a copy of the special key bit vector in shiftstate in modifiers . toshiftlock ( f - ) If the flag is true, the shift lock bit in the special key bit vector found in modifiers will be set and the shift lock light turned on. If the flag is false, the shift lock bit in the special key bit vector in modifiers will be cleared and the shift lock light will be turned off. If a learn is not currently being played back, the newly modified modifiers value will be copied into shiftstate . _______________________________________________________________________________ Learn Routines Summary _______________________________________________________________________________ #key? ( n - f ) Compares the scancode associated with the character most recently received to the scancodes for the numbers 0 through 9. Returns a true flag if the scancode corresponds to a number. Learn uses #key? to get the number which is to be assigned to the learn sequence about to be recorded. ?panic ( - f ) Checks to see if the user has panicked and pressed a key in order to stop playback of a learn sequence. The first time the user hits a panic key, a true flag is stored in the panicked system integer. This flag will not be cleared until the panic condition is handled by some other part of the system. If a panic key is not currently available, the current panic state flag, stored in panicked will be returned. If a panic key is available, a true value will be OR'ed with the current panic state value. The use of the OR operation ensures that the panic state will never be accidently cleared. A special key going up is not considered a valid panic key event. If a special key going up is encountered the special key's corresponding bit in the modifiers bit array will be cleared. 0-cmd 1-cmd 2-cmd 3-cmd 4-cmd 5-cmd 6-cmd 7-cmd 8-cmd 9-cmd ( - ) 0-cmd is the word executed when [LEARN] and [0] are pressed while the [USE FRONT] key is held down, 1-cmd is the word executed when [LEARN] and [0] are pressed while the [USE FRONT] key is held down, etc. All of the "n-cmd" words pass a buffer number to lrncmd and cause a learn sequence to be recorded or played back. clr-kbd ( - ) Ends playback of a learn recording or learn playback session. Copies the special key bit array settings found in shiftstate to modifiers and places false flags in the learning? and learnbuff system integers. Learn ( - ) Stops learn or playback and sets flag. learnsize ( - n ) Returns the maximum number of bytes which are available for storage of a learn sequence. Checks to see if maxlearn bytes are available. learnstrings ( - ) Returns the address of a ten entry array of learn string tokens. lrncmd ( n - ) Starts a learn using buffer 'n' or plays back the learn sequence located in buffer 'n'. newlearn ( n - ) Performs all initialization required prior to the start of a learn recording. newplayback ( n - ) Performs the initialization required before a learn string can be played back. numberkeys ( - a ) Pushes the address of a 10 byte array of the scancodes which correspond to the digits 0 through 9. Used by #key? . playback ( - c ) Returns the next learn sequence character to be played back. playback? ( - f ) Returns a true flag if there is learn sequence character to play back. record ( c - c ) Records the character 'c' in the current learn string. setlearn ( n - ) 'n' is the string number to be used for the current learn recording. Checks to see that the number is within the allowable range of learn string numbers. If it is, the string number is stored in the curlearn system integer. showlearn ( n - ) Causes the learn indicator light to be displayed with the string "Learn #n" listed in the indicator. thislearn ( - a n ) Returns the address 'a' and length 'n' of the current learn string. _______________________________________________________________________________ Keyboard Integers Summary _______________________________________________________________________________ ?char Logical version of kstat . Holds a flag which indicates whether or not the editor should be told a key is available. auto Holds a flag which indicates whether or not auto-repeating is on. cbuff Integer used to hold the start address of the $20 byte circular event queue. char Logical version of kval . Holds the next key value which should be passed to the editor. inptr Keyboard buffer pointer. inptr holds address where next key code should be inserted into buffer. kcodes Holds a pointer to the keyboard translation table. ktime Auto repeat timer ? kstat Holds a flag, set up by do-event , which indicates whether key information is currently available. kval Holds key value returned by do-event . modifiers Holds a bit vector of the imagined special keys (as affected by learn). outptr Keyboard buffer pointer. outptr holds address from which next requested key code should be taken. scancode Holds just the scan code portion of the event byte from the last keyboard event processed. shiftstate Holds a byte-long bit vector which represents the current physical states of the special keys. ticks Time ticks. time0 Holds number of ticks between auto-repeats. (2 ?) time1 Holds number of ticks between auto-repeats. (10 ?) _______________________________________________________________________________ Learn Integers Summary _______________________________________________________________________________ #learns Holds the maximum allowable number of learn strings. curlearn Holds offset into learn strings table to the current learn string. learnbuff Holds a flag which tells the keyboard whether a learn- related operation is currently under progress. learning? Holds a flag which is true when a learn sequence is being recorded. learnpos Holds offset into the current learn string. maxlearn Holds the maximum allowable length of a learn string. panicked Holds a flag which indicates whether the user has panicked and pressed a key in order to terminate playback of a learn sequence. _______________________________________________________________________________ Learn Strings Creation _______________________________________________________________________________ : learn0 [ 0 , : learn1 [ 0 , : learn2 [ 0 , : learn3 [ 0 , : learn4 [ 0 , : learn5 [ 0 , : learn6 [ 0 , : learn7 [ 0 , : learn8 [ 0 , : learn9 [ 0 , code learnstrings ( - a ) nx ) jsr, ;c t' learn0 w, ( An array of learn string tokens. ) t' learn1 w, t' learn2 w, t' learn3 w, t' learn4 w, t' learn5 w, t' learn6 w, t' learn7 w, t' learn8 w, t' learn9 w, _______________________________________________________________________________ 14. The Sort Command _______________________________________________________________________________ _______________________________________________________________________________ Modification History: First Draft LC 6/3/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The Sort command allows the user to sort the records within any section highlighted text into an ascending or descending order. A record is delimited by one or more break characters. Fields within a record are delimited by one or more tab characters. Any field within a record may be used as the sort field. Five steps are required to sort text: 1) the selected text must be prepared; 2) a list of sort entries, each of which contains information about a record in the selection, must be created; 3) the list must be sorted; 4) the text in the selection must be rearranged according to the order specified in the sort list; and 5) the text must be redrawn on the screen. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ 3 Preparing the Sort Selection 4 Creating the Initial Sort List 5 Sorting the List 6 Shuffling the Records 8 The Sort Command _______________________________________________________________________________ List of Illustrations 14.1 Structure of a Sort List Entry 14.2 The Sort List Before and After Sorting 14.3 Location of the Sort List _______________________________________________________________________________ Preparing the Selection for Sorting _______________________________________________________________________________ The word presort is used to prepare the selection for sorting. presort first checks for the two cases which can cause the sort operation to be aborted at this point: 1) if the selection lies within a locked region of text, or 2) if sortbreaks , the system integer which holds the number of break characters to be used as a record delimiter, is set to zero. If sortbreaks is set to zero, the sorting routines will not be able to isolate records in the text. presort uses findfield to determine which field in a record should be used as the sort field (the key field). How the last record in the selection is selected determines which field in the selected records will be used as the sort field. If the whole record, including the trailing break character, is selected, the first (leftmost) field in the record will be used as the sort record. Otherwise, the field in which contains the end of selection will be used as the sort field. The field number (field numbering starts at zero) for the key field is stored in the tab# system integer. Next, presort must trim the beginning and the ends of the selection so that the sorting routines will operate on a whole number of records. The selection will be modified to meet the following criteria: 1) the selection cannot begin or end on a page break or document separator character; 2) the selection cannot start on a break character; and 3) the selection must end on the final break character in a record delimiter. If the selection does not meet the above criteria, the selection will be extended or shortened until it does. Finally, presort must ensure that the format of the text below the text to be sorted does not change. The potential problem is that a record in the selection text could hold the format packet used by the following unselected text. If this record is sorted into a different location in the text, it would take the format packet with it and the unsorted text below could receive a new, unexpected format. To prevent this problem, a copy of the last format packet in the selected text is saved just after the gap. After the text has been sorted and rearranged, the saved format packet will be placed after the last record in the newly sorted text. After presort has completed execution the selection must be checked once more to make sure that presort's selection trimming hasn't eliminated the selection. _______________________________________________________________________________ Creating the Initial Sort List _______________________________________________________________________________ Rather than sorting and rearranging the records in the text area, the sorting routines use a sort list structure which is comprised of many sort entries, each of which contains information about and pointers to the actual records in the text. The sorting algorithms sort the records according to information in the sort records and the new sorted order for the records is specified by changing the arrangements of the pointers in the sort entries. Each sort entry is 22 bytes in length. The structure of a sort entry is shown in the following diagram (14.1). The olink field for each text record holds the address of the start of the sort entry that corresponds to the text record which originally followed it in the text. Initially, the olink and slink fields will hold the same addresses. After sorting has completed, the olink fields will be unchanged but the slink fields will hold the address of the entry which should follow it in the newly determined sort order. The word buildlist is used to create the initial sort list and to fill in the fields for each sort entry in the list. The sort list is placed in the gap area, below the undo buffer in memory. The following diagram (14.2) shows the position of the sort list and the initial relationship between the entries in the sort list and the records in the text. The top sort list entry, which corresponds to the first record in the selected text, lies immediately below the beginning of the undo buffer. The address in the olink field for the top sort list entry points to the start of the next entry in the sort list. The olink field in the last sort list entry contains a zero. The address of the start of the top sort list entry is kept in the eosl system integer. The address of the start of the bottom sort list entry is kept in the bosl ('bottom-of-sort-list') integer. These addresses, which are set up initially by buildlist , remain valid during the entire sorting process because during sorting, the entries are not moved around, only the pointers in the slink fields are altered. To build the sort list, buildlist starts at the beginning of the selected text and moves forward, isolating records in the text. Each time a record is found, newnode is used to allocate gap memory for another sort list entry. In order for sorting to successfully complete, there must always be enough memory in the gap to hold the largest record in the text selection. newnode checks to make sure this memory requirement is met and aborts if it is not met. After newnode has allocated memory for the new entry, buildlist fills in all fields in the entry. Diagram 14.1 A Sort Table Entry Diagram 14.2 The Sort List Before and After Sorting _______________________________________________________________________________ Sorting the List _______________________________________________________________________________ The sorting routines can sort records into an ascending or descending order. If the flag in the descending system integer is true, the records will be arranged in a descending order. Otherwise, they will be sorted into an ascending order. The word quicksort is the main sorting word. If the selection contains more than 7 records, quicksort uses a recursive algorithm to sort the records. Selections which contain less than 7 records will be sorted with the simpler sorting algorithm used by the word selectionsort . The comparisons used by the sort routines are not strict ASCII string comparison algorithms. If a corresponding series of characters in two strings being compared contains digits, the series of characters will be converted to their numerical values and the two numerical values will be compared (as opposed to comparing the values of the ASCII codes for the strings of digits). This means that lists such as part lists, which usually have fields which contain strings with mixed alphabetic characters and digits, will sort into the expected order: Original List Expected Sorted Order Strict ASCII Sorted Order 7404 74LS14 7404 74LS274 74LS24 7406 74LS138 74LS138 74LS138 74LS24 74LS274 74LS14 74LS14 7404 74LS24 7406 7406 74LS274 The main comparison words used by the sorting routines are: $< , comparenumbers , and comparestrings . _______________________________________________________________________________ Shuffling the Records into Order _______________________________________________________________________________ After the sorting routines have positioned the slink pointers to reflect the sorted order for the records, the records must be "shuffled" into place. shuffle is the word which rearranges the text records according to the sorted order specified in the sort list. shuffle is passed the address of the start of the sort list entry which corresponds to the first record in the sorted order. Ideally, if enough memory is available in the gap, the entire sorted body of text records will be constructed in the gap area before being moved into the correct position in the text. shuffle will work its way through the linked sorted order list (which uses the slink field), and append a copy of each corresponding text record to the end of the sorted list being constructed in the gap. Otherwise, as many text records as will fit will be placed in the gap in the proper order, the text records in the text which have not yet been moved into the gap will be pushed up in memory towards the gap (any original records which have already been moved into the gap, or into their proper place in the text, will be overwritten during this operation since they are no longer needed), and the sorted records in the gap will be moved into the opening created (see the following diagram, 14.3). This process will be repeated as often as necessary. preshuffle and postshuffle are used to take care of format and display details. Diagram 14.3 The Location of the Sort List _______________________________________________________________________________ The Sort Command _______________________________________________________________________________ The main word executed when [USE FRONT] and [SORT] are pressed is sort . The definition of sort provides a good summary for this section: : sort ( - ) " SORT" 3 indicate rule ( Turn the "SORT" light on. ) presort ( Prepare the selection. ) selsize 1 < ( Make sure a valid sort selection ) bos nextbrk nextchar gap > ( still exists after presort . ) OR NOT if ( If it does... ) buildlist undolist to ( Build the initial sort list. ) bosl undolist quicksort ( Sort the list. ) undolist shuffle ( Shuffle the records into place. ) refresh ( Redraw the screen contents. ) widecursor ( Set cursor to wide cursor. ) forceop on then " " 3 indicate rule ; ( Turn "SORT" light off. ) _______________________________________________________________________________ Undo-ing the Sort Command Since shuffling is the first sort activity which actually alters the text, the undo operation is set only after shuffling has completed. The undo operation for sort is undosort . undosort goes through the sort list and swaps all olink pointers with the slink pointers and then uses shuffle to place the text records back in their original order. undosort also rehighlights the selection so the selection will be exactly as it was before the sort operation was started. : undosort ( - ) " SORT" 3 indicate rule ( Turn "SORT" light on. ) op bos to eosl swaplinks shuffle ( Swap all olinks with slinks . ) extend ( Extend the selection. ) ['] redosort undop to ( Set redosort as the undo ) ( operation. ) " " 3 indicate rule ; ( Turn "SORT" light off. ) _______________________________________________________________________________ Undo-ing the undosort Command The undo operation for undosort is redosort . redosort swaps the olinks and slinks again (so that the sorted order list uses the slink field again) and then shuffle to put the text back into its sorted order. _______________________________________________________________________________ Sort Routines Summary _______________________________________________________________________________ _______________________________________________________________________________ Sort Preparation Routines: buildlist ( - a ) buildlist builds the original sort list and sets up the bosl ('bottom-of-sort-list') and eosl ('end-of-sort-list', should be 'top-of-sort-list') system integers. newnode is used to allocate memory for each new list entry and to check for out of memory errors. nextrecord is used to isolate text records within the text selected for sorting. nextfield is used to find the key field in the record. Each entry built will have its slink , olink , recaddr , reclen , foffset , and flen fields initialized. The address of the first entry created is placed in the eosl integer and the address of the last entry placed in the eosl integer. Returns the address of the top of the last sort entry. findfield ( a - n ) Establishes the sorting column (key field) within the records about to be sorted. Looks backwards from the address of the end of selection, 'a', to find the first previous break, i.e. the position just before the start of the last record in the highlighted selection. Then, steps forward from the start of the last record keeping a count of how many fields in the last record are highlighted. Returns the column number, or field number, within the record which is to be sorted on. If the entire last record is highlighted, the first field in the record is the sort field. Otherwise, the last field highlighted will be the sort field for all records. newnode ( n a - a1 f ) Tries to allocate memory for a new sort entry for the sort list. newnode is passed the size 'n' of the largest text record encountered so far and the pointer 'a' to the start of the last record in the sort list being constructed. If there is not enough memory in the gap to accomodate both the new sort entry and the text for the largest record, newnode will abort with a "No room." error message. If newnode is being asked to allocate memory for the first sort list entry 'a' will be 0 and newnode will initialize the list pointer by positioning it at the first even address which is rsize (the size of a sort entry) bytes below the start of the undo buffer. Otherwise, newnode decrements the pointer address by rsize bytes, stores the address of the previous sort entry in the olink field of the entry just created, and returns the new pointer address (which is now the last entry in the list) and a flag which is true if the entry just created was the first sort entry or false if not. nextfield ( a1 - a2 ) Given an address 'a1' of a character in a text record, returns the address 'a2' of the end of the field in which the character resides. nextfield searches forward from 'a1' looking for the first occurrence of a tab character (tabs are field delimiters). The search progresses byte by byte through the record. If a skip character is encountered nextfield will skip across the gap and continue searching (couldn't this make for an extremely long search if no subsequent tabs exist?). nextrecord ( a1 - a2 ) Takes the address 'a1' of a character in the highlighted selection to be sorted and returns the address 'a2' of the end of the record to which the character belongs. A real end of record must have sortbreaks break characters in series. nextrecord uses nextbrk to find the next occurrence of a break character and nextchar to make sure the required number of break characters follow the first break character. The address of the last break character which delimits the record is returned. presort ( - ) Prepares the highlighted selection for sorting. Uses findfield to establish the sorting column. Makes sure that the selection does not include the beginning and ending document separator characters, does not include a trailing page break or document separator character, and that the selection does end with sortbreaks break character. If the selection does not end on a break character, it is extended to include the rest of the current line, including the break character at the end of the line. prevpkt? ( a - a1 ) Looks at the character immediately before the character located at address 'a' in the text. If the previous character is a break character with a format packet following, the address 'a1' of the start of the format packet is returned. Otherwise, the address 'a1' of the previous character is returned. _______________________________________________________________________________ Low Level Sort Routines: countlist ( a1 a2 - n ) Given the addresses of two 'a1' and 'a2' entries in the sort list, countlist will return the total number of sort entries 'n' between the two specified entries. getstring ( a1 - a2 n ) Given the address of a list entry, 'a1', returns the address 'a2' and length 'n' of the key field for the record to which the list entry corresponds. insertrec prevrec ( a1 - a2 ) Given the address 'a1' of a specified sort entry, prevrec returns either the address 'a2' of the entry whose sort position is immediately before the specified sort entry (the entry which points to the specified sort entry) or 0, if no entry which points to the specified sort entry can be found. quicksort scansublist selectionsort setpointer swaprecs _______________________________________________________________________________ Sort Comparison Routines: $< ( a1 a1' a2 a2' - f ) ('string-less-than') Compares the two strings which start at addresses 'a1' and 'a2' and end at address 'a1'' and 'a2''. The string is analyzed to determine whether a numerical or string comparison algorithm should be used. The flag will hold one of three values when completed: -1: String 2 is greater than string 1 0: The two strings are equal. 1: String 1 is greater than string 2. @digit ( a - c f ) ('fetch-digit') Extracts the next character from the string at address 'a' and returns it on the stack. If the character is a digit or decimal point, a the flag returned is true. checksigns ( a1 a2 - a1 a2 f1 f2 ) Checks to see if the numbers found in the strings located at 'a1' and 'a2' are negative. If the string 1 is a negative number (contains a '-' sign), 'f1' will be a true flag. Likewise, if string 2 is a negative number, 'f2' will be a true flag. The original addresses are left on the stack, untouched. comparestrings ( a1 a1' a2 a2' - a1 a2 f ) Compares the two strings which start at addresses 'a1' and 'a2' and end at address 'a1'' and 'a2''. The flag will hold one of three values when completed: -1: String 2 is greater than string 1 0: The two strings are equal. 1: String 1 is greater than string 2. comparenumbers ( a1 a1' a2 a2' - a1 a2 f ) Compares the two numbers held in strings which start at addresses 'a1' and 'a2' and end at address 'a1'' and 'a2''. The flag will hold one of three values when completed: -1: Number 2 is greater than number 1 0: The two numbers are equal. 1: Number 1 is greater than number 2. signed? ( a1 a1' a2 a2' - f ) signed? is passed the start addresses, 'a1' and 'a2', and end addresses, 'a1'' and 'a2'', of the two strings which are being compared. A true flag is returned if at least one of the strings is a signed number (digit preceded by a '+' or '-' sign) and if the other string is at least a number (a digit, comma, or decimal point). These are examples of signed numbers: -3 , -3az , +3 , +3az , +.abz , -,e . These are examples of numbers: 3 , 3xyz , 3333 , ,ac , .r . _______________________________________________________________________________ Shuffle Routines: largestrec ( a1 a2 - a2' ) Returns the address 'a2'' of the sort entry which corresponds to the text record which has the largest text record address (recaddr) that is still less than the text record address found in the sort entry found at address 'a2' (the sort entry at 'a2' acts as an upper limit for the search). Used by moveunsorted . moverecord ( f a1 a2 - a1' a2' ) Moves the text record corresponding to the sort list entry at address 'a2' to the destination address 'a1' (usually somewhere in the gap) and returns the address of the next entry in the sort list, 'a2'', and the properly incremented destination address, 'a1''. The flag 'f' is true if the text record should be moved below the destination address and false if the text is to be moved above (higher than) the destination address. The address of the new location of the text record is stored into the recaddr field in the text record's sort list entry. moverecords ( a1 a2 n a3 - ) Moves the text records of length 'n', located in memory at address 'a1' (at the gap , usually), into the text, starting at address 'a2' (usually right after the last break in the sort selection). The recaddr fields in the sort list entries which correspond to the records to be moved are updated with the new text addresses before the records are moved. moveunsorted ( a - ) moveunsorted is called when the gap has filled up during the shuffling process. moveunsorted is passed the address 'a' of the first of the sort entries whose corresponding text records have not yet been placed in their new sorted positions in the text. moveunsorted pushes all of these unpositioned text records together and up towards the gap. This allows shuffle to place any properly arranged text records which have accumulated in the gap into the resulting hole in the text in order to free up the gap area. postshuffle ( a - ) After a sorting operation, the op will be positioned on the last break character in the sort selection. If there was a format packet associated with this last break, if the workpkt area contains a saved format packet, the saved packet is placed back in the text. Marks the gap interval as completely changed, marks all intervals in the second partition of text as partially changed, recalculates the entire contents of the window table and redraws the display, resets the gap , bos , and eos , sets undosort as the undo operation, and marks the text as dirty. preshuffle ( a - ) First checks to make sure there is enough room for the shuffling operation. To shuffle the sorted list into place, there must be enough room in the gap to hold the largest text record in the sorted list (located at address 'a'). Then checks to see if there is a format packet associated with the last break in the selection. If there is, the format packet is copied into the workpkt scratch format area temporarily. shuffle ( a - ) Arranges the text records according to the sorted order specified in the sort list located at the address 'a'. If enough memory is available, the entire sorted body of text records will be constructed in the gap area before being moved into the correct position in the text. Otherwise, as many text records as will fit will be placed in the gap in the proper order, the text records in the text which have not yet been moved into the gap will be pushed up in memory towards the gap (any original records which have already been moved into the gap, or into their proper place in the text, will be overwritten during this operation since they are no longer needed), and the sorted records in the gap will be moved into the opening created. This process will be repeated as often as necessary. preshuffle and postshuffle are used to take care of format and display details. _______________________________________________________________________________ High Level Sort Routines: aSort ( - ) ('ascending-sort') Sets the descending to false, to cause an ascending sort, and calls sort . dSort ( - ) ('descending-sort') Sets the descending to true, to cause a descending sort, and calls sort . redosort ( - ) redosort is the word which "un-does" the effect of an undosort operation. Uses swaplinks to reverse the contents of the olink and slink fields in all sort entries in the sort list and then uses shuffle to rearrange the records in the text according to the new data in the sort list. The "Sort" indicator light is turned on before the redo operation starts and is turned off after the redo operation ends. shuffle sets the word undosort as the undo operation for redosort. sort ( - ) Sorts the records in the current highlighted selection in ascending or descending order (the flag in the descending system integer controls the sort order). Uses presort to adjust the boundaries of the selection as necessary before sorting and then uses buildlist , quicksort , and shuffle to perform the main sorting tasks. Turns the "Sort" indicator light on at the start and off at the end of the sorting operation. swaplinks ( a - a ) Given the address of the top of a sort list, 'a', swaplinks swaps the contents of olink and slink fields in each sort entry in the list. undosort ( - ) undosort is the word which "un-does" the effect of a sort operation. Uses swaplinks to reverse the contents of the olink and slink fields in all sort entries in the sort list and then uses shuffle to rearrange the records in the text according to the new data in the sort list. Re-highlights the selection (as it was just before sort was used). The "Sort" indicator light is turned on before the undo operation starts and is turned off after the undo operation ends. Sets the word redosort as the undo operation for itself. _______________________________________________________________________________ Sort Integers Summary _______________________________________________________________________________ bosl Holds address of the beginning sort list entry position (the sort list entry which is located lowest in memory). descending Holds a flag which controls the sort order. A true flag causes a descending sort and a false flag causes an ascending sort. eosl Holds address of ending sort entry position (the sort list entry which is located highest in memory). flen Offset to the 2 byte key field length field in a sort list entry. foffset Offset to the 2 byte field in a sort list entry which holds the offset within a text record to the start of the key field in the record. lip Lower insertion pointer. lpp Lower partition pointer olink Offset to the 4 byte original order link field in a sortlist entry. recaddr Offset to the 4 byte text record address field in a sortlist entry. reclen Offset to the 2 byte text record length field in a sortlist entry. rsize Holds the size of a sort list entry ($16). sortbreaks Holds the number of breaks used to separate records. sorttable Holds the address of the sort table. tab# Holds the field number of the key field within the current record type being sorted. ulink Offset to reverse order link (currently unused) field in a sortlist entry. upp Upper partition pointer. undolist Holds the address of the first record in the sorted order list. ________________________________________________________________________________ 15. Calc Command and Data Structures ________________________________________________________________________________ ________________________________________________________________________________ Modification History: First Draft PB 8/6/87 Edited 8/13/87 ________________________________________________________________________________ ________________________________________________________________________________ Overview: The Calc command is used to evaluate expressions in the text. The evaluated results are inserted in the text. The expressions used by the Calc command are stored in the text, but they are normally "hidden" from view in a data structure called a pocket. Pockets contain the source code for the expression, and a forth token. The forth token is executed by the forth interpreter to evaluate the expression. ________________________________________________________________________________ ________________________________________________________________________________ Table of Contents ________________________________________________________________________________ 2 Terms used in Calc 8 Structure of Calculations in the text 10 Structure of compiled expressions Executing the Calc command 12 Recalculation 15 Calc command logic 16 Pushing (Compiling expressions) 20 Recursive Descent example 22 Popping Arithmetic and functions 23 Arithmetic operators (+, -, *, /, %, Logical ops) Functions 24 abs, int, sqrt 25 Relative references and sums 26 Support for Erase, Copy, Document Lock, copy-up, getforward and receive 27 Error Handling 28 Layout of the Calc code ________________________________________________________________________________ Glossary of Terms Used in Calc There are a number of terms that will be used in the description of the calculation package. These terms are defined below and can be referred to as necessary. Answer field In a compiled expression, 12 bytes are reserved to contain the answer. During pass 2 of recalc, the first time a compiled expression is executed the answer is copied to the answer field. Then later executions of that expression don't actually execute the expression, they just use the already computed answer stored in the answer field. Arithmetic Stack Arithmetic is performed on the arithmetic stack rather than the forth data stack. The arithmetic stack uses 12 byte stack elements containing 11 bytes of BCD digits and a tag byte (as opposed to the 32 bit binary numbers used by the forth data stack). Attribute byte An attribute byte following a character indicates that that character will be displayed on the screen with some special characteristic. The attribute byte allows the description of three attributes: bolding, underlining, and dotted underlining. All answers and popped expressions used by calc have dotted underline attribute bytes. Answers and popped expressions may also be underlined or bolded. Autopush In order to make the calculation package easier to use, the user can pop up several expressions, edit them, and then give the calc command. All popped expressions will automatically be pushed. This is called autopush. Autopushing takes place in two parts: the edited expressions are compiled during pass 1 and the edited source code is hidden during pass 3. Bit flags Each compiled expression includes 5 bit flags in it's data structure which are used to store state information about the compiled expression and the result associated with it. The bit flags are described in detail in the section "Structure of compiled expressions". Calctoken Pockets in the text are easily identified because they always begin with a calctoken (stored as an E4 in text). Thus the three scan passes of calc can simply look for calctokens to find all the pockets in the text. Column The user interprets a number on the screen to be in a certain column depending on its location on the screen. The calc package determines the address in the text by counting tab characters starting at the previous return character. The user's interpretation and the calc package's interpretation match unless a tab field has overflowed (in which case a number that appears to be in a column is fewer tabs away from the previous return) or when each line in a table is not ended with a return (in which case a number that appears to be in a column is more tabs away from the previous return). While this correspondence between the user's view and the calc pacakge's interpretation is not always correct, in well-constructed cases (no overflow tab fields and returns at the end of each line) it works well. Compiling Compiling is the process of converting the expression that the user types into a compiled expression that can be executed by the forth interpreter. Dotted Underline Dotted underlines are an attribute that can be appiled to a character. All displayed characters associated with a result are dotted underlined. Element An entry on the arithmetic stack. Encoding The process of converting a normal byte into two hidden bytes in the text. Hidden bytes always have their four high-order bits set and the low nibble contains one nibble from the byte being encoded. When an expression is pushed the token of the compiled expression is encoded as is the source text of the expression. Execution During recalc, the token associated with each result is executed. The execution calculates the value for that result. Expression An arithmetic statement that can be compiled and executed to produce a result. Forth dictionary The data structure used by forth to store forth code that is ready to be executed. Hidden Text Text that has been encoded and is stored in the text. Hidden text is never displayed. Locked calctoken When a region of text is locked, all the calctokens in the region are converted to locked calctokens. Locked calctokens have the value E5, so the three recalc passes, which are looking for calctokens (value E4) do not find locked calctokens and as a result, these results are not recalculated. Named references When an expression is named, it can be referred to in other expressions by using it's name. NaN Not a Number. The value NaN is returned by any operation that has an overflow. Division by 0 is an example of an operation that will return NaN. If one of the inputs to an operator is NaN, the result returned by that operator will be a NaN. Thus, once a result is NaN, any other results that are dependent on that result (either by a named reference or a relative reference) will also return NaN. Displayed as >???.?? Number Formatting The calc package supports several types of number formatting: The number may be punctuated every third digit to the left of the decimal point, the decimal point may be displayed, and the precision (number of digits to the right of the decimal point) may be adjusted. In addition, the characters used for punctuation and decimal point can be adjusted to meet local customs: In the USA, the default is comma for punctuation and period for decimal point. In europe, it is common to use the opposite character assignments. The number of digits to the right of the decimal point is set initially using the Setup command. Once a result is generated, the user controls the precision by deleting characters or adding 0's at the end of a result. Operand Operands are the elements of an expression that are input values for operators. Operands may be literals (numbers), named references, or other expressions. Operators Operators are the elements of an expression that specify the calculation to be performed. Operators may be unary (take only one operand) like - and %, or they may be binary (take two operands) like +, -, *, /, and the logical operators. Orphans Answer digits that have become separated from their result because the user has edited the text. This can happen by dragging around a result or by deleting the pocket associated with a result (by deleting the first answer digit). Orphans are dotted underlined, but the next execution of recalc that ends normally (that is without error or interruption) will remove the dotted underlines, turning the orphans into ordinary text. Overflow The result of a calculation may be a number that is too large to be represented in the number system used by the calc package (that is the number may require more than 12 digits to the left of the decimal point). When an overflow is detected, it is represented by a NaN. NaN is represented in a number as a bit in the tag byte. Since overflows are the only cause of NaN's, the NaN bit in the tag byte is also referred to as the overflow bit. Passes The process of recalculation requires three scans through the entire text. These scans are called passes. An integer called pass stores the value of the current pass (either 1, 2, or 3) Placemarker The placemarker is a special result that is inserted into the text by precalc. It is used to remember the location of the gap so the cursor can be returned to the gap when calc finishes. Since calc can increase or decrease the size of the text (by pushing, popping, or computing answers with a different number of digits) the text pointers are not valid at the end of calc. Since the placemarker locates the gap, it can be used to adjust all the pointers to their appropriate values when calc finishes. Pocket The part of a result that is hidden (that is the part that is stored in the text as encoded bytes) is called the pocket. The pocket contains the encoded token and the encoded expression. Pointer field The pointer field is a part of the data structure in a compiled expression. Pass 1 fills in the pointer field with the address of the calctoken associated with the compiled expression. Popping Popping is the process of bringing a copy of the hidden expression into the visible text. A popped expression follows the last answer digit and is preceeded and followed by an underline character. The entire result from the first answer digit to the last underline character is dotted underlined. Once an expression is popped, it can be edited. The next recalc operation will automatically push any popped expressions. Precision The number of answer digits to the right of the decimal point. The precision can be adjusted from no digits to 10 digits to the right of the decimal point. The answer stored in the answer field of the compiled expression always has 10 digits to the right of the decimal point so calculations that depend on this result will always have the full precision available to them. Punctuation Numbers may be punctuated at every third digit to the left of the decimal point. The character used for this punctuation may be either a comma, a period, or an apostrophe. Pushing To convert an expression in the text into a result, the expression is highlighted and the Calc command given. This causes pushing, which compiles the expression, encodes the token and the expression, and produces the result. Recalc Recalc is the routine that performs the three passes through the text, calculating all expressions and updating all results. Redefinerror Redefinerror is a token that is used by copy up, getforward, and receive to indicate to recalc that the hidden expression associated with this pocket must be compiled during pass 1. It is used in cases where the compiled expression is not available (whenever the forth part of calc is separated from the text part, specifically the three cases mentioned) to recreate the compiled expression. Reference counts In order to permit forward references, the compiled part of all named expressions contains a count of how many times that name is referred to in the text. If the reference count ever reaches 0, the name, token, and compiled expression can be recycled. Relative Reference An expression can refer to a number located in the text using a relative reference. The row and column of the number being referred to is calculated relative to the row and column of the expression. The user specifies the relative address in the expression. Result The in-text data structure generated by calc. A result consists of answer digits and a pocket. Row The user interprets a number on the screen to be in a certain row depending on its location on the screen. The calc package determines the address in the text by scanning for return characters. Both the sum and relative address operators in the calc package make use of the concept of rows. Page characters and document characters terminate row counting. For example, when sum encounters a page character, it quits adding new elements to the sum, even if there are more numbers just before the page character. If each line in a table does not end with a return, the user's interpretation of a table's structure will not correspond with the calc package's interpretation, as was mentioned in the description of column. Skip markers Markers located at the beginning and end of the gap. The markers start with the value "E0" and are followed by a 3 byte offset that is the number of bytes to the next character in the text. Described in greater detail in Chapter 1. Token Forth is token threaded and executes tokens. The token refers to code via one level of indirection through the token table, a table of addresses that relate tokens to code addresses. The calculation package uses the forth data structures to calculate answers. Each result has an associated token which binds the result to its corresponding compiled (and hence executable) expression via the token table. uNaN Undefined Not a Number. uNaN is simlar to NaN in that it propogates to subsequent calculations, but instead of being generated by an overflow, it is generated by an unresolved forward reference. uNaN is represented in a number by a bit in the tag byte called the uNaN bit. It is displayed as ????.??. ________________________________________________________________________________ Structure of Calculations in the Text ________________________________________________________________________________ Although calculation results appear on the screen in a manner that is similar to ordinary text, the structure of calculations is very different. The first difference is that the calculation data structure is stored in two places: the compiled code that produces the result is stored in the Forth dictionary while the answer and source expression are stored in the text. The data structure in the text is called the result. The data structure in the Forth dictionary is called the compiled expression and is uniquely identified by the token. A result has a rather complicated structure. It has four main components, (1) the first answer digit, (2) the token, (3) the hidden expression, and (4) the trailing answer digits. The first digit is the only component that is always visible (an answer always has at least 1 digit). The token is executed to calculate the answer. The expression is kept in text so that it can be reviewed or modified. The trailing digits are the remaining digits in the answer. Thus, the hidden information (the token and the hidden expression) is stored in the text between the first digit of the answer and the remaining answer digits. The hidden information is called a pocket. Thus a result is stored in text as follows: +---------------------+--------------------------+--------------+-------------- | digit | token | expression | digits +---------------------+--------------------------+--------------+-------------- <------ the pocket -------------> Each of these main components have smaller components: +---+------+----------+ | # | attr | [accent] | +---+------+----------+ digit +-----------+---+---+---+--+ | calctoken | encodedtoken | +-----------+---+---+---+--+ token +---+- - - -+--+ | hidden chars | +---+- - - -+--+ expression +---+------+----------+ | # | attr | [accent] | +---+------+----------+ digits A digit is composed of two (and sometimes three bytes if the user has added accented characters). The first byte is the ASCII code for the visible number. The second byte is an attribute code, with a value between hex EC and EF, indicating which type of emphasis, along with dotted underlining, is applied to the character in the display. Dotted underlining is displayed on the screen but not printed. A third byte is associated with the character if it is accented. +----+------+--------+ | 34 | EC | (C6) | +----+------+--------+ digit The token component is five bytes long, beginning with a unique byte value E4, called a calctoken. The first byte in a pocket is always a calctoken, so pockets can be found in the text by searching for calctokens. The next four bytes contain a two byte token encoded so that all the high nybbles have the hex value F. For example the token 041B is stored as the encoded value F0F4F1FB. This encoding hides the token and prevents LEAP from landing on it. In locked documents, the calctoken is replaced with the value locked calctoken (which has the unique value E5). The locked calctoken won't be found when searching for calctokens, so pockets preceeded by a locked calctoken won't be executed. +------------+---+--+--+---+ | E4 | F0 F4 F1 FB | +------------+---+--+--+---+ token When an expression is pushed, a copy of the source text is hidden by encoding it in the same way as is the token. Each byte is divided into nybbles, and each nybble stored in the low nybble of a byte with the high nybble of hex value F. +---+- - - -+--+ | hidden chars | +---+- - - -+--+ expression The trailing digits are constructed in exactly the same manner as the first digit. The pocket is attached to the first digit, so when the first digit is erased or moved the pocket goes with it. The trailing digits can be separated from the first one and in that condition are called orphans. +----+----+------ | 37 | EC | (C6) +----+----+------ digits The placemarker structure is a result consisting of a single pound sign (#) with no trailing digits and hidden expression consisting of two question marks (??). The placemarker is used to mark the location of the gap during the execution of recalc. +----+------+-----------+---+--+--+---+---+--+--+---+ | 23 | EC | E4 | F0 F2 F3 F6 | F3 FF F3 FF | +----+------+-----------+---+--+--+---+---+--+--+---+ digit calctoken hidden token hidden expression = ?? ________________________________________________________________________________ Structure of Compiled Expressions ________________________________________________________________________________ When an expression is pushed, Forth code is generated, the execution of which computes the answer, which is placed into the text. All Forth words begin with a jump to the nest subroutine, which is compiled as "$4ED3", a machine code instruction that performs an indirect jump through register A3. The function of nest is to begin interpretation of the Forth word, starting at the next token in the word. checkanswer is the first word executed by all compiled expressions and is followed by several data structures. These data structures hold information which improve calculation speed, and minimize usage of memory. +-------------+-----------------+---------+--------+ | checkanswer | bitflags & refs | pointer | answer | +-------------+-----------------+---------+--------+ 2 bytes 2 4 12 There are 5 bitflags: answered, popped, autohide, comma, and discrepancy. The answered flag is set when the answer field (see below) is valid. The popped flag is set when a copy of the hidden expression is displayed. The autohide flag is set by autopush once the surface expression has been successfully compiled. The comma flag is set when the result is to have commas to mark the thousands places. The discrepancy flag is set when autopush successfully compiles and cleared during pass 3. This bit is used by the next execution of recalc to force recompilation of the pocket in case pass 3 was not completed due to an error or user interrupt. +----------+--------+----------+-------+-------------+-----------+ | answered | popped | autohide | comma | discrepancy | ref count | +----------+--------+----------+-------+-------------+-----------+ value: 80 40 20 10 8 7 - 0 The remaining 11 bits (low 3 bits in this byte and 8 bits in the next byte) are only used if the definition is named. They are for the number of named (as opposed to relative addressed) references to this definition (2,047 maximum - 07ff hex). The reference count is used to determine how many other expressions refer to this expression. The use of the reference count is discussed below in connection with "forwarderror". During pass1 the pointer field is filled with the location in the text of the calctoken within the result. It is used by sum and relative addressing to extract appropriate values from the surrounding text. The answer field (which is identical in size and structure to elements on the arithmetic stack) begins with a one byte tag field, which includes the sign bit, undefined bit, and overflow bit. The remaining 11 bytes hold the answer. +------+------+-----+ | sign | uNaN | NaN | +------+------+-----+ value: 80 40 20 The rest of the compiled expression comes after the answer field. checkanswer advances the ip around the data structure so execution will commence with the rest of the compiled expresson. The token for "placeanswer" terminates all pushed definitions; its function is similar to ";" in normal forth definitions. The section "Executing the Calc command -- Pushing" describes the contents of compiled expressions in more detail. ________________________________________________________________________________ Executing the Calc command -- Recalculation ________________________________________________________________________________ Recalculation occurs when the Calc command is given and there is nothing to push (either nothing is selected or what is selected contains only popped pockets) or when all pushing is completed. Recalculation is performed by the word recalc. The basic function of recalc is to scan the text three times locating all the calctokens in the text, executing the associated compiled expressions and updating all answers in the text. When recalc begins, the location of the gap is indicated by the blinker, with all text pointers set correctly. The execution of recalc performs its function in the following order: ________________________________________________________________________________ 1 - Compress the gap -- precalc In normal text operations, the gap begins and ends with a special byte sequence called the skip marker. Since recalc scans the text three times, any efficiency matters a great deal. To make scanning faster, before the first scan, the placemarker is placed at the end of the gap, overwriting the skip marker. Then all the text before the gap is moved up against the placemarker. Thus the gap is compressed to 0 and both skip markers are removed. Since there is now no gap in the text, scanning the text for calctokens consists of simply looking at the next byte to see if it is a calctoken; there is no need to take the gap or its associated skipmarkers into account. The placemarker will be executed during all passes of recalc, however, it doesn't do anything except during the third pass, when it removes itself and stores its position in the integer "marker". Two other optimizations are made to make scanning the text faster. First, since a calctoken is always associated with an encoded token (which takes 4 bytes to store in the text) and at least one expression byte (encoded as two hidden bytes), scanning the text can proceed by checking every 6th character to see if it is >EF and then, if it is, determine whether it is part of a result by scanning backwards for the calctoken. This makes it faster to scan the text since not as many bytes must be examined. This speedup is only used during pass 1 and pass 2. During pass 3 each byte is scanned so that orphans can be turned into plain text. The other optimization is that a special pocket is located after the end of the text. Every time a calctoken is encountered, a test is made to determine whether it is beyond the end of text (eot). If it is, the scan is terminated. ________________________________________________________________________________ 2 - Recalc Pass 1 -- ascan1 During the first scan the pointer fields in each compiled expression are filled in with the location of the calctoken in the text. This pointer value will be used during pass 2 to evaluate sums and relative expressions. During pass 1 the answer bit flag in each expression is cleared. If the expression was popped (the poppedflag was set), the surface expression is compared to the hidden expression and if they are different, autopush is called. If autopush isn't called, the rest of the compiled expression is skipped. Autopush, compiles the surface expression, replacing the old compiled expression with the new one, and executes the new compiled expression. Autopush also sets the autohide and discrepancy bits. The newly compiled expression affects the forth area not the text. ________________________________________________________________________________ 3 - Recalc Pass2 -- ascan2 During the second scan, each calctoken is executed and placeanswer copies the result of the execution from the arithmetic stack into the answer field and sets the answer bit. Since the compiled expressions are simply forth code which is executed, executing a token will in turn execute all tokens used in that word. Thus a commonly used name could be executed many times during pass2. The first time a token is executed, the answer bit is set. The second time it is called, it isn't executed; rather, checkanswer copies the answer from the answer field into the arithmetic stack and skips the rest of the compiled expression. During this pass, one function of checkanswer is to push the address contained in the pointer field onto the return stack. Placeanswer Sums and relative references use the pointer field which locates the pocket in the text. For those named expressions that contain a sum or a relative reference and are themselves refered to only by name, there is no other means of knowing their location in the text. Since pass 2 doesn't insert or delete anything in the text, the pointer field is valid during all of pass 2. ________________________________________________________________________________ 4 - Recalc Pass 3 -- tscan and textify During the third scan all the answers are updated. Several kinds of information are incorporated into the answers extracted from the answer field. The result may have been edited, popped, orphaned, or moved up against another result (by dragging, copying, or deleting). All of this information is taken into account when updating an answer. The number of digits to the right of the decimal point can be decreased all the way to zero (including and even limited to the removal of the decimal point). A decimal point can be added. Zeroes can be added to the right of a decimal point. The new precision is determined by scanning for a decimal point (dotted underlined or not) and then counting how many digits (including only dotted underlined digits or zeroes) there are to the right of it. Commas can be added anywhere in an answer (although to count, it must have been inserted after the first digit and before the last one). This comma is removed and one is inserted to indicate the thousands, millions, and trillions places, if they exist. The commabit is set, so that even if there aren't enough digits to the left of the decimal point during this recalc, if there are enough later on, commas will automatically be inserted. If the autohide bit is set, the hidden expression is discarded and replaced by surface expression. All surface expressions are discarded. An orphaned result is merely one in which the first digit (and its associated pocket) has been removed (either by dragging, deleting, or copying). In these cases the attribute characters are simply removed from the digits. If two results are contiguous, pass 3 will insert a tab character. Unfortunately, if the expression associated with the result on the right uses a sum or a relative reference, pass 2 will have used an invalid location in the text and generated an erroneous answer. Because of this, (only for versions 2.00 and later) a flag is set and after pass 3 is done, recalc is begun again. When the placemarker is executed during pass 1 or 2, it does nothing. During pass 3, it removes itself from the text and stores its location into the integer "marker"; this will be the new location for the cursor. ________________________________________________________________________________ 5 - Uncompressing the gap -- aftercalc and showcalc Once pass three is done, the location of the new cursor is in the integer marker. The text before this location is shifted back down to the beginning of the text area. The structures which support display of text (the interval and window tables) are updated and the text displayed on the screen. ________________________________________________________________________________ 6 - Interrupting recalc (manually or through error handling) If recalc is interrupted or an error occurs during any of the passes, special routines restore the text to a displayable condition. ________________________________________________________________________________ 7 - Inserting tabs causes another recalc (not available until version 2.00) ________________________________________________________________________________ Executing the Calc command -- Calc command logic When the user gives the Calc command (UF-G), exactly what happens depends on the location and state of the cursor in the text. The Calc command logic decides which operation (pushing, popping, or recalc) to perform. The structure of the decisions performed by the calc command logic is as follows: Highlight Extended? Yes: Does the highlight contain a result? Yes: is everything popped? Yes: recalc No: multipop No: does the highlight contain dotted underlines? Yes: push redefinition, recalc No: insert tab, push (new expression), recalc No: Is the cursor on a result? Yes: is that result already popped? Yes: recalc No: pop No: recalc The highest level of the Calc command is the word Calc. The word getselect figures out whether the highlight is extended and if it is, sets pointers into the selection. If the highlight is extended, the word push|multipop is executed which handles the highest Yes clause above. If the highlight is not extended, the word pop|recalc is executed which handles the highest No clause above. ________________________________________________________________________________ Executing the Calc command -- Pushing The operation of compiling an expression in the text into a result (an answer plus a pocket) is called pushing. When the calc logic will determine that pushing is needed when the highlight is extended and there are no calctokens in the highlighted text. The highlight may contain one or more expressions. If the highlight contains more than one expression, the individual expressions must be separated by separator characters: tab, return, page or doc. As each expression is identified, that expression is pushed and its answer displayed, resulting in a display that is "animated" showing the progress of pushing. Pushing consists of several parts: 1 - Scanning the highlighted text -- parser The highlighted text is scanned looking for items to be compiled. Separator characters are skipped, and individual items that can be compiled (literals, names, :, the operators, and the functions) are identified and passed to the recursive descent compiler. 2 - Compiling the code The recursive descent compiler takes the items that were identified by the parser and generates the actual forth code for the compiled expression. A recursive descent compiler is used because the system supports operator precedence. The operator precedence is described below, and the next section of this manual describes the operation of a recursive descent compiler. When the recursive descent compiler completes compiling an expression, it checks next item produced by the parser. If the parser has reached the end of the highlight or is on a separator character, then the expression has compiled correctly. The compilation completes (by putting the placeanswer code at the end of the compiled expression) and inserts the dummy result. 3 - Inserting a dummy result Once the expression is compiled, a dummy result in inserted in the text. This result contains the encoded token and expression corresponding to the just-compiled expression, but the answer is set to 0 (in the default case 0.00 since the default precision is 2 digits). The dummy result is not displayed. 4 - Computing the immediate result -- immediacy Once the dummy result has been inserted in the text, the immediate value of the result is calculated and displayed. This is done by inserting a placemarker (serves the same purpose as the stop marker that is used to terminate the recalc passes through the entire text) in the gap right after the dummy result and then setting pass to 2 and executing ascan2, followed by setting pass to 3 and executing textify (which inserts the new result into the text). Finally, appropriate interval table and window table operations are performed to permit display of the intermediate result. This is what causes multipush to "animate" the display: each intermediate result is displayed as multipush pushes a pocket. ________________________________________________________________________________ 5 - Executing recalc when pushing is complete When all pushing is complete, recalc is begun, which updates all values in the entire text as well as making sure that the immediate values calculated for the just-pushed expressions are correct. Operator precedence When compiling, the operators are not exectued in left-to-right order, rather they are executed according to the operator precedence described below: highest real number, named reference parenthetical expression negative (and positive) logical negate percent exponentiation multiplication, division addition, subtraction logical operations (excluding negate) lowest The next section describes the actual code that is compiled for various expressions and parts of expressions that the user may enter. Literals A single value operand is compiled as an inline literal following the token for alit (arithmetic literal). For a comprehensive example, an expression containing only the constant 50 will look like: +-------------+------+------+---------------------------+-------------+ | checkanswer | data | alit | tag0000000000500000000000 | placeanswer | +-------------+------+------+---------------------------+-------------+ Names Named references are compiled as the token of the name found in the arithmetic vocabulary. Such named references can be used as an operand in further computations or returned as the answer. +-------+ . . . | token | . . . +-------+ Operators Most operators remove two operands from the stack. They are compiled in reverse polish order (like an HP calculator) following two operands (or equivalent). The result can be used as an operand in further computations. +---------+---------+----------+ . . . | operand | operand | operator | . . . +---------+---------+----------+ ________________________________________________________________________________ Sums sum (or avg), which use the pointer field, compile r@ and (or ). r@ places the address of the flag bits byte onto the stack. uses this address to fetch the pointer field, which serves as the starting point for adding vertically. The result returned by can be used as an operand in further computations. +----+-------+ . . . | r@ | | . . . +----+-------+ Relative addressing Relative addressing uses two single byte literals to specify the relative position of the value to be extracted from the text. The vertical coordinate is placed on the stack first. The result returned by can be used as an operand in further computations. +------+---+------+---+----+-------+ . . . | blit | y | blit | x | r@ | | . . . +------+---+------+---+----+-------+ Forward references Forward references are references to names whose expressions do not exist. They can be created either by using a name in an expression before the name is defined (by pushing an expression with that name), or by deleting a named pocket to which other expressions refer. When pushing an expression, if a named operand doesn't yet exist (a forward reference), a dummy definition is created whose token is compiled into the new expression's code. The dummy definition is assigned the non-existent operand's name and consists of the token for "forwarderror" followed by a 2 byte reference field (initialized with 1 reference). Since no other information is required, this terminates the definition. A dummy definition returns the value uNaN (displayed in the text as "????.??") to all expressions which refer to it. An encoded token in the text never points directly to a dummy definition. +--------------+------------+ | forwarderror | references | +--------------+------------+ Other expression may also use the same non-existent name. When this happens, the same token (for the dummy definition) is used and the reference count in the dummy definition is incremented by 1. Later, the user may create a definition for the non-existent name. When this happens, a normal compiled expression (beginning with checkanswer, containing the data structure, code, and ending with placeanswer) is compiled and the reference field from the dummy definition is copied into the new compiled expression and incremented by 1 (a named pocket uses its compiled expression at least once). The same token is used to point to this new code and the dummy definition is removed. All previously compiled references to the dummy definition now automatically refer to the newly compiled expression, thereby resolving the forward reference. When a named pocket is deleted, its reference count is decremented by one. If there are no references to it, the referenced count will be zero, in which case the compiled expression, name, and token are recovered. If the reference count isn't zero, there are still references to that name. In this case the code is recovered but the name and token are preserved and a dummy definition is created and the reference count is copied to it. ________________________________________________________________________________ Executing the Calc command -- Pushing -- Recursive Descent example The following is an example of a recursive descent compiler that works with a syntax that is similar to the syntax of the Cat calc command. It is simplified to reduce the size of the example. For this example, the precedence is: () (parentheses), numbers, variables, and unary + and - ^ (exponentiation) * or / + or - & or @ (and or or) ::= means "is defined as" | means "or" as in separating choices ... means "repeat this definition zero or more times but with a loop" means "nnn is defined here" other characters are literals num is a sequence of digits var is a variable ::= & ... | @ ... | ::= + ... | - ... | ::= * ... | / ... | ::= ^ ... | ( if you allow 2^3^5, otherwise ::= ^ ) ::= + | - | ::= num | var | ( ) So, to compile: 3+4*5 Begin by examining the 3, and begin at the top of the first routine. log calls fac fac calls pro pro calls exp exp calls val val says first item isn't + or - so calls trm trm says first item is a number. It removes the number from the input stream and compiles the 3. Then it returns to val. val returns to exp exp says next item isn't ^ so returns to pro pro says next item isn't * or / so returns to fac fac says item is + so it remembers the operator and calls pro again. pro calls exp exp calls val val says current item isn't + or - so it calls trm trm recognizes the number so it compiles the 4 and returns val returns to exp exp says next item isn't ^ so returns to pro pro recognizes the * so it remembers it and calls exp again exp calls val val doesn't recognizes + or - so it calls trm trm recognizes the number so compiles the 5 and returns to val val returns to exp exp doesn't see ^ so returns to pro pro has finished one part of definition so compiles * and doesn't recognize another * or / so returns to fac fac has finished one part of definition so compiles + and doesn't recognize another + or - so returns to log. log doesn't see & or @ so returns to who called it. main says that the input stream is empty so exp was successfully compiled. (if anything left, the expression was ill formed). We compiled 3 4 5 * + (which is the correct RPN form of the expression). Now, to write one of these words is very simple. Take pro for example: pro ::= exp * exp ... | exp / exp ... | exp All choices start with exp so it knows it must call exp. Then it checks the input stream for either a * or /. If found it is working on either choice 1 or 2. If not, it was choice 3 and it is complete, just return. If it was choice 1 or 2, remember the operator and call exp again. After it has returned compile the operator and check for a * or / again. If not the choice is complete so return. Otherwise do again. So, the word looks like: : pro ( -- ) local operator ( a place to hold the operator ) exp ( all choices start with this ) begin ( a way to do ... ) item dup ascii * = swap ascii / = or ( is current item * or /? ) while item operator to ( if so, save it ) parsenext ( and remove it from input stream ) exp ( if it was then call exp again ) operator ascii * = ( now compile appropriate operation ) if [compile] f* else [compile] f/ then again ; Executing the Calc command -- Popping When the calc logic determines that one or more pockets must be popped, it pops the pockets one at a time, thereby "animating" popping in a manner similar to the animation produced by multipush. Popping consists of three parts: 1 - Move the gap to to the end of a result First the gap is moved to the end (that is one character after) the last answer digit of the result to be popped. This makes it possible to use the gap as a work area to insert the popped expression. 2 - Pop the expression into the gap Next the hidden expression is unhidden and moved to the gap as a sequence of dotted underlined characters. This operation takes place in several steps. First the gap is checked to make sure that there is enough room for the popped expression. Next the leading underline character is inserted into the gap. Then the dotted underlined expression is inserted into the gap. Finally a trailing underline character is inserted into the gap. 3 - Display the popped result The interval table and window table are updated as required and the newly popped expression is displayed. When the display of the popped result is completed, the next result to be popped is searched for. Thus, each result is popped individually, resulting in an animated display. Arithmetic and Functions -- arithmetic operators The arithmetic performed by the calculation package is fixed point BCD with 12 digits to the left of the decimal point and 10 digits to the right. Arithmetic is performed on the arithmetic stack using special arithmetic operators and functions, as opposed to using the forth data stack and the normal forth arithmetic operators and functions. Thus when an expression is compiled, the forth words used in the compiled expression are arithemtic words rather than the ususal forth words. This section describes the arithmetic words The Arithmetic Stack The first set of words to be described are the words that manipulate the arithmetic stack. These words are similar to the words that manipulate the forth data stack, but since they operate on the arithmetic stack, all words are preceded by the letter a. The words are: alast, adrop, anew, NaN, uNaN, adup, aswap, aover, arot The words are mostly familiar. alast prepares the stack for a new entry by adjusting the stack pointer. anew clears the arithmetic stack. NaN and uNaN put those values on the top of the arithmetic stack. The Arithmetic Operators The next set of words are the acutal operators used to perform operations on the arithmetic stack. The words are: a-, a+ These words perform subtraction and addition. They consume two stack entries and produce one entry (the result). aneg Negates the top stack entry. a*, a/ Multiplies or divides the top two stack entries. The consume two stack entries and produce one entry (the result). a% Multiplies the top stack entry by .01. a<, a>, a=, a~, a|, and a& Used to perform the logical operations provided by the calculation package. In addition to these words, there are a variety of support words used to implement these words. All words associated with arithmetic and the arithmetic stack are located on pages 1 to 12 of the Disk C side 1 listing. Arithmetic and functions -- functions When the user specifies a function in an expression, the following words are compiled into compiled expressions: aabs Takes the absolute value of the top stack entry. aint Zeroes the fractional part of the top stack entry. asqrt Takes the square root of the top stack entry. These functions are compiled by words that appear in the function vocabulary. The compiling words in the function vocabulary are the same as what the user uses in the expression: abs for absolute value, int for integer value, and sqrt for square root. The recursive descent compiler detects these function names and compiles the appropriate token from the above list into the compiled expression. Arithmetic and Functions -- Relative References When the user specifies a relative reference or sum (or average) in an expression, the following words are compiled into the compiled expression: , , Expects an address on the stack. This is the location of the calctoken (in the text) of the result containing the sum or average. , Also expects an address on the stact which points to the calctoken (in the text) of the result containing the relative reference. In addition, the stack contains numbers that are the x and y offsets referred to by the relative reference. All five routines return a value on the arithmetic stack. If the referred cell does not contain a number, NaN is returned, otherwise the number is the result of the selected calculation. These functions are compiled by words that appear in the function vocabulary. The recursive descent compiler detects these function names and compiles the appropriate token from the above list into the compiled expression. Support for Erase, Copy, Document Lock, copy-up, getforward, and receive Other commands in the system may sometimes have to handle results. This section describes the associated support routines provided by the calc package. Erase When a result is erased the reference counts in the dependent expressions must be reduced accordingly. Since erasing can be undone, this adjustment is divided into two pieces. During Erase, a linked list of the tokens associated with the results being erased is created (performed by linkcalc). The next use of the Calc command descends the linked list and adjusts the reference counts of the affected words in the remainder of the system, and recovers the forth dictionary space formerly occupied by the erased results (performed by removecalcs). If the erasing is undone, the results in the undo buffer are removed from the linked list (performed by unlinkcalc). The results from several consecutive erasures can be accumulated in the linked list. Copy When the Copy command detects a calctoken (value E4) it calls copypocket, which copies the result in one of three ways. If the result hasn't been popped the answer is copied as plain text (the pocket and the dotted underlines are stripped from the new copy). If the result is popped, the result is copied "active" in one of two ways. If the expression is not named, the entire result is copied unchanged except that the token is changed to "redefinerror". If the expression is named, the encoded token is changed to "redefinerror" and only the name of the expression is copied (the rest of the expression is discarded, including the colon). The task of redefinerrror is either to compile the expression from the surface text or, if one doesn't exist there, from the hidden expression. Document Lock When a document is locked, any calctokens in the document (bytes whose value is E4) are changed to locked calctokens (value E5). When the document is unlocked, the reverse is done. Since this operation is so simple no special support is provided by the calc package. copy-up, getforward and receive In all of these cases, the text containing results is separated from the associated compiled expressions (in the forth dictionary). The encoded token is changed to "redefinerror" (explained above under Copy). Error Handling Since recalc compresses the text while it is executing, the text is in an abnormal state and cannot be used by the editor. If an error is detected while recalc is executing, the text must be returned to its normal state before control is returned to the editor. On the other hand, if an error is detected while compiling, the forth dictionary is in an abnormal state and must be returned to its normal state before control is returned to the editor. Both types of errors set a variable aerror# to a value to indicate which type of error was detected and prepare an explain message. To aid in the debugging process, the error numbers have the following interpretations: # Meaning Word Non-aborting errors 30 element is too large or too precise , 31 sumcount won't convert to arithmetic stack aborting errors 29 running out of text space tscan, recalc, pushpocket 33 missing operator (add op char to op+tokens) compileop 34 number too large or too precise to compile allot# 35 too many parentheses numerical 36 out of dictionary room numerical 37 number syntax error numerical 38 more than 2 or less than 1 coordinate relative 39 no delimiter getphrase 40 naming collision with a copied up expression redefinerror 44 stack underflow or overflow ainterpret 45 can't find " 46 out of tokens " 47 name is too long acreate 48 accented character in expression ainterpret 49 name already exists acreate 50 attempt to use a reserved (function) name acreate 51 syntax error? clause 52 attempt to push result compileop 54 no opening paren in use get( 55 closing without opening parenthesis buildbody Layout of the Calc Code Disk A Side 0 Page 3 Functions Disk C side 1 Pages 1-12 Arithmetic code " Pages 13-24 recalc, checkanswer, placeanswer " Pages 24-25 Recycling tokens, remove-word " Page 26 Error handling " Pages 26-36 Compiling " Pages 37-48 Calc command logic " Pages 48-52 Relative references " Pages 53-55 Support of Erase, Copy, Copy-up, etc. ________________________________________________________________________________ 16. Spell Check Leap/ Add Spelling/ Delete Spelling ________________________________________________________________________________ ________________________________________________________________________________ Modification History: First Draft PB 8/13/87 ________________________________________________________________________________ ________________________________________________________________________________ Overview: Canon has supplied a block of code that supports the spelling verification commands. This document describes the interface routines used with Canon's spelling code. ________________________________________________________________________________ ________________________________________________________________________________ Table of Contents ________________________________________________________________________________ 2 Spell Check Leap 3 Add/Delete Spelling 4 Spellcode Interface ________________________________________________________________________________ Spell Check Leap ________________________________________________________________________________ The Spell Check Leap function is performed by two words which are called by the Leap code. The word spellcheckleap is used when the user has just pressed the SCL key. The word spellcheckleapagain is called when the user presses leap again. These two words check which leap key is pressed to determine which direction to check for misspelled words. If the leap forward key is pressed, the scan starts at the first word after the gap, loops around the end of text to the beginning of text, and ends by checking the word at the gap. If the leap backward key is pressed, the scan starts by checking the word at the gap and then scans backward, looping around to the end of text and ending at the first word after the gap. If no misspelled words are found, the gap is not moved. The scanning is accomplished by using the code words nextsep, nextsep?, prevsep, nextnosep, and prevnosep. For example, when scanning forward, the pattern of calls is this: find the end of the word containing the cursor (if any) by using nextsep? Then loop finding the beginning of the next word with nextnosep and then the end of the word with nextsep. Once the beginning and end of the word have been found, use the word translate to convert the word from the Cat character set to the spelling checker character set. Finally, call spellcheck which uses the Canon supplied code and dictionary to determine whether the word exists. If the word doesn't exist, it is scanned for hypens. If any are found, each hypenated part is passed to the spelling checker. If all the parts are found, the word scanning continues, otherwise, the word is considered misspelled. Misspelled words are displayed by placing the cursor on the first character (using movegap) and updating the screen. op is adjusted so that pressing both leap keys will highlight the misspelled word. There are two tables used by the spelling code. The table spellchars is used by the scanning words (nextsep, etc.) to identify separator characters (which appear as an ff in the table) and valid characters (any other value). spellchars is also used by translate to convert the cat character codes to character codes that can be used by the spelling checker. This is done by a two step process. First the character being translated is used as an index into spellchars. If the corresponding byte in spellchars contains a 0, the character is discarded and translation proceeds. If the byte contains a 1, further translation is required. Finally, if the byte contains any other value, that value is the translated value of the character. If the byte in the spellchars table contains a 1, then a second table, spellaccents is used. This table allows the code to take the two-byte sequence used by the Cat for accented characters into the 1 byte code used by the spelling checker. The gap may break the word located at the gap into two pieces. The character at the gap is spellchecked at either the beginning or end of the scan, depending on which way the leap is proceeding. When the word at the gap is tranlsated, a special word, translategap is used. ________________________________________________________________________________ Add/Delete Spelling ________________________________________________________________________________ Add/Delete spelling commands scan the text using the same scanning words used by Spell Check Leap. The high-level words for these two commands are addspelling and deletespelling. When the command begins, if the highlight is extended, it is increased in size so that entire words are always added to the dictionary. Then the word translate is used to translate each word in the highlight. When the word is translated, it is spellchecked. The result of the spellcheck is used to avoid adding words that are in the ROM dictionary to the RAM dictionary. Also, words that do not exist do not need to be deleted. Thus the following tests are used: spellcheck result: exists doesn't exist addspelling: noop addspell deletespelling: deletespell noop Add Spelling and Delete Spelling can be undone. The undop for one is the other (that is the undo for add spelling is delete spelling). ________________________________________________________________________________ Spellcode Interface ________________________________________________________________________________ Canon has supplied a block of code to access the spelling dictionary. The code is simply copied into the ROM and there are 5 interface routines that make it easy to execute the various spelling routines from forth. The interface routines are: spellcheck spellcode+2 addspell spellcode+6 deletespell spellcode+a initdictionary spellcode+e emptycheck spellcode+12 These routines use several data structures: svram The address of the SV RAM. svrom0 The address of the SV ROM. svbuf The address of a 64 byte RAM buffer used to pass the word to be spellchecked. svwork The address of a 256 byte RAM buffer used by the spelling code as a work area. ________________________________________________________________________________ 17. Explain Command ________________________________________________________________________________ ________________________________________________________________________________ Modification History: First Draft PB 8/13/87 ________________________________________________________________________________ ________________________________________________________________________________ Overview: The explain command is used to provide an on-line manual and to explain the meaning of error beeps. The code operates by displaying an explain message when invoked. When the UF key is released, normal editor operation resumes. ________________________________________________________________________________ ________________________________________________________________________________ Table of Contents ________________________________________________________________________________ 2 Explain Command ________________________________________________________________________________ Explain Command ________________________________________________________________________________ The routine error stores the value %explain in the variable curop. It also sets the variables xplint and xplen. Normal execution of equit moves curop to lastop in preparation for the next operation. The explain command checks the variable lastop to see if it contains the value %explain. If it does, the user has requested explanation of a previous error. In this case, the message pointed to by xplint (for length xplen) is displayed as long as the user hold UF. If the lastop is not %explain, then xplint and xplen are pointed to the default message and displayed just like an error message. While holding the UF key, the user can press any other command key. When this happens, the routine extexpl looks in the array xplntbl to see if the key corresponds to one of the extended explain messages. If it does, that message is displayed. ________________________________________________________________________________ 18. Titles Command ________________________________________________________________________________ ________________________________________________________________________________ Modification History: First Draft PB 8/14/87 ________________________________________________________________________________ ________________________________________________________________________________ Overview: The Titles command displays the contents of the first page of all documents with an initial page number of less than 1. The leap keys can be used to scroll the display up or down one title at a time if all titles won't fit on the screen at once. ________________________________________________________________________________ ________________________________________________________________________________ Table of Contents ________________________________________________________________________________ 2 Titles Command ________________________________________________________________________________ Titles Command ________________________________________________________________________________ The Titles command is implemented by a word Titles on disk C side 0 page 29. The command operates by blanking the screen, an then scanning the text for document characters. When it finds a document character, it checks the variable #ipage (a 16 bit quantity) to see if the first page is less than 1. If it is, then all the text between the document character and the first page break is defined to be the document title. Once a title has been found, it is displayed on the screen (including the page break at the end of the title). Then the process is repeated until either there are no more titles or there is no more room on the screen. If there is more than one screenful of titles, pressing either leap key will cause the display to scroll by one title at a time. This is done by picking which title will be at the top of the screen and then redisplaying the entire screen using the same procedure described above. The two leap keys are key $36 and key $3e, as can be seen in the code. When UF is released, the original text screen returns. ________________________________________________________________________________ 19. Disk ________________________________________________________________________________ ________________________________________________________________________________ Modification History: First Draft ________________________________________________________________________________ ________________________________________________________________________________ Overview: ________________________________________________________________________________ ________________________________________________________________________________ Disk Routines: ________________________________________________________________________________ diskaddr disk>mem oldidblock idblock displaydisk copyup diskcmd? allselected cleantext? !id save savenew save&backup backup fbackup nontextdisk? samedisk? backupdisk? killdisk? showdisk verify&erase emptytext? driveA driveB Disk Disk1 Bdisk Bdisk1 DiskB DiskB1 BDiskB BdiskB1 ________________________________________________________________________________ Disk Initialization Words: ________________________________________________________________________________ .initlist reset.hardware <> initkeyboard cold inithardware initring initinterruptvecs initvocab initstate initnumbers inittables initstrings initialize ________________________________________________________________________________ 20. Send, Receive and Phone Commands ________________________________________________________________________________ ________________________________________________________________________________ Modification History: First Draft PB 8/10/87 ________________________________________________________________________________ ________________________________________________________________________________ Overview: The telecommunication features of the Cat are supported by the routines described in this section. The commands described in this section are Send, Send Control, Send Password, and Phone. In addition, the receive part of the system (which is automatic and does not involve a command) is described. ________________________________________________________________________________ ________________________________________________________________________________ Table of Contents ________________________________________________________________________________ 2 Phone Command 3 Receive Routines 4 Send Command 5 Send Control and Send Password Commands ________________________________________________________________________________ The Phone Command ________________________________________________________________________________ The Phone command is used to dial the phone, disconnect the phone at the end of a call, and establish a data connection during an voice call. The code that implements the Phone command is on Disk C0, pages 1-6. The Phone command itself is on page 4 of Disk C0. The code first checks to see if the Send command is attached to the serial port. If it is, the Phone command is disabled. The code then checks for an extended selection, and if the phone is not off-hook, it begins dialing the number. The first part of dialing is to indicate that dialing is taking place by putting the message in the ruler. Then the phone is taken off-hook and a 2 second wait for the dial tone is inserted. Then a begin again loop that analyzes the selection is executed. Whenever a number is found, it is dialed. If the character is underlined, it is pulse dialed, otherwise it is tone dialed. This loop also puts in 1/2 second waits when a comma is encountered in the selection. If the Cat is off hook, then the Phone command should hang up. It does this by first sending the ETX string, in case the other end is a Cat, and then drops the line. If the Cat is not off hook, the Phone command should attempt to establish a connection. The code first waits for ringing to stop (since it could damage the circuitry if the phone were off-hooked while the ring voltage were on the line. When ringing has finished, the Cat is taken off-hook and an attempt to establish a charrier (via carriertimeout) is made. If the Phone command was used, we did not autoanswer, so the autoanswer flag is reset. The support routines used by the phone command are located either near the Phone command (pages 1-6 of Disk C0), or in the lower level support words on Disk A0. ________________________________________________________________________________ Receive Routines ________________________________________________________________________________ In the Cat, receiving characters is automatic, so there is no "Receive Command". Rather, every cycle through the main loop checks to see if there is a character ready to receive. The main loop is called and it appears on Disk C0, page 51. Near the end of the routine, a check is made to see if there is a character ready to receive. If there is, it is received and inserted in the text. The support words ?rxch, rxget, and txchr are the interface between the high-level send and receive words and the lower level routines. Buffering is performed by the low level words, and ?rxch is a flag that indicates there is a character in the rx buffer. The interface to the setup command is also performed by the low level words, so rxget always gets characters from the correct port as defined by the Setup command. If there is a character ready to receive, the word receive is called. Receive checks the state of the cattocat? flag and calls the appropriate receive word: ctocreceive if it is cat-to-cat and if it is non-cat-to-cat. simply gets data from the rx buffer (using rxget) and inserts it into the text at gap or at op if forceop is on (in the case where the user has typed). Then the interval table is adjusted appropriately and the display is refreshed. Finally, the von timer as if someone had just typed a key. ctocreceive is used to receive if the system is in cat-to-cat mode. This routine is much more complex than because it checks the received characters to make sure that they represent valid character strings. This checking is performed by the routine verifychar which breaks the received data down into individual elements which can be checked sequentially. In order to make sure that the checking is proceeding correctly, ctocreceive waits until two valid characters are in the receive buffer before taking on character out. This is done because most of the effort required to verify a character consists of making sure that the hidden information in the encoded text is correct. verifychar breaks the received data into characters that will appear in the text followed by hidden characters. For a break character, the contents of the format packet are checked for validity (including reasonable values for margins,etc). Doc characters are checked for document packets as well as format packets (if any). The first answer digit of a calc pocket is checked for a correct calctoken. The token part of the pocket is always set to redefineerror. The encoded expression is checked for bad characters, and if any are found the bad characters are replaced with a question mark (represented as 2 encoded bytes). Characters are also checked for correct accents and to make sure that reasonable attribute bytes are appended. If an error is found, the bad characters are replaced by question marks. This can result in a large number of question marks being inserted in the text if there is a bad byte in a format packet. If two characters have not been received after a 1 second timeout, the routine assumes that the end of the transmission has occurred and attempts to remove the last character from the buffer and insert it into the text. ________________________________________________________________________________ Send Command ________________________________________________________________________________ The Send command is used to send selected (or autoselected) text. Depending on the setup, it will send surface text only or both the text and its underlying structure. The Send command code is on Disk C0, page 49. The command first tests to make sure that if the modem is selected as the commport that the modem is trained. If it is not, the code attempts to establish a connection. Once a connection is established, the sending mode is selected. If the mode is cat-to-cat, then the routine deepsend, which sends the text and it's structure is called. If the mode is not cat-to-cat, then a test of the length of the string sendend$ is made. If this string is length 0 (that is the Setup command has selected None for the line end string), then the routine unformattedsend is called. This routine sends all surface characters plus tabs and returns. It does not fill out the margins or tabs with spaces and it does not send the underline, bold, or dotted underline status of the selected text. The contents of pockets or format packets are not sent either. If the sendend$ string is not zero length, then the formattedsend routine is called. This routine fills out the left margin and tabs with spaces, so a normal terminal at the other end will receive text that looks formatted like it is on the Cat screen. At the end of each line the sendend$ string is sent, so each line will end with a cr or crlf, depending on the user's choice. ________________________________________________________________________________ Send Control and Send Password Command ________________________________________________________________________________ The Send Control command sends a control character for each press of a key as long as UF is held down. Send Password simply sends the character typed as long as the UF key is held down. Neither command echoes to the screen. The code for these two commands is on Disk C0 page 50. In addition, there is an array that insures that Send Control only sends control characters according to the keyboard map in the spec. If a key that is not assigned to a control key is pressed, nothing is sent. Send password simply sends the key that is pressed, after converting it to an ascii character using the same sendtable used by the formatted and unformatted send words. _______________________________________________________________________________ 21. Printing _______________________________________________________________________________ _______________________________________________________________________________ Modifications: Initial Draft LC 7/20/87 More LC 8/1/87 General Discussion LC 8/25/87 More, more, and more LC 9/3/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: Given the diversity of printers supported by the Cat, the majority of the print code is very printer independent. Whenever a printer is selected by the user, a set of printer command strings is generated. These command strings tell the printer how to perform all required print activities: perform a carriage return, feed a sheet of paper out of the printer, bold face a character, initialize the printer, etc. Individual printer set up words prepare these strings for the selected printer. The print code can then communicate with the printer through the command strings without having to deal too much with the idiosyncrasies of each printer. The other printer dependent aspect of printing is character availability. Every printer supports a different character set. To circumvent this problem, each printer has a printer table which contains the ASCII codes it requires to print a given character in the defined CAT printer character set. If a particular character is not easily supported by a printer, a special execution vector is used to print the character in the best available manner. This execution vector is also virtual to the print code. _______________________________________________________________________________ _______________________________________________________________________________ Table of Contents _______________________________________________________________________________ Maintaining Printer Independence Printer Commands Printer Command Strings Printer "Knowledge" Character Selection Printer Tables Handling Character Set Exceptions Decomposing Text for Printing _______________________________________________________________________________ List of Illustrations: 21.1 Printer Character Set _______________________________________________________________________________ Maintaining Printer Independence _______________________________________________________________________________ The Cat supports 8 different printers/typewriters and 3 different printing technologies. The table below lists the printers/typewriters supported and the print technology each printer uses. The "printercode" is a value used by the print code to identify the printer currently in use. printercode Printer Name Printer Type 0 Cat180 Daisy Wheel 1 LBP8 Laser Beam 2 NewAP Daisy Wheel 3 AP400 Daisy Wheel 4 AP300 Daisy Wheel 5 AP100 Daisy Wheel 6 BJ80 Dot Matrix 7 FX80 Dot Matrix 8 No printer. N/A In order for any new printers/typewriters to be added to the above list (if necessary), the print code had to be written in a very printer independent manner. Ideally, the print code should never have to know which printer it was using. (In reality, a very small number of these special cases do exist.) The two areas in which printer independence is a problem are printer commands and printer character selection. _______________________________________________________________________________ Printer Commands _______________________________________________________________________________ The Cat communicates with a printer through a serial or parallel link. The data to be printed and the command codes which tell the printer how to print the data are sent over this link. Although there is a basic set of commands required to print a page of text, the codes required to implement these commands are usually quite different for each printer. _______________________________________________________________________________ Printer Command Strings To allow the print code to remain ignorant of all printer specific command codes, a set of printer command strings -- one for each basic print action required -- are defined. The basic print command string names, and the actions they perform, are listed in the table below: Print String Action initprint" Initializes printer. topofform" Instructs printer to eject current page and feed in another. endprint" Instructs printer to eject current page without feeding in another page. endline" Tells printer what to do when it reaches the end of a line. halfline" Instructs printer to move the paper up one half line. startline" Instructs printer to prepare to start printing a new line. +underline" Instructs printer to underline all subsequent characters. -underline" Instructs printer to stop underlining characters. +bold" Instructs printer to boldface all subsequent characters. -bold" Instructs printer to stop boldfacing characters. backspace" Instructs the printer to perform a backspace. overstrike" Tells printer to print characters without moving the carriage forward. unoverstrike" Tells printer to move forward after it prints a character. hmi" Tells printer how far to move after printing a character. evenhalfspace" Tells the printer to move forward one half space from an even half space position. oddhalfspace" Tells the printer to move forward one half space from an odd half space position. printreverse" Tells the printer to print from right to left (in reverse). printforward" Tells the printer to print from left to right (forward). Whenever a printer is selected by the user, the contents of these strings are changed. Each printer has a "set up" word which is responsible for filling each command string with codes understood by the printer. These are the names of the printer initialization words: Printer Set Up Word Cat180 cat180setup LBP8 lbp8setup NewAP cat180setup AP400 ap400setup AP300 ap300setup AP100 ap100setup BJ80 bj80setup FX80 fx80setup No printer. noprintersetup _______________________________________________________________________________ Printer "Knowledge" _______________________________________________________________________________ Aside from preparing the commands strings for a printer, the printer set up words also set the contents of the printer integers which describe what type of activities the printer "knows" how to perform. These are the main printer knowledge integers: Integer Knowledge Indicated boustrophedon Knows how to print bi-directionally. braindamaged Doesn't know how to print bi-directionally. knowstof? Is printer aware of form feeds ? knowsbold Can printer boldface ? knowsul? Can printer underline ? knowsos? Does printer prefer overstrike to backspace ? knowshmi? Does the printer use Diablo-like HMI setting ? ulinehack? Translate underlined whitespace to underline characters. NOTE: HMI stands for "Horizontal Motion Index". If a printer knows about HMI it is able to adjust the width of a character, i.e. the distance the carriage travels after printing a character. _______________________________________________________________________________ Character Selection _______________________________________________________________________________ The character sets supported by all of the printers differ both in content and in the codes used to access a given character. To hide these character set differences from the print code, "printer tables" are used. Printer tables are used to fool the print code into believing that each printer supports a common character set, the Cat printer character set (see following diagram). Each printer has its own printer table. _______________________________________________________________________________ Printer Tables A printer table has $10A word length entries. The data in each entry maps the ASCII code used to represent a character in the Cat printer character set to the actual printer codes used to generate the character. Entries $00 through $1F, which correspond to non-printable characters, are treated as spaces in the Cat printer character set. Since all printers use a $20 code to represent a space character, all of the printer table entries $00 through $1F consist of a null value in the upper byte and an ASCII space value in the lower byte: $0020. Entries $20 through $7E correspond the the characters in the standard ASCII character set. Most of the printers respect the standard ASCII character set and use the standard ASCII character codes to print the characters in this range. For this reason, most of these entries will consist of a null value in the upper byte and the corresponding ASCII value in the lower order byte: $0041 for the character 'A'. Entries $7F through $CF are for the characters in the extended Cat character set. The extended Cat character set contains special, country-specific characters and accent marks which may or may not be supported by a particular printer. Even if the printer supports a character in this range, it is very likely that the code used to generate the character is different than the code another printer will use. Therefore, the codes found in the entries in this range tend to vary widely among the different printer tables. If a character in this range is not supported by a printer, its corresponding entry in the printer' printer table will hold a space value, $0020. Entries $D0 through $10A are for the printer extensions to the extended Cat character set. These entries mainly correspond to the accented vowels and consonants that are commonly used in other countries. _______________________________________________________________________________ Handling Character Set Exceptions _______________________________________________________________________________ When print codes are looked up in a printer table during printing (see print), the contents of the upper byte of the printer table entry determines how the character will be printed. _______________________________________________________________________________ Simple Characters If the upper byte of the entry holds a zero, the character is a "simple" character (usually one in the $00 through $7F range). The lower byte of the entry contains the printer code which will be sent directly to the printer. _______________________________________________________________________________ Overstruck Characters If the upper byte of the entry holds a value which is greater than $1F, the character is an overstruck character. An overstruck character is a character which cannot be directly printed by the printer but can be constructed using characters which are available from the printer. For example, although the laserbeam printer can not directly print an 'E' with a circumflex accent (Cat character code = $E5), it can print both the accent and an 'E' separately. To fake this character, entry $E5 in the laserbeam print table contains a $B2 (character code for the accent) in the upper byte and a $45 (character code for an 'E') in the lower byte. When an entry with an overstruck character combination is encountered, the overstrike character is printed first, and then the main character is printed on top of the overstrike character. _______________________________________________________________________________ Weird Characters and the 'weirdprint Execution Vector If the upperbyte of the entry holds a positive value which is less than $20, the character is a "weird" character. Weird characters are typically characters which the printer supports in an indirect manner. For example, the laserbeam printer supports character sets for several different countries. To reach a character in one of its obscure character sets, printer codes which tell the laserbeam to use a new character set must be sent before the weird character can be printed. After the weird character is printed, more printer codes must be sent to set the laserbeam back to its main character set. When the print routines encounter a weird character, they will execute the routine whose address is found in the 'weirdprint integer. Each printer has its own weird print routine. The 'weirdprint integer will always hold the address of the weird print routine for the printer in use. Figure 21.1 Cat Printer Character Set _______________________________________________________________________________ FX80 Character Selection The weird print routine for an FX80 printer is called fx80magic . Although the FX80 printer supports a few different country character sets, the main character set it prints from is the USA character set. The fx80magic routine allows the FX80 to access characters in its other character sets. _______________________________________________________________________________ Daisy Wheel Character Selection The daisy wheel on a daisy wheel printer has 96 'petals'. Each petal holds one character. 94 of those characters are accessed using the ASCII codes $21-7E. To access the petals associated with ASCII codes $20 and $7F, a special escape sequence must be sent to the daisy wheel printer. The weird print routine daisymagic allow the daisy wheel printers to access the two hard-to-reach character petals _______________________________________________________________________________ Laserbeam Character Selection The laserbeam printer also supports several country character sets. The countries table lists the codes used to select different character sets on the laserbeam printer: code countries nx ) jsr, ;c 8701 w, ( IBM1 ) 8702 w, ( IBM2 ) 642 w, ( USA ) 641 w, ( UK ) 645 w, ( Norway/Denmark ) 64A w, ( Japan ) 632 w, ( Netherlands ) 742 w, ( IBM1, low half ) 652 w, ( France ) 633 w, ( Switzerland ) 64B w, ( West Germany ) 630 w, ( Canada ) The laserbeam weird print routine LBPmagic allows characters these other character sets to be reached. _______________________________________________________________________________ BJ80 Character Selection The BJ80 printer has no weird characters and no weird print routine. _______________________________________________________________________________ Print Table Patching _______________________________________________________________________________ _______________________________________________________________________________ Daisy Wheel Print Table Patching The basic character set for each daisy wheel is defined in the daisy.printer printer table. Each of the 14 different daisy wheels has approximately 20-23 characters which are different from and are used in place of the characters in the basic set. These daisy wheel specific characters are called "exceptions". Each daisy wheel has an "exception table" which lists the ASCII value of each exception character and the data which defines the replacement character. As an example, let's look at the exception table for the United States daisy wheel: code usa.dw nx ) jsr, ;c 0023 w, 23 c, ( sharp sign ) 003c w, a4 c, ( super 2 ) 003e w, a5 c, ( super 3 ) 0040 w, 40 c, ( @ ) 005b w, 5b c, ( left bracket ) 005c w, 81 c, ( plus/minus ) 005d w, 5d c, ( right bracket ) 005e w, 90 c, ( degrees ) 9061 w, 86 c, ( circle-a ) 0060 w, 9b c, ( cents ) 007b w, ac c, ( 1/4 ) 007c w, 7c c, ( vertical bar ) 007d w, ab c, ( 1/2 ) 007e w, b4 c, ( double underline ) 007e w, c4 c, ( double underline also ) 0100 w, 94 c, ( paragraph ) 0101 w, 95 c, ( section ) -1 w, ( End of USA daisy exceptions. ) When usa.dw is executed, the address of the USA daisy wheel exception table is returned. Each entry in the table is 3 bytes in length. The first two bytes contain the new replacement value ("print code") for a character. The third byte in each entry contains the ASCII code which specifies which character is being replaced. The exception data is terminated with a word-length '-1' value. By referring to both this table and the daisy.printer printer table we can see, for example, that the default daisy character set would normally cause a space to be printed whenever the ASCII code $5C is sent to the printer. When the printer is using the USA daisy wheel, the USA daisy exception table shows that a $5C code will cause a plus/minus sign to be printed. The tokens for each of the 14 daisy wheel exception tables are kept in yet another table, the DW.countries table: code DW.countries ( - a ) nx ) jsr, ;c t' usa.dw w, ( offset = 00 ) t' canada.dw w, ( offset = 02 ) t' latin.dw w, ( offset = 04 ) t' norway.dw w, ( offset = 06 ) t' sweden.dw w, ( offset = 08 ) t' holland.dw w, ( offset = 10 ) t' german.dw w, ( offset = 12 ) t' swiss.dw w, ( offset = 14 ) t' france.dw w, ( offset = 16 ) t' uk.dw w, ( offset = 18 ) t' spain.dw w, ( offset = 20 ) t' italy.dw w, ( offset = 22 ) t' special.dw w, ( offset = 24 ) t' japan.dw w, ( offset = 26 ) Whenever the printing code queries for the location of the current daisy wheel exception table, it is returned an offset into the DW.countries table. _______________________________________________________________________________ BJ80 Print Table Patching The bjsecond.dw table contains the character exceptions to the standard BJ80 printer table. These exceptions are patched over the standard BJ80 printer table contents if the user chooses these exceptions through SETUP. _______________________________________________________________________________ Paper Length _______________________________________________________________________________ The calculations used to determine paper lengths and the commands used to set the paper lengths for the various printers also vary. An execution vector for setting the page length is kept in the 'docbreak integer. This vector functions similarly to the 'weirdprint execution vector. Whenever a page break is being output (by the pagebreak routine), the routine whose address is in 'docbreak is executed to reset the printer's page length/size information. The four page length routines shared among all the printers are: CATdocbreak Set paper length for some daisy wheel printers. LBPdocbreak Set paper size for LBP printer. ETWdocbreak Set paper length for 12/inch ETW typewriters bj80docbreak Set paper length for BJ80 printers. _______________________________________________________________________________ Printing Text _______________________________________________________________________________ The previous text in this chapter has described the printing data structures and other printing terminology. All of the routines and integers used to implement printing are described in detail in the routines and integers summary at the end of the chapter. This section will describe the overall flow of the print command--which commands perform which printing functions--so that you will be able to use the printing routines summary section knowledgeably. The word called when [USE FRONT][PRINT] is pressed is Print . Print uses pickprinter to set up the print spooler, makeprinttable to prepare the printer table and patch it if necessary, and to perform the main printing functions. uses printify to highlight the "Print" status light, initprinter to initialize various printing integers and <> to perform the actual printing. <> basically spins in a loop using printline to print one line at a time until the entire selection has been printed. printline uses wrap to step through the text until a line with displayable text is encountered. When a displayable line is found, build is used to create a "disp-format" image of the line of text in the line output buffer (lbuff). is used to step through the lbuff image one character at a time, using unbuild to decompose the character for printing, and then using render to actually print the character on the page. render uses print to print a single character printer independently. will continue until all characters on the line have been printed. printline also checks for page break and document characters. If a page break or document character is encountered, pagebreak is used to eject the current page from the printer and to feed in another if necessary. Refer to the individual routines listed above for further information on the printing process. _______________________________________________________________________________ Printing Routines _______________________________________________________________________________ _______________________________________________________________________________ Print Data Tables: BJ80.printer Printer code table for bubble jet printer. bjsecond.dw Character exceptions to the bubble jet printer table. countries Table used to map an unusual character to the country character set which contains the character. Used by the laser beam printer only. daisy.printer Basic version of a daisy wheel print code table. Since each daisy wheel contains different characters in different locations, this table will be copied to RAM and patched whenever a daisy wheel printer is used. fx80.printer Printer code table for the FX80 printer. LBP.printer Printer code table for the laser beam printer. LBPpaper Table of laser beam printer paper size information. lbpsmarts Mystical font selection information for the laser printer. printers Table used to map a particular printer, represented by a printer code (from 0-8), to its associated setup code. The printers table contains the tokens for the routines used to set up the printers. After setprinter determines which printer is about to be used, it uses the printer code for the printer to index into the printers table and causes the token at the offset to be executed. code printers nx ) jsr, ;c t' cat180setup w, t' lbp8setup w, t' newapsetup w, t' ap400setup w, t' ap300setup w, t' ap100setup w, t' bj80setup w, t' fx80setup w, t' noprintersetup w, vanilla.unbuild Table used to map an overstruck character to its corresponding offset into the printer table. Used by unbuild . wheel>country Print wheel selection codes for the AP100, AP300, and AP400 daisy wheel printers. wheel>iso Print wheel selection codes for the Cat180 and NewAP daisy wheel printers. _______________________________________________________________________________ Daisy Wheel Exception Data Tables: afrikaans.dw South Africa exceptions to the daisy wheel print table. canada.dw Canada exceptions to the daisy wheel print table. france.dw France exceptions to the daisy wheel print table. german.dw West Germany exceptions to the daisy wheel print table. holland.dw Netherland exceptions to the daisy wheel print table. italy.dw Italy exceptions to the daisy wheel print table. japan.dw Japan exceptions to the daisy wheel print table. latin.dw Latin America exceptions to the daisy wheel print table. norway.dw Norway/Denmark exceptions to the daisy wheel print table. spain.dw Spain exceptions to the daisy wheel print table. sweden.dw Sweden exceptions to the daisy wheel print table. swiss.dw Switzerland exceptions to the daisy wheel print table. uk.dw United Kingdom exceptions to the daisy wheel print table. usa.dw USA exceptions to the daisy wheel print table. DW.countries Table containing the addresses of the country-specific daisy wheel exception data: code DW.countries ( - a ) nx ) jsr, ;c t' usa.dw w, ( offset = 00 ) t' canada.dw w, ( offset = 02 ) t' latin.dw w, ( offset = 04 ) t' norway.dw w, ( offset = 06 ) t' sweden.dw w, ( offset = 08 ) t' holland.dw w, ( offset = 10 ) t' german.dw w, ( offset = 12 ) t' swiss.dw w, ( offset = 14 ) t' france.dw w, ( offset = 16 ) t' uk.dw w, ( offset = 18 ) t' spain.dw w, ( offset = 20 ) t' italy.dw w, ( offset = 22 ) t' special.dw w, ( offset = 24 ) t' japan.dw w, ( offset = 26 ) _______________________________________________________________________________ Print Table Construction Words (used at compile time): w,'s ( n1 n2 - ) ("double-you-commas") Uses w, to lay count 'n2' occurrences of the value 'n1' into the dictionary. ch ( - ) Places a 2-byte value into the dictionary. Takes the next character from the input stream and lays it into the table being constructed. os ( - ) Places a 2-byte value into the dictionary. Takes the next two characters from the input stream and lays them into the table under construction. ,chars ( a n - ) ('comma-chars') Takes each byte length character value from the string located at address 'a' of length 'n' and lays the value into the printer table under construction as a two byte value (upper byte = 0). Used to add many characters to a printer table at once. ,accents ( a n c - ) Takes each byte length character value from the string located at address 'a' of length 'n' and lays the value into the printer table under construction as a two byte, accented value where the upper byte contains the ASCII code for the accent 'c'. Used to add many accented characters to a printer table at once. XXX ( - ) Adds the 2-byte value $0020 (lower byte = ASCII code for a space) to a printer table. Used as a filler for unprintable or unused characters in a printer table. ,unbuild ( n1 a n2 c - n3 ) Takes each byte length character value from the string located at address 'a' of length 'n2' and lays the value into the unbuild table under construction as a two byte, overstruck value where the upper byte contains the ASCII code for the overstrike character 'c'. A two byte printer table offset value is placed into the unbuild table immediately after the overstruck character. The original offset value for the string is 'n1'. The offset value is incremented for each overstruck character placed into the table and the ending offset value 'n3' is returned on the stack. _______________________________________________________________________________ Basic Printer Driver Words: backspace ( - ) Moves the printer carriage backwards one space (2 half-spaces). If the printer carriage was moving to the right, backspace will cause the carriage to be moved 2 half spaces to the left and vice versa. If the printer is a "braindamaged" printer, a printer which doesn't have a backspace command, backspace will specifically check the carriage direction and output a $08 (backspace) ASCII code to the printer if the carriage is moving to the right and a $20 (space) ASCII code if the carriage is moving to the left. If the printer "knows" about backspacing, backspace will simply send the backspace" printer command string to the printer. backspace also passes a -2 to motion to indicate that the carriage has moved backwards by two half spaces. halfspace ( - ) Tries to move the printer carriage one half space in the current carriage direction. If the printer is braindamaged and the carriage is moving backwards (to the left), halfspace will use backspace to move the carriage a full space to the right. Next, halfspace checks oddhalfspace to see if the next half space is an odd half space. If it is, the oddhalfspace" printer command string is sent to the printer. Otherwise, the evenhalfspace" printer command string is sent to the printer. Finally, the contents of the oddhalfspace integer are toggled and a 1 is passed to motion to indicate that the carriage has moved 1 half space forward. motion ( n - ) Used to help keep track of print head location. Adds the specified motion 'n', expressed in half characters, to the current contents of the prcol integer. Before prcol is updated, it is clipped to make sure it lies within the current left and right margin boundaries. newhalfline ( - ) Moves the paper up one 1/2 line. Uses put" to send the halfline" string to the printer. newline ( - ) Puts the printer carriage physically and logically at the start of the next line and alters or resets several printer state integers. The endline" printer command string is used to physically position the printer carriage at its new line position. If the printer cannot print bi-directionally, the carriage must always be placed at the left edge of the paper. If the printer can print bi-directionally, the carriage will be positioned at either the left or right paper edge. prcol is the integer used to hold the current logical horizontal position of the carriage. newline uses the phrase gutter negate prcol to to set the logical carriage position to the left margin for non-bi-directional printers. newline also zeros the contents of the oddhalfspace , bolded , and underlined printer state flags and the proldflags integer and increments the prline logical vertical page position integer by 2 half lines (since we are moving down to a new line). If underlining or bolding was turned on at the end of the previous line, newline will send either the -bold" or -underline" printer command string to the printer to turn the bolding or underlining off. paperlength ( - n ) Returns the length (in half lines) of the paper being used for printing. paperlength calculates the total length of the paper using the contents of the #above , #long , and #below state integers and then subtracts the contents of the papershort integer from the total length to calculate the actual paper length. printc ( c - ) Higher level version of the word that will simulate a bold character on a printer which does not know how to print in boldface. If the printer does not know how to print in boldface and a bold character must be printed, printc will doublestrike (print once, backspace, print again) the character 'c' to simulate a bold appearance. Otherwise, the character will be printed just once. Passes a 2 to motion to indicate that the carriage has advanced by two half spaces. printerror ( - ) Presents a system error and aborts. put" ( a n - ) Sends the string at address 'a' of length 'n' to the printer. Uses to send each character individually. _______________________________________________________________________________ Vertical Paper Motion: formfeed ( - ) Feeds the current page out of the printer, feeds a new page in if necessary and possible, and resets the logical printer carriage positioning integers. If the printer understands a "top-of-form" command, if knowstof? is true, formfeed checks to see if either the end of the printable selection has been reached ( #nextwr @ gap > ) or if the user has prematurely terminated printing ( stopprint ). If either of these cases are true, we will not be printing another page. The endprint" printer command string is sent to the printer to indicate that the current page should be ejected without feeding in a new page. If neither of the cases are true, we will be printing another page. The topofform" printer command string is sent to the printer to indicate that the current page should be ejected and a new page should be fed into the printer. If the printer does not have a "top-of-form" command formfeed will specifically place the carriage at the first line past the end of the page. formfeed's final actions are to reset prcol and prline so that the carriage is logically positioned at the left edge on line 0 on the paper. The backwards integer is set to false so that printing will commence in a left to right direction. newpage? ( - ) If necessary, initializes the physical and logical vertical line position of the printer carriage for a new page. If the printer carriage is at the top of the paper ( prline 0= if ), newpage? will try to move the carriage #above half lines down from the top of the paper. #above holds the height of the top margin on a printed page, expressed in half lines. paperpos holds the "top-of-form" position for a printer, expressed in half lines. After a form feed, the carriage will be located paperpos half lines from the top of the new sheet of paper. If the #above position is less than the paperpos position, the carriage will be left at its current top-of-form position. If the #above position is greater than the paperpos position, toline will be used to move the carriage the remaining number of half lines required to reach the #above position ( #above paperpos - ). The prline system integer is initialized to the value held in #above . page#string ( - a n ) Formats the page number to be printed at the bottom of a page. Gets the local page number within this document from the #pgl state integer and adds it to the start page number for this document, found in the #ipage state integer, to form the page number for the current page. Uses the basic Forth pictured numeric output words to create a page number string which includes the right frill character, the page number (positive or negative), and the left frill character. Returns the address and length of the page number string. pagebreak ( - ) Outputs a page break on the printer. This involves printing the page footer if required and possible, performing a formfeed, and resetting document characteristics if necessary. If the single page printing mode is in use, pageprint sets stopprint to true so that printing will stop after the current page is printed. pageprint calculates the page number for this page and compares it to the page number in the #iprint state integer. #iprint holds the page number of the first printable page in the current document. If the calculated page number for the current page is less than the #iprint page number, pagebreak will not print the footer. Also, if for some reason the carriage has already moved below the line on which the footer should be printed, the footer will not be printed. Whether or not the footer was printed, using printfooter , pagebreak uses formfeed to eject the paper and then checks the current character. If the current character is a document separator and there is more text to print, #wr @ nextchar findchar is used to get the control variables for the next document and the document break routine for the printer is executed to set the new paper length for the document. pagebreak? ( - f ) Returns a true flag if the lbuff contains either an implicit or explicit page break representation. printfooter ( - ) Prints the page footer line. Moves the carriage to column 0 of the footer line and prints the leftfoot" string, if any. Then, uses page#string to calculate and form the page number string and print" to print it. The page number string is centered over column 40. Finally, if a right hand footer string, rightfoot" , exists it is also printed. The page number is always printed in decimal. The current number base is saved and restored by printfooter . showpage ( - ) Uses displaybos to display the end of the page just printed and checkline# rule to update the ruler display to match the display. skippage ( - ) Advances the control variables over the current page break. If the page break is explicit, #wr @ nextchar findchar is used to advance over the page break character. If the break is implicit, and there is only one character on the next page, printing is terminated (because when an implicit page break is selected the first character on the page is also selected). toline ( n - ) Feeds paper until the carriage is positioned at half line 'n' on the page. If the carriage is already at or beyond the specified half line, toline will do nothing. If an odd half line is specified newhalfline is used to move the carriage 1 half line and then newline is used to move the carriage the remaining number of half lines in 2 half line increments. _______________________________________________________________________________ Character Rendering: lbuffend ( - a ) Returns the address of the end of the lbuff . overstrike ( c - ) Prints a character without moving the carriage. If the character is white, overstrike does nothing. If the character is visible, and it knows how to overstrike, the overstrike" and unoverstrike" printer command strings are used for overstriking. Otherwise, print is used to print the character and backspace to move the carriage back. print ( n - f ) Prints the character represented by the printcode, 'n', printer independently. If the character is a white character that should be underlined, and the printer chosen does not underline white characters, an underline character, $5F, is output in place of the white character. Otherwise, the printcode is used to index into the current printer table to find the 2 byte entry for the character to be printed. If the first byte of the 2 byte entry is zero, the character is a simple, standard ASCII character. The ASCII code for this simple character is taken from the second byte in the entry and printed using printc . If the first byte of the 2 byte entry is a non-zero value greater than $1F, then the character is comprised of two characters, one overstruck over the other. overstrike is passed the ASCII code found in the first byte of the 2 byte entry and printc is used to print the character corresponding to the ASCII code found in the 2nd byte of the entry. If the first byte of the entry is a non-zero value less than $1F, then the character to be printed is a special character in the printer's character set which requires printer specific commands to print. In this case the 'weirdprint vector is executed to handle printing of the special character. print" ( a n - ) Prints the string located starting at address 'a' of length 'n' to the printer, printer independently. If the printer was printing backwards, the backwards integer is set to zero and the printforward" command string is sent to the printer. Then print is used to print the characters in the string one-by-one. render ( f - ) After unbuild has decomposed the next printable character in the lbuff and stored the character value in prchar and set the printer flags accordingly, render is used to print the character. First, render prepares the printer by checking the underline transition flag pr\uline/ and the bold transition flag pr\bold/ . If either of these flags indicates that a font style transition is occurring, render will send whichever printer command string is required to effect the transition, +underline" , -underline" , +bold" , or -bold" , to the printer. The underlined and bolded integers will be set to true if the character to be printed is to be bolded or underlined. If the character is to be underlined and the printer does not know how to underline, render will use 5f overstrike to specifically print an underline character in the location where the real character will be placed. If the prsmall? integer indicates that the character is a half-wide character, a half space will be emitted and the character will not be printed. If the character is not a small character, the flag on the stack, returned by unbuild , is checked. If the flag indicates that the character to be printed is a character found in the current printer's printer table, the character is passed to print for printing: prchar print . If the character was not found in the printer's character set, the codes in the upper and lower bytes of the 2 byte printer code are printed separately, one overstruck on top of the other. If the code in the upper byte is one of the accent codes in the range from $c0 to $cf, render makes an additional check to see if a short or a tall accent should be used as the overstrike character. A short accent will be used if the main character to be printed is a lowercase character. short? ( c - f ) Returns a true flag if the character 'c' is a lowercase character. white? ( c - f ) Returns a true flag if the character is 'white'. A white character is a character with an ASCII code less that $21, a permanent space character: $93, or an overstrike space character. _______________________________________________________________________________ Horizontal Motion Control: printblanks ( n - ) "Prints" 'n' half spaces on the current line. Used for carriage positioning on printers which do not know how to move directly to a specified horizontal position. If the number of half spaces is odd, halfspace will be used to print one half space and then spc print will be used to print the remaining even number of half spaces, 2 at a time. If 'n' is negative, the carriage is not moved. tocol ( n - ) Moves the carriage to half character position 'n' on the current line. If the printer does not know how to print bi-directionally, the startline" printer command string is sent to the printer to cause the carriage to be moved to the left margin and 'n' half spaces are "printed" on the current line using printblanks . If the printer can print bi-directionally, tocol can use one of two methods to move the carriage to the desired position. If the knowshmi? integer flag indicates that the printer knows how to modify the character width (hmi), tocol will set the character width to 1 inch, calculate the number of whole inches between the current carriage position and the destination, and then will print 'x' spaces where 'x' is the number of whole inches to move. Each space printed at this point will cause the carriage to move 1 inch in the desired direction. To move the carriage any remaining distance, tocol sets the character width to the width of the remainder distance, prints a space (to move the carriage), sets the character width back to normal, and then terminates execution. If there is no remainder distance, if the original distance was a whole number of inches, tocol will simply set the character width back to normal and terminate execution. If the printer does not know how to modify the character width, tocol will try to determine the fastest way to get to the desired position using only spaces and carriage returns. The two possibilities are: (1) move the carriage to the left margin with the use of the startline" printer command string and then space over to the desired position, or (2) space directly to the desired position from the current position. seektime ( n1 - n2 ) Given a destination carriage position 'n1', expressed in half characters, seektime returns a simpleminded estimate of the "time" required to get to that position starting from the current position by returning the absolute value of the delta distance between the two locations: (n1) prcol - abs . _______________________________________________________________________________ Printing a Line of Text: ( - ) Print a line of text. Steps through the lbuff using unbuild until unbuild returns a flag which indicates that the end of the lbuff has been reached. Each valid character obtained by unbuild is printed using render . The definition of is : : begin unbuild while render again drop ; initprinter ( - ) Initializes the print-time integers: proldflags , backwards , stopprint , oldcountry , bolded , underlined , prcol , prline . printify ( - ) Displays "Print" in indicator light 3. printline ( f1 - f2 ) Processes one line of text. printline is passed a flag 'f1' which indicates whether the line about to be processes is the first line of text and returns a flag 'f2' which indicates whether there are more lines of text left to process. printline will not start processing the line of text until the print buffer has more that $200 bytes of available space. print.buf.free is used to check the available printer buffer space. Next, printline checks to see if the user has prematurely terminated the print command. If printing has been prematurely terminated, the stopprint integer is set to true, quit.print is used to stop printing, and UnPanicPrint is set as the undo operation in case the user changes their mind and does not want to stop the printing. If printing was not terminated, printline checks the contents of #spr to determine what type of line is up for processing. If #spr holds a 2, printline is being asked to print the blank double half line which is inserted between lines of double spaced text. printline will use newline to move the carriage down 2 half lines and wrap twice to decrement the #spr count to zero. If #spr holds a 1, printline is being asked to print the blank single half line which is inserted between lines of 1 1/2 spaces text. printline will use newhalfline to move the carriage down 1 half line and wrap to decrement the #spr count to zero. If #spr holds a 0, printline is being asked to print an actual line of text. wrap is used to load the address of the end of the next line of text into the control variables. This address is then stored into the #nextwr integer ( build needs it) and prevwrap is used to restore the control variables for the line of text about to be processed. build is used to get an image of the line of text into the lbuff . If pagebreak? indicates that the lbuff contains a page break character, and if the flag passed to printline indicates that this is the first page break in the printing session, the page break will be skipped over and ignored (to avoid printing a blank page at the start of each printing session). Otherwise, if a page break which is not the first page break is encountered, pagebreak will be used to eject the current page, and showpage and skippage will be used to get to the next printable page, if any. If the lbuff does not contain a page break, printline prepares the line for printing. If the line is the first line on a page, newpage? will set up the page parameters. trimline is used to trim non-printable characters from the start and end of the lbuff string. startline prepares the printer and the printer integers for printing. If startline indicates that the line contains printable characters, is used to print the line. newline is used to move the carriage down by 2 half lines and wrap is used to update the control variables. printline's final action is to check for more text to print and to return a flag which indicates the outcome of the check. printposition ( a1 a2 - n1 n2 ) Converts the start lbuff print address 'a1' and the end lbuff print address 'a2' to their corresponding start position 'n1' and end position 'n2' on the current line. The start and end position are expressed in half characters. startline ( a1 a2 - f ) Checks for a blank line of text, initializes the printlimit , printnext and backwards printing integers, and sets the printing direction. If the start lbuff print address 'a1' and end lbuff print address 'a2' are equal, this line is a blank line (no text to print), and a false flag 'f' is returned. If the lbuff addresses are not equal, there is text to print. The address of the first printable lbuff character is placed in printnext and the address of the last printable lbuff character is placed in printlimit and a true flag will be returned when startline completes execution. If the printer can print bi-directionally, startline checks to see if the carriage is currently closer to the start column or end column position for the line. If it is closer to the start position, tocol is used to move the carriage to the start column, the printforward" printer command string is sent to the printer, and the backwards integer is set to false. If the carriage is closer to the end position, tocol is used to move the carriage to the end column, the printbackward" printer command string is sent to the printer, and the backwards integer is set to true. The printnext and printlimit integer contents are switched if backwards printing is used. If the printer cannot print bi-directionally, the carriage is moved to the start column position and the backwards integer is set to false. traqptext trim1 ( a1 a2 - a1 a2' ) Given the addresses of the start 'a2' and end 'a1' of the lbuff , trim1 adjusts the start address so that no unhighlighted characters at the start of the current line are printed. trim2 ( a1 a2 - a1 a2' ) Given the addresses of the start 'a2' and end 'a1' of the lbuff , trim2 adjusts the start address so that no leading white characters at the start of the printable section of the current line are printed. Used after trim1 . trim3 ( a2 a1 - a2 a1' ) Given the addresses of the start 'a2' and end 'a1' of the lbuff , trim3 adjusts the end address so that no unhighlighted characters at the end of the current line are printed. trim4 ( a2 a1 - a2 a1' ) Given the addresses of the start 'a2' and end 'a1' of the lbuff , trim4 adjusts the end address so that no trailing white characters at the end of the printable section of the current line are printed. Used after trim3 . trimline ( - a1 a2 ) Given a line of text in the lbuff , trimline determines which parts of the line can and should be printed and then returns the addresses of the first lbuff character 'a1' and the last lbuff character 'a2' to be printed. Any highlighted character which is not a leading or trailing white character is a valid printable character. unbuild ( - f1 f2 ) Takes a single character from the lbuff , tries to find the character in the printer character set, and sets up the printer flags. 'f1' is a 'known?' flag which indicates whether the character is character from the printer character set. 'f2' is a 'valid?' flag which indicates whether or not this character should be printed (have we reached the end of the printable lbuff characters ?). unbuild uses the address in printnext to find the next printable character in the lbuff . unbuild first checks to see if the character has any associated overstrike character. If there is an overstrike character, unbuild will create a word which has the overstrike character code in the upper byte and the main character code in the lower byte (same format as a printer table entry) and will compare the word to the list of overstrike combinations found in the vanilla.unbuild table. The vanilla.unbuild table contains all of the overstrike combinations which are found in the printer character set ($d0 - $109). If the overstrike combination is found in the table, the 2 separate codes used to construct the character are discarded and the single printer character code which represents the overstrike combination in the printer character set is placed in the prchar integer and a true 'known?' flag is placed on the stack. If the overstrike combination is not matched, the 2 byte set of character codes used to represent the character are placed in prchar and a false 'known?' flag is placed on the stack. Next, unbuild checks the character flags which are associated with each character in the lbuff and sets the related printing flags accordingly. The smallbit is used to set the prsmall? integer. The invbit is used to set the printed? integer (only inverted characters are printed). The ulinebit , boldbit , and dlinebit are used to check for underline, bold, and dotted underline style transitions. The current states of these bits are compared with the character flag bits from the previous character (saved in the proldflags integer). A change in any of these bits will cause either the pr\uline/ , pr\bold/ , or pr\dline/ style transition integers to be set to true. Finally, unbuild checks to see if the address in printlimit has been reached and then increments/decrements the printnext address by 4, depending upon the current printing direction. A flag which indicates whether the printlimit has been reached is placed on the stack. UnPanicPrint ( -- ) Restarts a printing session which was terminated with a panic stop. Uses restore.print to reset all of the low level print buffer pointers, extend to re-highlight the unprinted text, printify to turn the "Print" indicator on, <> to restart and perform the printing, indicate to turn the "Print" indicator off, and 0 setprinter to select the default printer when finished. stopprint is set to false. _______________________________________________________________________________ Main Print Words: <> ( - ) Performs the bulk of the printing activities. This word was factored out of so that UnPanicPrint could be used to restart printing. <> sets undop to zero (no undo operation) and causes the top of the selection to be displayed. The control variables are prepared and printline is called specially to print the first line in the selection (to handle any initial page breaks). Then printline is called in a loop until there are no more lines of text to print. When the printline loop is completed, <> checks to see if printing was prematurely terminated. If it was terminated prematurely, the first unprinted character is saved in the op and then the selection is reduced to a cursor at the end of the selection. If printing was not terminated prematurely, the end of the original selection is just displayed on the screen. If necessary, formfeed is used to eject a partial page. ( - ) This is the highest level print word, aside from the Print command itself. Uses printify to turn on the "Print" indicator light and checks for a printable selection. If there is nothing to print, KillPrint is used to terminate the command. Otherwise, bos nextchar findchar is used to set the control variables for the printable selection, initprinter is used to perform printer initialization, and <> is used to perform the bulk of the print operations. is also responsible for turning the indicator light off after printing has finished. AltPrint ( - ) Word executed when [USE FRONT] [SHIFT] [PRINT] is pressed. Causes the current text selection, if any, to be printed out on the alternate printer. Since the main printer is always the default printer, setprinter is used to initialize the alternate printer. pickprinter and makeprinttable are used to set up the print spooler and printer table and then is used to print the text. After the completion of printing on the alternate printer setprinter is used again to initialize the main printer and to make the main printer the default printer. KillPrint ( - ) Stops the printer spooler. If the print buffer is not empty, quit.print is used to stop printing and UnPrint is set as the undo operation. makeprinttable ( - ) For those printers which have non-standard (BJ80 w/second character set) or country specific (any of the daisy wheel printers) print tables, makeprinttable creates a RAM image of the print table in the trkbuf and patches it as necessary. patchprint is used to patch the RAM image. The trkbuf address is stored in the printertable integer. makeprinttable will do nothing if a printer with a standard print table is being used. patchprint ( a - ) Used to patch non-standard print tables. The patch data at the address 'a' is used to patch the print table located at the address contained in the printertable system integer. pickprinter ( - ) Used to link the print spooler to the correct printing port. If there is a selection, and if the printercode and printerport system integers indicate that a valid printer port has been selected, pickprinter will direct the print spooler to either the serial port (with print.serial) or the parallel port (with print.parallel). setprinter ( f - ) Sets the desired printer, f=0 for the main printer and f=1 for the alternate printer, as the current printer and performs printer preparation activities for that printer. setprinter initializes all printer integers and strings which contain values shared by the majority of the printers and then, using the printer code for the chosen printer as an index into the printers table, obtains and executes the token corresponding to the word which performs printer specific initialization for the chosen printer. Print ( - ) Word executed when [USE FRONT] [PRINT] is pressed. Causes the current text selection, if any, to be printed out on the main printer. Uses pickprinter to set up the print spooler, makeprinttable to make a patched RAM print table image if a printer with a non-standard print table is being used, and uses to print the selection. UnPrint ( - ) Undoes the stopping of the print spooler. Uses restore.print to reset the print spooler and sets KillPrint as the undo operation. _______________________________________________________________________________ Printing Initialization Words: ap100setup ( - ) Performs AP100 printer set up procedures. ap300setup ( - ) Performs AP300 printer set up procedures. ap400setup ( - ) Performs AP400 printer set up procedures. apsetup ( n - ) Performs the printer set up procedures which are common to the AP300 and AP400 printers. The printer-specific steps/line parameter, 'n', is passed on the stack. bj80docbreak ( - ) Routine which sets the page length for the BJ80 printer. bj80setup ( - ) Performs BJ80 printer set up procedures. cat180setup ( - ) Performs Cat180 printer set up procedures. Sets the paperpos integer to 2 (top of form position) CATdocbreak ( - ) Routine which sets the paper length for the Cat180 printer. daisymagic ( a c - ) Handles weird print for daisy wheel printers. ETWdocbreak ( - ) Routine which sets the paper length for the 12/inch ETW typewriters (the AP100, AP300 and AP400 printers). fx80setup ( - ) Performs FX80 printer set up procedures. fx80magic ( a c - ) Handles fancy font switches for the FX80 printer. lbp8setup ( - ) Performs LBP printer set up procedures. LBPdocbreak ( - ) Routine which sets the paper size for the LBP printer. LBPmagic ( a c - ) Handles the printing of unusual characters on the laser beam printer. Switches to the country whose character set contains the unusual character, prints the character, and then switches back to the country character set previously being used. setcountry ( n - ) Sets the laser beam printer to a country character set. The country character code 'n' is obtained from the countries data table. newapsetup ( - ) Performs Cat180 printer set up procedures. noprintersetup ( - ) Does nothing. _______________________________________________________________________________ SetUp Export Words: printercode ( - n ) Returns a code which indicates which printer is currently in use on the current printer port. printercode Printer Name Printer Type 0 Cat180 Daisy Wheel 1 LBP8 Laser Beam 2 NewAP Daisy Wheel 3 AP400 Daisy Wheel 4 AP300 Daisy Wheel 5 AP100 Daisy Wheel 6 BJ80 Dot Matrix 7 FX80 Dot Matrix 8 No printer. N/A printerport ( - n ) Returns a code which indicates which printer port is currently in use: -1 = parallel ; 0 = serial ; positive value = no printer port in use (?). printerinfo ( n1 - n2 ) Returns information about the printer currently in use. The code passed in, n1, indicates what information is desired. Eight possible input codes are recogized. The input code, and the information associated with the code, are listed in the table below: Input Code Data Returned 0 If a daisywheel printer is in use, a code which indicates which wheel is being used is returned. If a daisywheel printer is not being used, a flag which indicates whether underlined code should be underlined or italicized is returned. 1 If an laserbeam printer is in use, a code which indicates which laserbeam font is being used will be returned (Courier, Gothic, Pica, Elite). 2 Returns a code which indicates the pitch of the font currently in use. 0 = 10 pitch 1 = 12 pitch 2 = 15 pitch 3 Returns the current left margin offset. 4 Returns a flag which indicates whether bidirectional printing is being used. 5 Returns a true flag if sheet feeding is being used. 6 Returns a code which indicates which paper tray is being used. 7 Returns a flag which indicates whether single page printing is being used. _______________________________________________________________________________ Print Spooling Export Words _______________________________________________________________________________ ( c - ) Places a character in the print buffer. pbuf! ( c - ) Store a character into the print buffer. print.parallel ( - ) Waits in a loop until at least 1 byte of free space is available in the print buffer. print.serial ( - ) Waits in a loop until at least 1 byte of free space is available in the print buffer. print.empty ( - f ) Returns a true flag if the print buffer is empty. print.buf.free ( - f ) Returns the number of free bytes in the print buffer. quit.print ( - ) Stops printing in such a manner that printing may be resumed if necessary. restore.print ( - ) Restores the low level printing state so that printing may be resumed after a quit.print . _______________________________________________________________________________ Print Strings _______________________________________________________________________________ initprint" String used to initialize the printer. This string is constructed for the main printer when the SetUp command exits or when the system powers on. The individual printer printer setup words are used to construct the string. initprint , which is called by , is responsible for sending the initialization string to the printer. userinit" User-specific printer initialization string. Sent to the printer right after the initprint" is sent. This string is never touched by the set up commands. leftfoot" String which contains the text for a footer to be placed to the left of the page number. If this string has a length of zero it will not be printed. rightfoot" String which contains the text for a footer to be placed to the right of the page number. If this string has a length of zero it will not be printed. leftfrill" String which contains the "frill" mark to be placed to the left of the page number. The string is initially 2 characters long and contains a minus sign followed by a space, "- " . rightfrill" String which contains the "frill" mark to be placed to the right of the page number. The string is initially 2 characters long and contains a space followed by a minus sign, " -". topofform" String which contains the commands which tell a printer to eject the current page and feed in another. endprint" String sent when a print job is completed. Tells a printer eject the current page without feeding in another page. endline" String sent when the printer has reached the end of a line. This string will always contain a linefeed character and, with some printers, will additionly contain a carriage return. halfline" String which commands the printer to move the paper up one half line. startline" String sent when the printer should start printing a new line of text. For those printers which do not automatically perform a carriage return when they receive a linefeed (in the endline" string), the startline" string will contain a carriage return so that the printer starts printing on the correct line. +underline" Contains the commands which instruct the printer to underline all subsequent characters. -underline" Contains the commands which instruct the printer to stop underlining all subsequent characters. +bold" Contains the commands which instruct the printer to boldface all subsequent characters. -bold" Contains the commands which instruct the printer to stop boldfacing all subsequent characters. backspace" Instructs the printer to perform a backspace. overstrike" Tells the printer to print the next character in the string without moving the carriage forward. unoverstrike" Tells the printer to move forward after it prints a next character. hmi" Used as lead in for setting the pitch (?). evenhalfspace" Tells the printer to move forward one half space from its even half space position. oddhalfspace" Tells the printer to move forward one half space from its odd half space position. printreverse" Tells the printer to print from right to left (in reverse). Only used when a printer which can print bi-directionally is in use. printforward" Tells the printer to print from left to right (forward). _______________________________________________________________________________ Printer Integers _______________________________________________________________________________ _______________________________________________________________________________ Printer "Knowledge" Integers boustrophedon Manual bi-directional printing. braindamaged Flags printers which can't reverse direction or print bi-directionally. knowstof? Is printer aware of form feeds ? knowsbold Can printer boldface ? knowsul? Can printer underline ? knowsos? Does printer prefer overstrike to backspace ? knowshmi? Does the printer use Diablo-like HMI setting ? ulinehack? Translate underlined whitespace to underline characters. _______________________________________________________________________________ Page Logistics Integers oldcountry What country is the LBP set to ? Only used by LBPmagic . rightstop Holds right carriage stop information for the Cat180 printer. steps/line Printer steps per line feed. Set only by typewriters. char/inch Print pitch. steps/inch Granularity of the HMI setting. gutter Left margin offset in halfchars. paperpos Location where paper is 'top-of-form'. papershort Number of lines missing from the page ? footpos Offset from bottom of page to line which holds page number. _______________________________________________________________________________ Print State Integers pageprint Single page printing flag. backwards Currently printing backwards ? oddhalfspace True if next halfspace is odd. Set to false when finished with a line. prcol Current print column. prcol = 0 corresponds to ruler colume zero. prline Half-line on the current page ? underlined Currently underlining ? bolded Currently bolding ? stopprint Switch used for early escape from Print . Used either during single page mode or for a panic print stop. _______________________________________________________________________________ unbuild Integers prchar Holds either the current print code (number from $000-$109), or an unknown 2 byte value from the printer table. proldflags lbuff flags byte for the previously printed character. prsmall? Is this a half character ? printed? Is this character part of the selection ? That is, is the invert bit in the lbuff information for this character on ? Only inverted characters are printed. prwhite? Is this character white ? pr\bold/ Is this a font transition ? pr\uline> Is this an underlining transition ? _______________________________________________________________________________ Printing Integers printlimit Address of the last lbuff character to print. Set up by startline . printnext Address of the next character to print in lbuff . This address is set up by startline and bumped either forward or backward by unbuild . printnext will be bumped forward if forward printing is used or backward otherwise. _______________________________________________________________________________ Character Selection Integers printertable Holds the address of the printer table for the printer currently in use. unbuildtable Contains the address of the vanilla.unbuild table. This table is used to translate from lbuff character code to a printer table character code. _______________________________________________________________________________ Printer Execution Vectors 'weirdprint Holds execution vector which handles printer table values from $00xx - $1Fxx. This vector is initialized with the token for printerror . 'docbreak Holds execution vector for printing document breaks. This vector is initialized with the token of noop. _______________________________________________________________________________ SetUp Integer whichprinter Printer usage flag: 0=main printer, 1=alternate printer. The printer setup export words, especially printerinfo , check this integer to determine how they should function. _______________________________________________________________________________ Print Integers (constants) $214 integer printsize Size of a print table. _______________________________________________________________________________ 22. SetUp _______________________________________________________________________________ _______________________________________________________________________________ Modifications: Initial Draft LC 7/20/87 More LC 8/1/87 Added JOB's stuff PB 9/11/87 _______________________________________________________________________________ _______________________________________________________________________________ Overview: The Setup command is used to adjust settings for document parameters and for connections to the Cat. The command operates on a data vector which contains the current settings. This vector is saved in the battery-backed-up RAM so that the user's settings are preserved across power off. _______________________________________________________________________________ Setup Data Structures Setup has three data structures, two vectors and a two dimensional array. The vectors are a matched pair with one holding executable tokens and the other setup data. The array governs the logic flow, screen display lines and indicies of which series of tokens to execute. The Token and Data Vectors Setup has two n word data vectors, one, called , is in ROM and the other, called setdata, is in RAM. Each screen line of information in the setup command has a forth word associated with it and each word's token is in the token vector in ROM. The RAM vector is matched to and holds the corresponding data for each setup line. Where a setup screen line is a display only line, setdata has only filler data in that element. When the user interacts with an executing setup display word, the data generated goes into the matching location in setdata. The Groups Array and Logic Flow in Setup The logic flow in setup is controlled by data in the 'groups' array. The groups array is a 5 column by n row array where each element consists of two bytes. The rows in the groups array contain two kinds of information: what setup group information to display where and which setup group to go to next. The display information consists of the first and last indexes into the matched token/data vectors and the first screen display line to use for each group of setup information. The information on which setup to go to next governs the general logic flow of setup. Most of the information in groups is fixed at compile time but part of it is set during setup execution depending on what the user selects. Setup Data Initialization During power-up initialization, the information in the data structures is loaded from battery RAM if the information is intact and if not the structures are initialized from ROM. After each user use of setup, the battery RAM is updated and verified. (Add more about the DISK command.) Displayed Screen Data The forth words whose tokens are in are executed in two modes governed by a flag called cflag. If cflag is off (zero) the words only display their information and if cflag is on (non-zero) the words display their information in bold font and can interact with the user to obtain data. Not surprisingly, most of the words in setup examine or manipulate cflag. General Operation When the user presses the SETUP command the word Setup is called by the edde interpreter. The first part of Setup checks and initializes various things and then the main setup begin loop is entered. The main loop is a state machine that picks parameters to use and the next state to execute from the groups array. This loop never finishes as the SETUP command must exit whenever the user releases the USE FRONT key and this may occur during parameter entry. The release of the either USE FRONT key is detected right at the begining of the word scode which then calls exitsetup , sets up the Cat via setupcat and returns to the user. _______________________________________________________________________________ 23. Key to Index _______________________________________________________________________________ A description of each word or integer can be found at the end of the specified chapter. Chapter Number Chapter Title 1 Pointers and Data Structures 2 Text Display 3 Ruler Display 4 The Cursor 5 What's in the Text 6 Typing, Erasing, and Copying Text 7 Character Style 8 Paragraph Format 9 Document Format 10 Leap 11 Drag 12 Copyup 13 The Keyboard Interface and Learn Command 14 The Sort Command 15 Calc Command and Data Structures 16 Spelling Checker 17 Explain 18 Titles 19 Disk 20 Communications 21 Print 22 Setup _______________________________________________________________________________ _______________________________________________________________________________ Word Index _______________________________________________________________________________ !char 13 #defaults 8 #key? 13 $< 14 ,accents 21 ,chars 21 ,unbuild 21 0-cmd 13 1-cmd 13 2-cmd 13 3-cmd 13 4-cmd 13 5-cmd 13 6-cmd 13 7-cmd 13 8-cmd 13 9-cmd 13 <> 13 <> 21 13 4 4 13 21 21 21 10 > 10 5 10 >lbuff 3 >status 3 ?auto 13 ?ctl 13 ?ev 13 ?expanded 4 ?extended 4 ?k 13 ?kb2 13 ?keystep 13 ?kval 13 ?lex 13 ?panic 13 ?rex 13 ?shift 13 ?shifted 13 ?shiftlock 13 ?split 4 ?t 13 @digit 14 @k 13 accent 5 accentable? 5 addtab 8 adjust 1 adjustleaprange 9 advanceptr 10 afrikaans.dw 21 aftererase 6 AltPrint 21 ap100setup 21 ap300setup 21 ap400setup 21 apsetup 21 ascii.dw 21 aSort 14 attribable? 5 attribregion 7 attribute 5 backspace 21 badivl 1 bare? 5 bj80docbreak 21 BJ80.printer 21 bj80setup 21 bjsecond.dw 21 bl# 3 blink 4 Bold 7 bos-display 2 break? 5 brk+ 5 build 2 buildlist 14 buildtable 10 canada.dw 21 capregion 7 cat180setup 21 CATdocbreak 21 cformat1 7 cformat2 7 cformat3 7 ch 21 checkgauge 3 checkline# 3 checklocallight 9 checksigns 14 clear-auto 13 clearundo 1 clr-kbd 13 collapse 2 comparenumbers 14 comparestrings 14 continueinsert? 6 copypkt 5 copyup 12 countlist 14 countries 21 cursorline 4 cursoroff 4 cursoron 4 daisy.printer 21 daisymagic 21 daisyoverstrike 21 Defindent 8 Defjustify 8 Defleft 8 Defright 8 Defspacing 8 Deftabs 8 deltab 8 differs? 2 disp 2 display 2 do-event 13 do-lex 10 Doclock 9 down? 13 dpktbytes 5 drag-backward 11 drag-forward 11 dSort 14 DW.countries 21 end-drag 11 end-search 10 enoughtext 6 eos-display 2 eou 1 Erase 6 ETWdocbreak 21 extend 4 extendedcursor 4 extramods 7 findchar 1 findds 9 findfield 14 findline 1 findnarrow 4 findpkt 5 findsplit 4 findwide 4 findwidth 4 finish-lex 10 firstbreak 5 fit-display 2 fixcursor 4 fixindent 8 fixivl 1 fixjustify 8 fixleft 8 fixright 8 fixspacing 8 fixtabs 8 formfeed 21 fpkt? 5 france.dw 21 fx80magic 21 fx80.printer 21 fx80setup 21 german.dw 21 getdocpkt 5 getdpkt 5 getkey 8 getpkt 5 getstring 14 getwidth 4 gobble 6 goodivl 1 halfdisp 2 halfspace 21 hideivls 1 holland.dw 21 Indent 8 indicate 3 init-lex 10 initprinter 21 initruler 3 initset 8 Insert 6 insertblock 6 insertrec 14 inwindow 2 italy.dw 21 japan.dw 21 Justify 8 Kb1/2 13 key 13 killivls 1 KillPrint 21 knownplace 1 largestrec 14 lastbreak 5 lastknownline 1 latin.dw 21 LBP.printer 21 lbp8setup 21 LBPdocbreak 21 LBPmagic 21 LBPpaper 21 LBPpaper 21 lbpsmarts 21 lbuffend 21 Learn 13 learnsize 13 learnstrings 13 leave-extended 10 leave-extended 4 Left 8 lex-scroll 10 lex-tap 10 line>ivl 1 loadline 2 local/global 9 lockedrange? 9 lockedsel 9 lockedtext? 9 lowercase 7 lrncmd 13 makedpkt 5 makepkt 5 makeprinttable 21 makespace 5 marginloop 8 maxundo 5 motion 21 move&adjusttext 12 movenotwith 7 movepkt 5 moverecord 14 moverecords 14 moveunsorted 14 movewith 7 narrowcursor 4 narrowcursor? 4 nearinterval 1 nearivl 1 new-display 2 newapsetup 21 newgauge? 3 newhalfline 21 newlearn 13 newline 21 newnode 14 newpage? 21 newplayback 13 nextab 8 nextbrk 5 nextchar 5 nextdoc 1 nextdsorcalc 5 nextfield 14 nextivl 1 nextpage 1 nextrecord 14 noprintersetup 21 norway.dw 21 notyet 21 numberkeys 13 os 21 overstrike 21 page#string 21 page? 5 pagebreak 21 pagebreak? 21 paperlength 21 partknown 1 patchprint 21 pattadd 10 pattdel 10 pbpat 10 pbuf! 21 pformat1 8 pformat2 8 pickprinter 21 pktbytes 5 playback 13 playback? 13 portugal.dw 21 postshuffle 14 preform 8 preshuffle 14 presort 14 prevbrk 5 prevchar 5 prevdoc 1 previvl 1 prevpage 1 prevpkt? 14 prevrec 14 prevwrap 1 print 21 Print 21 print" 21 print.buf.free 21 print.empty 21 print.parallel 21 print.serial 21 printblanks 21 printc 21 printercode 21 printerinfo 21 printerport 21 printerror 21 printers 21 printfooter 21 printify 21 printline 21 printposition 21 pushpos 1 put" 21 putivl 1 quicksort 14 quit.print 21 qume.dw 21 real? 4 realign 1 record 13 redisplay 2 redoc 9 redosort 14 reform 8 refresh 2 regobble 6 render 21 repos 8 research 10 resetcursor 4 resetselection? 6 restore.print 21 restoreselection 6 rewindow 2 Right 8 rotatepkts 5 rule 3 samepkt? 5 savedpkts 5 savepkts 5 savepos 1 savepos2 1 scansublist 14 scrollback 2 scrolldown 2 scrollfwd 2 scrollup 2 search< 10 search> 10 searching 10 seektime 21 seenlines 1 selected 2 selectionsort 14 selsize 1 set-auto 13 setcountry 21 setlearn 13 setpointer 14 setprinter 21 short? 21 showlearn 13 showpage 21 shuffle 14 signed? 14 skippage 21 sort 14 Spacing 8 spain.dw 21 special.dw 21 splitcursor 4 start-search 10 startline 21 stepahead 2 stepback 2 storeline 2 swapdpkts 5 swaplinks 14 swappkt 5 swappkts 5 swappos 1 swappos2 1 swaprecs 14 sweden.dw 21 swiss.dw 21 sync-shiftkeys 13 tabloop 8 Tabs 8 tapmove 10 thislearn 13 tocol 21 toline 21 toshiftlock 13 trim1 21 trim2 21 trim3 21 trim4 21 trimline 21 trimselection 6 uk.dw 21 unbuild 21 Uncformat 7 Under 7 undoclock 9 undolocal/global 9 undosort 14 unformat 8 ungobble 6 unmove 10 unpackcopiedup 12 UnPanicPrint 21 UnPrint 21 unscroll 10 unvtline 8 update! 1 update? 1 Upper 7 uppercase 7 usa.dw 21 vanilla.unbuild 21 visible? 2 vtline 8 w,'s 21 wheel>country 21 wheel>iso 21 white? 21 widecursor 4 widecursor? 4 wrap 1 wrapthru 1 XXX 21 ^dfmt 5 ~disp 2 ^fmt> 5 ^nextchar 5 ^prevchar 5 ~showrule 3 ~showstatus 3 ^sk< 5 ^sk> 5 _______________________________________________________________________________ Integers Index _______________________________________________________________________________ #above 1 #below 1 #ctrl 1 #goldenbytes 3 #goldenmodes 3 #indent 1 #ipage 1 #iprint 1 #itbl 1 #iwide 1 #just 1 #learns 13 #left 1 #ln 1 #lnl 1 #lock 1 #long 1 #lsp 1 #nextwr 1 #oldlsp 1 #pctrl 1 #pg 1 #pgl 1 #spr 1 #tabs 1 #update 1 #wide 1 #wr 1 #wtable 1 $bold 2 $dln 2 $end 2 $half 2 $inv 2 $uln 2 %above 1 %below 1 %indent 1 %ipage 1 %iprint 1 %iwide 1 %just 1 %left 1 %ln 1 %lnl 1 %lock 1 %long 1 %lsp 1 %oldlsp 1 %page 1 %pgl 1 %pwrap 1 %spr 1 %tabs 1 %wide 1 %wr 1 &attr 5 &calc 5 &dln 5 &firstacc 5 &firstcmd 5 &firsthid 5 &fmt 5 &horiz 2 &lastacc 5 &lastasc 5 &lastchr 5 &lastcmd 5 &lockedcalc 5 &skip 5 /lscan 2 /scan 2 ?char 13 active/scan 2 auto 13 beot 1 beotivl 1 blackruler 3 blinktime 4 boldbit 2 bos 1 bosl 14 bosptr 4 bosptr 2 bot 1 bou 1 bytes/char 2 bytes/image 2 bytes/line 2 cbuff 13 char 13 cpos 4 cstate 1 curlearn 13 cursor? 4 cursorblock 4 cursorbuf 4 cursorstate 4 cwidth 4 cx 4 cy 4 descending 14 disktext 1 dlinebit 2 dpktsize 5 ds 2 endtext 1 endtextivl 1 eos 1 eosl 14 eosline 1 eosptr 4 eosptr 2 eot 1 eou 1 esize 1 firstseen 1 flen 14 foffset 14 gap 1 gapivl 1 gapline 1 gaugepos 3 goldbytes 3 goldenbytes 3 height 2 hrulercursor 4 indichars 3 inptr 13 invbit 2 isize 1 itblsize 1 kcodes 13 kstat 13 ktime 13 kval 13 lastkey 8 lastline 1 lastseen 1 lbound 8 lbuflen 2 lbufwide 2 lbufwidth 2 learnbuff 13 learning? 13 learnpos 13 lines 1 lines/screen 2 lip 14 logbytes/char 2 lok 2 lpp 14 markbl 2 markpoint 1 maxlearn 13 middle 1 modechars 3 modifiers 13 ncursorimage 4 offtime 4 oldbos 1 oldbos2 1 oldcstate 1 oldcstate2 1 oldeos 1 oldeos2 1 oldgauge 3 oldlnl 3 oldop 1 oldop2 1 oldpop 1 oldpop2 1 oldtopline 1 oldtopline2 1 olink 14 ontime 4 op 1 outptr 13 p 1 pagebase 1 pages 1 panicked 13 pb 2 pgs 1 pktsize 5 po 1 pop 1 posit 8 rbound 8 recaddr 14 reclen 14 rsize 14 rtn 2 rulerblink? 4 ruleredge 3 rulersmarts 3 rulerstart 3 scancode 13 scans/char 2 scans/image 2 shiftstate 13 smallbit 2 sortbreaks 14 sorttable 14 statbuff 3 statuslights 3 stopbit 2 tab# 14 tab0 2 tab1 2 tabspace 2 text 1 thiskey 8 ticks 13 time0 13 time1 13 tophalf 2 topline 1 ulinebit 2 ulink 14 undolist 14 upp 14 vbheight 8 vtbuff 8 wcursorimage 4 width 2 wraplim 1 [ FINIS ]