C H A P T E R 4 |
Using Forth Tools |
This chapter introduces Forth as it is implemented in OpenBoot. Even if you are familiar with the Forth programming language, work through the examples shown in this chapter; they provide specific, OpenBoot-related information.
The version of Forth contained in OpenBoot is based on ANS Forth. Appendix E lists the complete set of available commands. Words that are specifically used for writing OpenBoot FCode programs for SBus devices are described in the manual, Writing FCode 2.x Programs .
Forth has a very simple command structure. Forth commands, also called Forth words, consist of any combination of characters that can be printed--for example, letters, digits, or punctuation marks. Examples of legitimate words are shown below:
To be recognized as commands , Forth words must be separated by one or more spaces (blanks). Pressing Return at the end of any command line executes the typed commands. (In all the examples shown, a Return at the end of the line is assumed.)
A command line can have more than one word. Multiple words on a line are executed one at a time, from left to right, in the order in which they were typed. For example:
In OpenBoot, uppercase and lowercase letters are equivalent. Therefore, testa , TESTA , and TesTa all invoke the same command. However, words are conventionally written in lowercase.
Some commands generate large amounts of output (for example, dump or words ). You can interrupt such a command by pressing any key except q . (If you press q , the output is aborted, not suspended.) Once a command is interrupted, output is suspended and the following message appears:
Press the space bar ( <space> ) to continue, press Return ( <cr> ) to output one more line and pause again, or type q to abort the command. When you are generating more than one page of output, the system automatically displays this prompt at the end of each page.
Enter a number by typing its value, for example, 55 or -123. Forth accepts only integers (whole numbers); fractional values (for example, 2/3) are not allowed. A period at the end of a number signifies a double number. Periods or commas embedded in a number are ignored, so 5.77 is understood as 577. By convention, such punctuation usually appears every four digits. Use one or more spaces to separate a number from a word or from another number.
OpenBoot performs 32-bit integer arithmetic, and all numbers are 32-bit values unless otherwise specified.
Although OpenBoot implementations are encouraged to provide a hexadecimal conversion radix, they are not required to do so. So, you must establish such a radix if your code depends on a given base for proper operation.
You can change the operating number base with the commands octal , decimal and hex which cause all subsequent numeric input and output to be performed in base 8, 10 or 16, respectively.
For example, to operate in decimal, type:
To change to hexadecimal type:
Two simple techniques for identifying the active number base are:
The 16 and the f on the display show that you are operating in hexadecimal. If 10 and 9 showed on the display, it would mean that you are in decimal base. 8 and 7 would indicate octal.
The Forth stack is a last-in, first-out buffer used for temporarily holding numeric information. Think of it as a stack of books: the last one you put on the top of the stack is the first one you take off. Understanding the stack is essential to using Forth .
To place a number on the stack, simply type its value.
ok 44 (The value 44 is now on top of the stack) ok 7 (The value 7 is now on top, with 44 just underneath) ok |
The contents of the stack are normally invisible. However, properly visualizing the current stack contents is important for achieving the desired result. To show the stack contents with every ok prompt, type:
The topmost stack item is always shown as the last item in the list, immediately before the ok prompt. In the above example, the topmost stack item is 8 .
If showstack has been previously executed, noshowstack will remove the stack display prior to each prompt.
Nearly all words that require numeric parameters fetch those parameters from the top of the stack. Any values returned are generally left on top of the stack, where they can be viewed or consumed by another command. For example, the Forth word + removes two numbers from the stack, adds them together, and leaves the result on the stack. In the example below, all arithmetic is in hexadecimal.
Once the two values are added together, the result is put onto the top of the stack. The Forth word . removes the top stack item and displays that value on the screen. For example:
53 ok 12 53 12 ok . 12 53 ok . 53 ok (The stack is now empty) ok 3 5 + . 8 ok (The stack is now empty) ok . Stack Underflow ok |
To aid understanding, conventional coding style requires that a stack diagram of the form ( -- ) appears on the first line of every definition of a Forth word. The stack diagram specifies what happens to the stack with the execution of the word.
Entries to the left of -- show stack items that are consumed (i.e. removed) from the stack and used by the operation of that word. Entries to the right of -- show stack items that are left on the stack after the word finishes execution. For example, the stack diagram for the word + is: ( nu1 nu2 -- sum ) , and the stack diagram for the word . is: ( nu -- ) . Therefore, + removes two numbers ( nu1 and nu2 ), then leaves their sum ( sum ) on the stack. The word . removes the number on the top of the stack (nu) and displays it.
Words that have no effect on the contents of the stack (such as showstack or decimal ), have a ( -- ) stack diagram.
Occasionally, a word will require another word or other text immediately following it. For example, the word see , used in the form see thisword ( -- ).
Stack items are generally written using descriptive names to help clarify correct usage. See for stack item abbreviations used in this manual.
Stack manipulation commands (described in ) allow you to add, delete, and reorder items on the stack.
A typical use of stack manipulation might be to display the top stack item while preserving all stack items, as shown in this example:
5 77 ok dup (Duplicates the top item on the stack) 5 77 77 ok . (Removes and displays the top stack item) 77 5 77 ok |
Forth provides an easy way to create custom definitions for new command words. shows the Forth words used to create custom definitions.
Definitions for new commands are called colon definitions, named after the word : that is used to create them. For example, suppose you want to create a new word, add4 , that will add any four numbers together and display the result. You could create the definition as follows:
The ; (semicolon) marks the end of the definition that defines add4 to have the behavior ( + + + . ). The three addition operators ( + ) reduce the four stack items to a single sum on the stack; then . removes and displays that result. An example follows.
Definitions are stored in local memory, which means they are erased when a system resets. To keep useful definitions, put them into a text file (using a text editor under your operating system or using the NVRAMRC editor). This text file can then be loaded as needed. (See Chapter 5 , for more information on loading files.)
When you type a definition from the User Interface, the ok prompt becomes a ] (right square bracket) prompt after you type the : (colon) and before you type the ; (semicolon). For example, you could type the definition for add4 like this:
Every definition you create (in a text file) should have a stack effect diagram shown with that definition, even if the stack effect is nil ( -- ). This is vital because the stack diagram shows the proper use of that word. Also, use generous stack comments within complex definitions; this helps trace the flow of execution. For example, when creating add4 , you could define it as:
Or you could define add4 as follows:
The commands listed in perform basic arithmetic with items on the data stack.
The User Interface provides interactive commands for examining and setting memory. Use the User Interface to:
Memory operators let you read from and write to any memory location. All memory addresses shown in the examples that follow are virtual addresses.
A variety of 8-bit, 16-bit, and 32-bit operations are provided. In general,
a
c
(character) prefix indicates an 8-bit (one byte) operation; a
w
(word) prefix indicates a 16-bit (two byte) operation; and an l (longword) prefix indicates a 32-bit (four byte) operation.
Note - "l" is sometimes printed in uppercase to avoid confusion with 1 (the number one). |
waddr , qaddr , and addr64 indicate addresses with alignment restrictions. For example, qaddr indicates 32-bit (4 byte) alignment; so this address must be evenly divisible by 4, as shown in the following example:
The Forth interpreter implemented in OpenBoot adheres closely to the ANS Forth Standard. If you explicitly want a 16-bit fetch or a 32-bit fetch, use w@ or L@ instead of @ . Other commands also follow this convention.
lists the commands used to access memory.
The dump command is particularly useful. It displays a region of memory as both bytes and ASCII values. The example below displays the contents of 20 bytes of memory starting at virtual address 10000. It also shows you how to read from and write to a memory location.
If you try (with @ , for example) to access an invalid memory location, the operation immediately aborts and the PROM displays an error message, such as Data Access Exception or Bus Error .
lists memory mapping commands.
Allocate and map size bytes of available memory; return the virtual address. |
||
The following screen is an example of the use of alloc-mem and free-mem .
alloc-mem allocates 4000 bytes of memory, and the starting address ( ffef7a48 ) of the reserved area is displayed.
Finally, free-mem returns the 4000 allocated bytes of memory starting at ffef7a48 .
An example of using memmap is shown below.
Here is a general method for mapping an SBus device from the ok prompt, without the necessity of knowing system-dependent device addresses. This method does not depend on the presence of a valid FCode PROM on the SBus device. The method will work on any OpenBoot system version 2.0 or higher.
For example, to inspect the FCode PROM for a device in slot #3 of a system, enter:
ok " /sbus" select-dev ok 0 3 1000 map-in .s ffed3000 ok dup 20 dump (Dump of first 20 bytes of FCode PROM) ok |
Here are some variations to the method:
On some systems, the pathname for the system SBus may vary. For example, " /iommu/sbus" (for Sun4m) or " /io-unit/sbi" (for Sun4d). The show-devs command from the ok prompt (which lists all system devices) is one way to determine the correct path.
Direct placement of (offset size) on the stack may or may not work in the most general cases on future systems. If you encounter problems, try the following, more general approach:
The dictionary contains all the available Forth commands. Defining words are used to create new Forth commands.
Defining words require two stack diagrams. The first diagram shows the stack effect when the new command is created. The second (or "Usage:") diagram shows the stack effect when that command is later executed.
lists the defining words that you can use to create dictionary entries.
Define a word for forward references or execution vectors using execution token. |
||
( offset size -- offset+size )
|
||
You can use the defining word constant to create a name whose value will not change. A simple colon definition : foo 22 ; accomplishes a similar result.
value lets you assign a name to any number. Later execution of that name leaves the assigned value on the stack. The following example assigns a value of 22 to a word named foo , and then calls foo to use its assigned value in an arithmetic operation.
The value can be changed with the dictionary compiling word is . For example:
Commands created with value are convenient, because you do not have to use @ every time you want the number.
The defining word variable assigns a name to a 32-bit region of memory, which you can use to hold values as needed. Later execution of that name leaves the address of the memory on the stack. Typically, @ and ! are used to read or write at that address. For example:
The defining word defer lets you change the execution of previously defined commands, by creating a slot which can be loaded with different functions at different times. For example:
ok hex ok defer printit ok ['] .d to printit ok ff printit 255 ok : myprint ( n -- ) ." It is " .h ] ." in hex " ; ok ['] myprint to printit ok ff printit It is ff in hex ok |
The dictionary contains all the available Forth commands. lists tools you can use to search the dictionary.
see , used in the form see thisword , decompiles the specified command (that is, it shows the definition used to create thisword ). The decompiled definition may sometimes be confusing, because some internal names may have been omitted from the PROM's symbol table to save space.
The following screen is an example of how to use sifting .
words displays all the word (command) names in the dictionary, starting with the most recent definitions.
The commands listed in control the compilation of data into the dictionary.
shows basic commands to display stack values.
Turn off automatic display of the stack before each ok prompt |
||
The .s command displays the entire stack contents without disturbing them. It can be safely used at any time for debugging purposes. (This is the function that showstack performs automatically.)
You can change the operating number base using the commands in .
The d# , h# and o# commands are useful when you want to input a specific number in another base without explicitly changing the current base. For example:
The .d and .h commands act like " . " but display the value in decimal or hexadecimal, respectively, regardless of the current base setting. For example:
This section describes text input and output commands. These commands control strings or character arrays, and allow you to enter comments and control keyboard scanning.
lists commands to control text input.
Comments are used with Forth source code (generally in a text file) to describe the function of the code. The ( (open parenthesis) is the Forth word that begins a comment. Any character up to the closing parenthesis ) is ignored by the Forth interpreter. Stack diagrams are one example of comments using (.
Note - Remember to follow the( with a space, so that it is recognized as a Forth word. |
\ (backslash) indicates a comment terminated by the end of the line of text.
key waits for a key to be pressed, then returns the ASCII value of that key on the stack.
ascii , used in the form ascii x , returns on the stack the numerical code of the character x .
key? looks at the keyboard to see if the user has recently pressed any key. It returns a flag on the stack: true if a key has been pressed and false otherwise. See Conditional Flags for a discussion on the use of flags.
lists general-purpose text display commands.
cr sends a carriage-return character to the output. For example:
emit displays the letter whose ASCII value is on the stack.
shows commands used to manipulate text strings.
Some string commands specify an address (the location in memory where the characters reside) and a length (the number of characters in the string). Other commands use a packed string or pstr , which is a location in memory containing a byte for the length, immediately followed by the characters. The stack diagram for the command indicates which form is used. For example, count converts a packed string to an address-length string.
The command ." is used in the form: ." string " . It outputs text when needed. A " (double quotation mark) marks the end of the text string. For example:
Normally, your system uses a keyboard for all user input, and a frame buffer with a connected display screen for most display output. (Server systems may use an ASCII terminal connected to a system serial port. For more information on how to connect a terminal to the system unit, see your system's installation manual.) You can redirect the input, the output, or both, to either one of the system's serial ports. This may be useful, for example, when debugging a frame buffer.
lists commands you can use to redirect input and output.
The commands input and output temporarily change the current devices for input and output. The change occurs when you enter a command; you do not have to reset your system. A system reset or power cycle causes the input and output devices to revert to the default settings specified in the NVRAM configuration parameters input-device and output-device . These parameters can be modified, if needed (see Chapter 3 for information about changing defaults).
input must be preceded by one of the following: keyboard , ttya , ttyb , or device-specifier text string. For example, if input is currently accepted from the keyboard, and you want to make a change so that input is accepted from a terminal connected to the serial port TTYA, type:
At this point, the keyboard becomes non-functional (except for Stop-A ), but any text entered from the terminal connected to TTYA is processed as input. All commands are executed as usual.
To resume using the keyboard as the input device, use the terminal keyboard to type:
Similarly, output must be preceded by one of the following: screen , ttya , or ttyb . For example, if you want to send output to TTYA instead of the normal display screen, type:
The screen does not show the answering ok prompt, but the terminal connected to TTYA shows the ok prompt and all further output as well.
io is used in the same way, except that it changes both the input and output to the specified place.
Generally, input , output , and io take a device-specifier , which can be either a device path name or a device alias. The device must be specified as a Forth string, using double quotation marks ( " ), as shown in the two examples below:
In the preceding examples, ttya , screen , and keyboard are Forth words that put their corresponding device alias string on the stack.
OpenBoot specifies a required command line editor (similar to EMACS, a common text editor), some optional extensions and an optional history mechanism for the User Interface. Use these powerful tools to re-execute previous commands without retyping them, to edit the current command line to fix typing errors, or to recall and change previous commands.
lists the required line-editing commands available at the ok prompt.
Finishes editing of the line and submits the entire visible line to the interpreter regardless of the current cursor position. |
The OpenBoot standard also describes three groups of extensions of these capabilities. lists the command line editing extension group.
The command line history extension enables previously-typed commands to be saved in an EMACS-like command history ring that contains at least 8 entries. Commands may be recalled by moving either forward or backward around the ring. Once recalled, a command may be edited and/or re-submitted (by typing the Return key). The command line history extension keys are:
Selects and displays the previous command in the command history ring. |
|
Selects and displays the next command in the command history ring. |
|
The command completion extension enables the system to complete long Forth word names by searching the dictionary for one or more matches based upon the already-typed portion of a word. After you type in a portion of a word followed by the command completion keystroke, Control-Space , the system responds as follows:
If the system finds exactly one matching word, the remainder of the word is automatically displayed.
If the system finds several possible matches, it displays all characters common to all possibilities.
If the system cannot find a match for the already-typed characters, it deletes characters from the right until there is at least one match for the remaining characters.
The command completion extension keys are:
Forth conditionals use flags to indicate true/false values. A flag can be generated in several ways, based on testing criteria. The flag can then be displayed from the stack with the word ".", or it can be used as input to a conditional control command. Control commands can cause one response if a flag is true and another if it is false. Thus, execution can be altered based on the result of a test.
A 0 value indicates that the flag value is false . A -1 or any other non-zero number indicates that the flag value is true . (In hexadecimal, the value -1 is displayed as ffffffff .)
lists commands that perform relational tests, and leave a true or false flag result on the stack.
> takes two numbers from the stack, and returns true ( -1 ) on the stack if the first number was greater than the second number, or returns false ( 0 ) otherwise. An example follows:
0= takes one item from the stack, and returns true if that item was 0 or returns false otherwise. This word inverts any flag to its opposite value.
The following sections describe words used within a Forth program to control the flow of execution.
The commands if , then and else provide a simple control structure.
The commands listed in control the flow of conditional execution.
The format for using these commands is:
The if command consumes a flag from the stack. If the flag is true (non-zero), the commands following the if are performed. Otherwise, the commands (if any) following the else are performed.
ok : testit ( n -- ) ] 5 > if ." good enough " ] else ." too small " ] then ] ." Done. " ; ok ok 8 testit good enough Done. ok 2 testit too small Done. ok |
Note - The ] prompt reminds you that you are part way through creating a new colon definition. It reverts to ok after you finish the definition with a semicolon. |
A high-level
case
command is provided for selecting alternatives with multiple possibilities. This command is easier to read than deeply-nested
if...then
commands.
lists the conditional case commands.
Here is a simple example of a case command:
A begin loop executes the same commands repeatedly until a certain condition is satisfied. Such a loop is also called a conditional loop.
lists commands to control the execution of conditional loops.
Begin a begin...while...repeat , begin...until , or begin...again loop. |
||
Continue executing a
begin...while...repeat
loop while
|
In both cases, the commands within the loop are executed repeatedly until the proper flag value causes the loop to be terminated. Then execution continues normally with the command following the closing command word ( until or repeat ).
In the begin...until case, until removes a flag from the top of the stack and inspects it. If the flag is false , execution continues just after the begin , and the loop repeats. If the flag is true , the loop is exited.
In the begin...while...repeat case, while removes a flag from the top of the stack and inspects it. If the flag is true , the loop continues by executing the commands just after the while . The repeat command automatically sends control back to begin to continue the loop. If the flag is false when while is encountered, the loop is exited immediately; control goes to the first command after the closing repeat .
An easy aid to memory for either of these loops is: If true, fall through.
ok begin 4000 c@ . key? until (repeat until any key is pressed) 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 ok |
The loop starts by fetching a byte from location 4000 and displaying the value. Then, the key? command is called, which leaves a true on the stack if the user has pressed any key, and false otherwise. This flag is consumed by until and, if the value is false , then the loop continues. Once a key is pressed, the next call to key? returns true , and the loop terminates.
Unlike many versions of Forth, the User Interface allows the interactive use of loops and conditionals -- that is, without first creating a definition.
A do loop (also called a counted loop) is used when the number of iterations of the loop can be calculated in advance. A do loop normally exits just before the specified ending value is reached.
lists commands to control the execution of counted loops.
This screen shows several examples of the ways in which loops are used.
contains descriptions of additional program execution control commands.
Return from the current word. (Cannot be used in counted loops.) |
||
abort causes immediate termination and returns control to the keyboard. abort" is similar to abort but is different in two respects. abort" removes a flag from the stack and only aborts if the flag is true . Also, abort" prints any desired message when the abort takes place.
eval takes a string from the stack (specified as an address and a length). The characters in that string are then interpreted as if they were entered from the keyboard. If a Forth text file has been loaded into memory (see Chapter 5 , then eval can be used to compile the definitions contained in the file.