OpenBoot Command Reference Manual
  Искать только в названиях книг
Загрузить это руководство в формате PDF

Using Forth Tools

4

This chapter introduces Forth as it is implemented in the OpenBoot firmware. 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 the OpenBoot firmware is based on Forth-83, with extensions. Appendix E, "Forth Word Reference," lists the complete set of available commands. Words that are specifically used for writing FCode programs for SBus devices are described in the manual Writing FCode Programs (part number 801-7050-10).

Note - This chapter assumes that you know how to enter and leave the Forth Monitor. At the ok prompt, if you type commands that hang the system and you cannot recover using Stop-A, you may need to perform a power cycle to return the system to normal operation.

Forth Commands

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:
@
dump
.
0<
+
probe-scsi

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:

  ok testa testb testc  
  ok  

is equivalent to:

  ok testa  
  ok testb  
  ok testc  
  ok  

In the OpenBoot implementation of Forth, uppercase and lowercase letters are equivalent. Therefore, testa, TESTA, and TesTa all invoke the same command.
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:

  More [<space>,<cr>,q] ?  

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.

Using Numbers

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 decimal point in a number is ignored, so 5.77 is understood as 577. Use one or more spaces to separate a number from a word or from another number.
The OpenBoot firmware performs 32-bit integer arithmetic, and all numbers are 32-bit values unless otherwise specified. Because hexadecimal (base 16) numbers are so commonly used, the Forth Monitor automatically interprets all numbers in hexadecimal, not decimal (base 10). Therefore, adding 8 and 7 returns the value f, not 15. You can change the operating number base.
The commands decimal and hex cause all subsequent numeric input and output to be performed in base 10 or base 16, respectively. To operate in decimal, type:

  ok decimal  
  ok  

To change to hexadecimal type:

  ok hex  
  ok  

To find out what number base is currently active, use the following example:

  ok 10 .d  
  16  
  ok  

The 16 on the display shows that you are operating in hexadecimal. If 10 showed on the display, it would mean that you are in decimal base.

The Stack

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  

Displaying Stack Contents

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:

  ok showstack  
  44 7 ok 8  
  47 7 8 ok showstack  
  ok  

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.
On older systems, showstack, once invoked, remains in effect until a machine reset takes place. Starting with release 2.6 of the OpenBoot PROM, you can toggle the showstack command on and off.

Note - In some of the examples in this chapter, showstack is enabled. In those examples, each ok prompt is immediately preceded by a display of the current contents of the stack. The examples work the same if showstack is not enabled, except that the stack contents are not displayed.

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.

  44 7 8 ok +  
  44 f ok +  
  53 ok  

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  

The Stack Diagram

To aid programming, there is a stack diagram in the form ( -- ) for every defined 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 (or 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: ( n1 n2 -- n3 ), and the stack diagram for the word "." is: ( n -- ). Therefore, + removes two numbers (n1 and n2), then leaves their sum (n3) on the stack. The word "." removes the number on the top of the stack (n) 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. The word see, used in the form see thisword ( -- ), is such an example.
Stack items are generally written using descriptive names to help clarify correct usage. See Table 4-1 for stack item abbreviations used in this manual.
Table 4-1
NotationDescription
|Alternate stack results, for example: ( input -- adr len false | result true ).
?Unknown stack items (changed from ???).
???Unknown stack items.
acfCode field address.
adrMemory address (generally a virtual address).
adr16Memory address, must be 16-bit aligned.
adr32Memory address, must be 32-bit aligned.
adr64Memory address, must be 64-bit aligned.
byte bxxx8-bit value (smallest byte in a 32-bit word).
char7-bit value (smallest byte), high bit unspecified.
cnt
len
size
Count or length.
Table 4-1 (Continued)
NotationDescription
flag xxx?0 = false; any other value = true (usually -1).
long Lxxx32-bit value.
n n1 n2 n3Normal signed values (32-bit).
+n uUnsigned, positive values (32-bit).
n[64]

(n.low n.hi)

Extended-precision (64-bit) numbers (2 stack items).
physPhysical address (actual hardware address).
pstrPacked string (adr len means unpacked string).
virtVirtual address (address used by software).
word wxxx16-bit value (smallest two bytes in a 32-bit word).

Manipulating the Stack

Stack manipulation commands (described in Table 4-2) allow you to add, delete, and reorder items on the stack.
Table 4-2
CommandStack DiagramDescription
-rot( n1 n2 n3 -- n3 n1 n2 )Inversely rotate 3 stack items.
>r( n -- )Move a stack item to the return stack. (Use with caution.)
?dup( n -- n n | 0 )Duplicate the top stack item if it is non-zero.
2drop( n1 n2 -- )Remove 2 items from the stack.
2dup( n1 n2 -- n1 n2 n1 n2 )Duplicate 2 stack items.
2over( n1 n2 n3 n4 -- n1 n2 n3 n4 n1 n2 )Copy second 2 stack items.
2rot( n1 n2 n3 n4 n5 n6 -- n3 n4 n5 n6 n1 n2 )Rotate 3 pairs of stack items.
2swap( n1 n2 n3 n4 -- n3 n4 n1 n2 )Exchange 2 pairs of stack items.
3drop( n1 n2 n3 -- )Remove 3 items from the stack.
3dup( n1 n2 n3 -- n1 n2 n3 n1 n2 n3 )Duplicate 3 stack items.
clear( ??? -- )Empty the stack.
depth( ??? -- ??? +n )Return the number of items on the stack.
drop( n -- )Remove top item from the stack.
dup( n -- n n )Duplicate the top stack item.
nip( n1 n2 -- n2 )Discard the second stack item.
Table 4-2 (Continued)
CommandStack DiagramDescription
over( n1 n2 -- n1 n2 n1 )Copy second stack item to top of stack.
pick( ??? +n -- ??? n2 )Copy +n-th stack item (1 pick = over).
r>( -- n )Move a return stack item to the stack. (Use with caution.)
r@( -- n )Copy the top of the return stack to the stack.
roll( ??? +n -- ? )Rotate +n stack items (2 roll = rot).
rot( n1 n2 n3 -- n2 n3 n1 )Rotate 3 stack items.
swap( n1 n2 -- n2 n1 )Exchange the top 2 stack items.
tuck( n1 n2 -- n2 n1 n2 )Copy top stack item below second item.
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  

Creating Custom Definitions

Forth provides an easy way to create custom definitions for new command words. Table 4-3 shows the Forth words used to create custom definitions.
Table 4-3
CommandStack DiagramDescription
: name( -- )Start creating a new definition.
;( -- )Finish creating a new definition.
Definitions for new commands are called colon definitions, named after the ":" word 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 would create the definition as follows:

  ok : add4  + + + .  ;  
  ok  

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.

  ok 1 2 3 3 + + + .  
  9  
  ok 1 2 3 3 add4  
  9  
  ok  

Definitions are stored in local memory, which means they are forgotten if a machine reset takes place. 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, "Loading and Executing Programs," for more information on loading files.)
When you type a definition in the Forth Monitor, 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:

  ok : add4  
  ]  + + +  
  ]  .  
  ]  ;  
  ok  

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:

  : add4  ( n1 n2 n3 n4 -- )  + + + .  ;  

Or you could define add4 as follows:

  : add4  ( n1 n2 n3 n4 -- )  
     + + +  ( sum )  
     .  
  ;  


Note - The ( (open parenthesis) is a Forth word meaning to ignore the following text up to ) (the closing parenthesis). Like any other Forth word, the open parenthesis must have one or more spaces following it.

Using Arithmetic Functions

The commands listed in Table 4-4 perform basic arithmetic with items on the data stack.
Table 4-4
CommandStack DiagramDescription
*( n1 n2 -- n3 )Multiply n1 * n2.
+( n1 n2 -- n3 )Add n1 + n2.
-( n1 n2 -- n3 )Subtract n1 - n2.
/( n1 n2 -- quot )Divide n1 / n2; remainder is discarded.
/mod( n1 n2 -- rem quot )Remainder, quotient of n1 / n2.
<<( n1 +n -- n2 )Left-shift n1 by +n bits.
>>( n1 +n -- n2 )Right-shift n1 by +n bits.
>>a( n1 +n -- n2 )Arithmetic right-shift n1 by +n bits.
*/( n1 n2 n3 -- n4 )n1 * n2 / n3.
*/mod( n1 n2 n3 -- rem quot )Remainder, quotient of n1 * n2 / n3.
1+( n1 -- n2 )Add 1.
1-( n1 -- n2 )Subtract 1.
2*( n1 -- n2 )Multiply by 2.
2+( n1 -- n2 )Add 2.
2-( n1 -- n2 )Subtract 2.
2/( n1 -- n2 )Divide by 2.
Table 4-4 (Continued)
CommandStack DiagramDescription
abs( n -- u )Absolute value.
aligned( n1 -- n2 )Round n1 up to the next multiple of 4.
and( n1 n2 -- n3 )Bitwise logical AND.
bounds( startadr len -- endadr startadr )Convert startadr len to endadr startadr for do loop.
bljoin( b.low b2 b3 b.hi -- long )Join four bytes to form a 32-bit longword.
bwjoin( b.low b.hi -- word )Join two bytes to form a 16-bit word.
flip( word1 -- word2 )Swap the bytes within a 16-bit word.
lbsplit( long -- b.low b2 b3 b.hi )Split a 32-bit longword into four bytes.
lwsplit( long -- w.low w.hi )Split a 32-bit longword into two 16-bit words.
max( n1 n2 -- n3 )n3 is maximum of n1 and n2.
min( n1 n2 -- n3 )n3 is minimum of n1 and n2.
mod( n1 n2 -- rem )Remainder of n1 / n2.
negate( n1 -- n2 )Change the sign of n1.
not( n1 -- n2 )Bitwise ones complement.
or( n1 n2 -- n3 )Bitwise logical OR.
u*x( u1 u2 -- product[64] )Multiply 2 unsigned 32-bit numbers; yield unsigned 64-bit product.
u/mod( u1 u2 -- un.rem un.quot )Divide unsigned 32-bit number by an unsigned 32-bit number; yield 32-bit remainder and quotient.
u2/( u1 -- u2 )Logical right shift 1 bit; zero shifted into vacated sign bit.
wbsplit( word -- b.low b.hi )Split 16-bit word into two bytes.
wflip( long1 -- long2 )Swap halves of 32-bit longword.
wljoin( w.low w.hi -- long )Join two words to form a longword.
x+( n1[64] n2[64] -- n3[64] )Add two 64-bit numbers.
x-( n1[64] n2[64] -- n3[64] )Subtract two 64-bit numbers.
xor( n1 n2 -- n3 )Bitwise exclusive OR.
xu/mod( u1[64] u2 -- rem quot )Divide unsigned 64-bit number by unsigned 32-bit number; yield 32-bit remainder and quotient.

Accessing Memory

The Forth Monitor provides interactive commands for examining and setting memory. Use the Forth Monitor to:
  • Read and write to any virtual address.
  • Map virtual addresses to physical addresses.
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).

adr16, adr32, and adr64 indicate addresses with alignment restrictions. For example, adr32 indicates 32-bit (4 byte) alignment; so this address must be evenly divisible by 4, as shown in the following example:

  ok 4028 L@  
  ok 4029 L@  
  Memory address not aligned  
  ok  

The Forth interpreter implemented in the OpenBoot firmware adheres closely to the Forth 83-Standard in most respects. One major exception is that the OpenBoot Forth implementation uses 32-bit numbers instead of 16-bit numbers. In most cases, this difference is not visible to you. For example, @ and ! (described in this section) work with variables as expected. 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.
Table 4-5 lists the commands used to access memory.
Table 4-5
CommandStack DiagramDescription
!( n adr16 -- )Store a 32-bit number at adr16, must be 16-bit aligned.
+!( n adr16 -- )Add n to the 32-bit number stored at adr16, must be 16-bit aligned.
<w@( adr16 -- n )Fetch signed 16-bit word at adr16, must be 16-bit aligned.
?( adr16 -- )Display the 32-bit number at adr16, must be 16-bit aligned.
@( adr16 -- n )Fetch a 32-bit number from adr16, must be 16-bit aligned.
2!( n1 n2 adr16 -- )Store 2 numbers at adr16, n2 at lower address, must be 16-bit aligned.
2@( adr16 -- n1 n2 )Fetch 2 numbers from adr16, n2 from lower address, must be 16-bit aligned.
blank( adr u -- )Set u bytes of memory to space (decimal 32).
c!( n adr -- )Store low byte of n at adr.
c?( adr -- )Display the byte at adr.
c@( adr -- byte )Fetch a byte from adr.
cmove( adr1 adr2 u -- )Copy u bytes from adr1 to adr2, starting at low byte.
cmove>( adr1 adr2 u -- )Copy u bytes from adr1 to adr2, starting at high byte.
cpeek( adr -- false | byte true )Fetch the byte at adr. Return the data and true if the access was
successful. Return false if a read access error occurred.
cpoke( byte adr -- okay? )Store the byte to adr. Return true if the access was successful.
Return false if a write access error occurred.
comp( adr1 adr2 len -- n )Compare two byte arrays, n = 0 if arrays are identical, n = 1 if first byte that is different is greater in array#1, n = -1 otherwise.
d!( n1 n2 adr64 -- )Store two 32-bit numbers at adr64, must be 64-bit aligned. Order is implementation-dependent.
d?( adr64 -- )Display the two 32-bit numbers at adr64, must be 64-bit aligned. Order is implementation-dependent.
d@( adr64-- n1 n2 )Fetch two 32-bit numbers from adr64, must be 64-bit aligned. Order is implementation-dependent.
dump( adr len -- )Display len bytes of memory starting at adr.
erase( adr u -- )Set u bytes of memory to 0.
fill( adr size byte -- )Set size bytes of memory to byte.
L!( n adr32 -- )Store a 32-bit number at adr32, must be 32-bit aligned.
Table 4-5 (Continued)
CommandStack DiagramDescription
L?( adr32 -- )Display the 32-bit number at adr32, must be 32-bit aligned.
L@( adr32 -- long )Fetch a 32-bit number from adr32, must be 32-bit aligned.
lflips( adr len -- )Exchange 16-bit words within 32-bit longwords in specified region.
lpeek( adr32 -- false | long true )Fetch the 32-bit quantity at adr32. Return the data and true if the access was successful. Return false if a read access error occurred.
lpoke( long adr32 -- okay? )Store the 32-bit quantity at adr32. Return true if the access was
successful. Return false if a a write access error occurred.
move( adr1 adr2 u -- )Copy u bytes from adr1 to adr2, handle overlap properly.
off( adr16 -- )Store false (32-bit 0) at adr16.
on( adr16 -- )Store true (32-bit -1) at adr16.
unaligned-L!( long adr -- )Store a 32-bit number, any alignment.
unaligned-L@( adr -- long )Fetch a 32-bit number, any alignment.
unaligned-w!( word adr -- )Store a 16-bit number, any alignment.
unaligned-w@( adr -- word )Fetch a 16-bit number, any alignment.
w!( n adr16 -- )Store a 16-bit number at adr16, must be 16-bit aligned.
w?( adr16 -- )Display the 16-bit number at adr16, must be 16-bit aligned.
w@( adr16 -- word )Fetch a 16-bit number from adr16, must be 16-bit aligned.
wflips( adr len -- )Exchange bytes within 16-bit words in specified region.
wpeek( adr16 -- false | word true )Fetch the 16-bit quantity at adr16. Return the data and true if the access was successful. Return false if a read access error occurred.
wpoke( word adr16 -- okay? )Store the 16-bit quantity to adr16. Return true if the access was
successful. Return false if a write access error occurred.
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.

  ok 10000 20 dump   (Display 20 bytes of memory starting at virtual address 10000)  
         \/ 1  2  3  4  5  6  7   8  9  a  b  c  d  e  f  v123456789abcdef  
   10000 05 75 6e 74 69 6c 00 40  4e d4 00 00 da 18 00 00 .until.@NT..Z...  
   10010 ce da 00 00 f4 f4 00 00  fe dc 00 00 d3 0c 00 00 NZ..tt..~\..S...  
  ok 22 10004 c!   (Change 8-bit byte at location 10004 to 22)  
  ok  

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.
Table 4-6 lists memory mapping commands.
Table 4-6
CommandStack DiagramDescription
alloc-mem( size -- virt )Allocate and map size bytes of available memory; return the virtual address. Unmap with free-mem.
free-mem( virt size -- )Free memory allocated by alloc-mem.
free-virtual( virt size -- )Undo mappings created with memmap.
map?( virt -- )Display memory map information for the virtual address.
memmap( phys space size -- virt )Map a region of physical addresses; return the allocated virtual
address. Unmap with free-virtual.
obio( -- space )Specify the device address space for mapping.
obmem( -- space )Specify the onboard memory address space for mapping.
sbus( -- space )Specify the SBus address space for mapping.
memmap will always work correctly for general mapping of any device, to generate a virtual address for accessing the device.
A general method for mapping any SBus device directly from the Forth monitor (ok prompt) without needing to know system-dependent device addresses follows. The method will work on any OpenBoot 2.0 system or later:

  ok " /sbus" select-dev  
  ok (offset) (slot#) (size)  map-in  ( virt )  
  ok  

For example, to inspect the FCode PROM for a device in slot #3 of a system, use"

  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  


Note - On some systems, the pathname for the system SBus may vary, for example:
   " /iommu/sbus"   (for sun4m machines)
   " /io-unit/sbi"  (for sun4d machines)

show-devs list of all system devices will show the correct pathname.

Note - Future systems may not work correctly in the most general cases if you directly place ( offset size ) on the stack. If you encounter problems doing this, try the following, more general approach:
  ok " /sbus" select-dev
  ok " 3,0" decode-unit  ( offset space )
  ok 1000 map-in        ( virt )
  ok


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.
  • dump displays the contents of 20 bytes of memory starting at ffef7a48.
  • This region of memory is then filled with the value 55.
  • Finally, free-mem returns the 4000 allocated bytes of memory starting at ffef7a48.

  ok  
  ok 4000 alloc-mem .  
  ffef7a48  
  ok  
  ok ffef7a48 constant temp  
  ok temp 20 dump  
            0  1  2  3  4  5  6  7  \/  9  a  b  c  d  e  f   01234567v9abcdef  
  ffef7a40  00 00 f5 5f 00 00 40 08  ff ef c4 40 ff ef 03 c8  ..u_..@..oD@.o.H  
  ffef7a50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................  
  ffef7a60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................  
  ok temp 20 55 fill  
  ok temp 20 dump  
            0  1  2  3  4  5  6  7  \/  9  a  b  c  d  e  f   01234567v9abcdef  
  ffef7a40  00 00 f5 5f 00 00 40 08  55 55 55 55 55 55 55 55  ..u_..@.UUUUUUUU  
  ffef7a50  55 55 55 55 55 55 55 55  55 55 55 55 55 55 55 55  UUUUUUUUUUUUUUUU  
  ffef7a60  55 55 55 55 55 55 55 55  00 00 00 00 00 00 00 00  UUUUUUUU........  
  ok  
  ok temp 4000 free-mem  
  ok  

An example of using memmap is shown below.

  ok 200.0000 sbus 1000 memmap ( virt )  
  ok  

Using Defining Words

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.
Table 4-7 lists the defining words that you can use to create dictionary entries.
Table 4-7
CommandStack DiagramDescription
: name( -- )

Usage: ( ??? -- ? )

Start creating a new colon definition.
;( -- )Finish creating a new colon definition.
alias new-name old-name( -- )

Usage: ( ??? -- ? )

Create new-name with the same behavior as old-name.
buffer: name( size -- )

Usage: ( -- adr64 )

Create a named array in temporary storage.
constant name( n -- )
Usage: ( -- n )
Define a constant (for example, 3 constant bar).
2constant name( n1 n2 -- ) Usage: ( -- n1 n2 )Define a 2-number constant.
create name( -- )

Usage: ( -- adr16 )

Generic defining word.
defer name( -- )

Usage: ( ??? -- ? )

Define a word for forward references or execution vectors using code field address.
does>( -- adr16 )Start the run-time clause for defining words.
field name( offset size -- offset+size ) Usage: ( adr -- adr+offset )Create a named offset pointer.
struct( -- 0 )Initialize for field creation.
value name( n -- )
Usage: ( -- n )
Create a changeable, named 32-bit quantity.
variable name( -- )

Usage: ( -- adr16 )

Define a variable.
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.

  ok 72 constant red  
  ok  
  ok red .  
  72  
  ok  

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.

  ok 22 value foo  
  ok foo 3 + .  
  25  
  ok  

The value can be changed with the dictionary compiling word is. For example:

  ok 43 value thisval  
  ok thisval .  
  43  
  ok 10 is thisval  
  ok thisval .  
  10  
  ok  

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:

  ok variable bar  
  ok 33 bar !  
  ok bar @ 2 + .  
  35  
  ok  

The defining word defer lets you change the execution of previously defined commands, by creating a slot which can be loaded with different behaviors at different times. For example:

  ok hex  
  ok defer printit  
  ok ['] .d  is  printit  
  ok ff printit  
  255  
  ok : myprint ( n -- ) ." It is " .h  
  ] ." in hex " ;  
  ok ['] myprint is printit  
  ok ff printit  
  It is ff in hex  
  ok  

Searching the Dictionary

The dictionary contains all the available Forth commands. Table 4-8 lists some useful tools you can use to search the dictionary.
Table 4-8
CommandStack DiagramDescription
' name( -- acf )Find the named word in the dictionary. Returns the code field address. Use outside definitions.
['] name( -- acf )Similar to ' but is used either inside or outside definitions.
.calls( acf -- )Display a list of all words that call the word whose compilation address is acf.
$find( adr len -- adr len false | acf n )Find a word. n = 0 if not found, n = 1 if immediate, n = -1 otherwise.
find( pstr -- pstr false | acf n )Search for a word in the dictionary. The word to be found is indicated by pstr. n = 0 if not found, n = 1 if immediate, n = -1 otherwise.
see thisword( -- )Decompile the named command.
(see)( acf -- )Decompile the word indicated by the code field address.
sift( pstr -- )Display names of all dictionary entries containing the string
pointed to by pstr.
sifting ccc( -- )Display names of all dictionary entries containing the sequence of characters. ccc contains no spaces.
words( -- )Display all visible words in 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.

  ok sifting input  
  input-device input restore-input line-input input-line input-  
  file  
  ok  

words displays all the word (command) names in the dictionary, starting with the most recent definitions.

Compiling Data into the Dictionary

The commands listed in Table 4-9 control the compilation of data into the dictionary.
Table 4-9
CommandStack DiagramDescription
,( n -- )Place a number in the dictionary.
c,( byte -- )Place a byte in the dictionary.
w,( word -- )Place a 16-bit number in the dictionary.
L,( long -- )Place a 32-bit number in the dictionary.
[( -- )Begin interpreting.
]( -- )End interpreting, resume compilation.
allot( n -- )Allocate n bytes in the dictionary.
>body( acf -- apf )Find parameter field address from compilation address.
body>( apf -- acf )Find compilation address from parameter field address.
compile( -- )Compile next word at run time.
[compile] name( -- )Compile the next (immediate) word.
forget name( -- )Remove word from dictionary and all subsequent words.
here( -- adr )Address of top of dictionary.
immediate( -- )Mark the last definition as immediate.
is name( n -- )Install a new action in a defer word or value.
literal( n -- )Compile a number.
origin( -- adr )Return the address of the start of the Forth system.
patch new-word old-word word-to-patch( -- )Replace old-word with new-word in word-to-patch.
(patch( new-n old-n acf -- )Replace old-n with new-n in word indicated by acf.
recursive( -- )Make the name of the colon definition being compiled visible in the dictionary, and thus allow the name of the word to be used recursively in its own definition.
state( -- adr )Variable that is non-zero in compile state.

Displaying Numbers

Basic commands to display stack values are shown in Table 4-10.
Table 4-10
CommandStack DiagramDescription
.( n -- )Display a number in the current base.
.r( n size -- )Display a number in a fixed width field.
.s( -- )Display contents of data stack.
showstack( -- )Execute .s automatically before each ok prompt.
u.( u -- )Display an unsigned number.
u.r( u size -- )Display an unsigned number in a fixed width field.
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.)

Changing the Number Base

You can change the operating number base using the commands in Table 4-11.
Table 4-11
CommandStack DiagramDescription
base( -- adr )Variable containing number base.
binary( -- )Set the number base to 2.
decimal( -- )Set the number base to 10.
d# number( -- n )Interpret the next number in decimal; base is unchanged.
hex( -- )Set the number base to 16.
h# number( -- n )Interpret the next number in hex; base is unchanged.
.d( n -- )Display n in decimal without changing base.
.h( n -- )Display n in hex without changing base.
The d# and h# commands are useful when you want to input a specific number in another base without explicitly changing the current base. For example:

  ok decimal          (Changes base to decimal)  
  ok 4 h# ff 17 2  
  4 255 17 2 ok  

The .d and .h commands act like "." but display the value in decimal or hexadecimal, respectively, regardless of the current base setting. For example:

  ok hex  
  ok ff .  ff .d  
  ff 255  

Controlling Text Input and Output

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.
Table 4-12 lists commands to control text input.
Table 4-12
CommandStack DiagramDescription
( ccc )( -- )Begin a comment.
\ rest-of-line( -- )Skip the rest of the line.
ascii ccc( -- char )Get numerical value of first ASCII character of next word.
expect( adr +n -- )Get a line of edited input from the assigned input device's keyboard; store at adr.
key( -- char )Read a character from the assigned input device's keyboard.
key?( -- flag )True if a key has been typed on the input device's keyboard.
span( -- adr16 )Variable containing the number of characters read by expect.
word( char -- pstr )Collect a string delimited by char from input string and place in memory at pstr.
Comments are used with Forth source code (generally in a text file) to describe the function of the code. The ( (open parenthesis) is a command 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 command.

\ (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 whether the user has recently typed any key. It returns a flag on the stack: true if a key has been pressed and false otherwise. See "Conditional Flags" on page 73 for a discussion on the use of flags.
Table 4-13 lists general-purpose text display commands.
Table 4-13
CommandStack DiagramDescription
." ccc"( -- )Compile a string for later display.
(cr( -- )Move the output cursor back to the beginning of the current line.
cr( -- )Terminate a line on the display and go to the next line.
emit( char -- )Display the character.
exit?( -- flag )Enable the scrolling control prompt: More [<space>,<cr>,q] ? The return flag is true if the user wants the output to be terminated.
space( -- )Display a space character.
spaces( +n -- )Display +n spaces.
type( adr +n -- )Display n characters.
cr sends a carriage-return character to the output. For example:

  ok 3 . 44 . cr 5 .  
  3 44  
  5  
  ok  

emit displays the letter whose ASCII value is on the stack.

  ok ascii a  
  61 ok 42  
  61 42 ok emit emit  
  Ba  
  ok  

Table 4-14 shows commands used to manipulate text strings.
Table 4-14
CommandStack DiagramDescription
",( adr len -- )Compile an array of bytes from adr of length len, at the top of the dictionary as a packed string.
" ccc"( -- adr len )Collect an input stream string, either interpreted or compiled. Within the string, "(00,ff...) can be used to include arbitrary byte values.
.( ccc)( -- )Display a string immediately.
-trailing( adr +n1 -- adr +n2 )Remove trailing spaces.
bl( -- char )ASCII code for the space character; decimal 32.
count( pstr -- adr +n )Unpack a packed string.
lcc( char -- lowercase-char )Convert a character to lowercase.
left-parse-string( adr len char -- adrR lenR adrL lenL )Split a string at the given delimiter (which is discarded).
pack( adr len pstr -- pstr )Make a packed string from adr len; place it at pstr.
p" ccc"( -- pstr )Collect a string from the input stream; store as a packed string.
upc( char -- uppercase-char )Convert a character to uppercase.
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:

  ok  : testing 34 .  ." This is a test"  55 . ;  
  ok  
  ok testing  
  34 This is a test55  
  ok  

Redirecting Input and Output

Normally, your system uses a standard Sun 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.
Table 4-15 lists commands you can use to redirect input and output.
Table 4-15
CommandStack DiagramDescription
input( device -- )Select device (ttya, ttyb, keyboard, or " device-specifier") for subsequent input.
io( device -- )Select device for subsequent input and output.
output( device -- )Select device (ttya, ttyb, screen, or " device-specifier") for subsequent output.
The commands input and output temporarily change the current devices for input and output. The change takes place as soon as 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, "Setting Configuration Parameters," 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:

  ok ttya input  
  ok  

At this point, the Sun 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:

  ok keyboard input  
  ok  

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:

  ok ttya output  

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:

  ok " /sbus/cgsix" output  

or:

  ok " screen" output  

In the preceding examples, ttya, screen, and keyboard are Forth words that put their corresponding device alias string on the stack.

Keyboard Editor

A keyboard line editor (similar to EMACS, a common text editor) and a history mechanism are also provided with the Forth Monitor. Use these powerful tools to re-execute previous commands without retyping them, to edit the current command line to fix typing errors, or to change previous commands.
Table 4-16 list the line-editing commands available at the ok prompt.
Table 4-16
CommandFunction
Control-AGo to start of line.
Control-BGo backward one character.
Control-DErase this character.
Control-EGo to end of line.
Control-FGo forward one character.
Control-HErase previous character (also Delete or Back Space keys).
Control-KErase forward, from here to end of line.
Control-LShow command history list, then re-type line.
Control-NRecall subsequent command line.
Control-PRecall previous command line.
Control-QQuote next character (to type a control character).
Table 4-16 (Continued)
CommandFunction
Control-RRe-type line.
Control-UErase entire line.
Control-WErase previous word.
Control-YInsert save buffer contents before the cursor.
Control-spaceComplete the current command.
Control-/Show all possible matches/completions.
Control-?Show all possible matches/completions.
Control-}Show all possible matches/completions.
Esc-BGo backward one word.
Esc-DErase this portion of word, from here to end of word.
Esc-FGo forward one word.
Esc-HErase previous portion of word (also Control-W).
The command completion function saves you the work of typing very long command names. After you have typed the first part of the desired word, you can type Control-Space to tell the Forth Monitor to search the current dictionary of defined words.
  • If you have typed enough letters such that there is only one possible command starting with the typed letters, the rest of the command is filled in automatically.
  • If there is more than one possible match, the system echoes as many characters as can be determined for sure.
  • If there is no word starting with the given letters, those letters will be erased until there are possible candidates for the remaining letters.
  • The system beeps if it cannot determine an unambiguous match.

Conditional Flags

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 behavior if a flag is true and another behavior if the flag 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.)
Table 4-17 lists commands that perform relational tests, and leave a true or false flag result on the stack.
Table 4-17
CommandStack DiagramDescription
<( n1 n2 -- flag )True if n1 < n2.
<=( n1 n2 -- flag )True if n1 <= n2.
<>( n1 n2 -- flag )True if n1 <> n2.
=( n1 n2 -- flag )True if n1 = n2.
>( n1 n2 -- flag )True if n1 > n2.
>=( n1 n2 -- flag )True if n1 >= n2.
0<( n -- flag )True if n < 0.
0<=( n -- flag )True if n <= 0.
0<>( n -- flag )True if n <> 0.
0=( n -- flag )True if n = 0 (also inverts any flag).
0>( n -- flag )True if n > 0.
0>=( n -- flag )True if n >= 0.
between( n min max -- flag )True if min <= n <= max.
false( -- 0 )The value FALSE, which is 0.
true( -- -1 )The value TRUE, which is -1.
u<( u1 u2 -- flag )True if u1 < u2, unsigned.
u<=( u1 u2 -- flag )True if u1 <= u2, unsigned.
Table 4-17 (Continued)
CommandStack DiagramDescription
u>( u1 u2 -- flag )True if u1 > u2, unsigned.
u>=( u1 u2 -- flag )True if u1 >= u2, unsigned.
within( n min max -- flag )True if min <= n < max.
> 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:

  ok 3 6 > .  
  0                (3 is not greater than 6)  
  ok  

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.

Control Commands

The following sections describe words used within a Forth program to control the flow of execution.

The if-then-else Structure

The commands if, then, and else provide a simple control structure.
The commands listed in Table 4-18 control the flow of conditional execution.
Table 4-18 if-then-else
CommandStack DiagramDescription
else( -- )Execute the following code if if failed.
if( flag -- )Execute the following code if flag is true.
then( -- )Terminate if...then...else.
The format for using these commands is:

  flag      if     (do this if true)  
            else   (do this if false)  
            then   (continue normally)  

or

  flag      if     (do this if true)  
            then   (continue normally)  

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 back to ok after you finish the definition with a semicolon.

The case Statement

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.
Table 4-19 lists the conditional case commands.
Table 4-19 case
CommandStack DiagramDescription
case( selector -- selector )Begin a case...endcase conditional.
endcase( selector | {empty} -- )Terminate a case...endcase conditional.
endof( -- )Terminate an of...endof clause within a case...endcase
of( selector test-value -- selector | {empty} )Begin an of...endof clause within a case conditional.
Here is a simple example of a case command:

  ok : testit  ( testvalue -- )  
  ]  case  0  of  ." It was zero "  endof  
  ]    1  of  ." It was one "  endof  
  ]    ff of  ." Correct "  endof  
  ]    -2 of  ." It was minus-two "  endof  
  ]    ( default )  ." It was this value: "  dup .  
  ]  endcase   ." All done."  ;  
  ok  
  ok 1 testit  
  It was one All done.  
  ok ff testit  
  Correct All done.  
  ok 4 testit  
  It was this value: 4 All done.  
  ok  


Note - The (optional) default clause can use the test value which is still on the stack, but should not remove it (use the "dup ." phrase instead of "."). A successful of clause automatically removes the test value from the stack.

The begin Loop

A begin loop executes the same commands repeatedly until a certain condition is satisfied. Such a loop is also called a conditional loop.
Table 4-20 lists commands to control the execution of conditional loops.
Table 4-20 begin
CommandStack DiagramDescription
again( -- )End a begin...again infinite loop.
begin( -- )Begin a begin...while...repeat, begin...until, or begin...again loop.
repeat( -- )End a begin...while...repeat loop.
until( flag -- )Continue executing a begin...until loop until flag is true.
while( flag -- )Continue executing a begin...while...repeat loop while flag is true.
There are two general forms:

  begin      any commands...     flag until  

and

  begin      any commands...     flag while  
                                more commandsrepeat  

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 mnemonic for either of these loops is: If true, fall through.
A simple example follows.

  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 Forth Monitor allows the interactive use of loops and conditionals -- that is, without first creating a definition.

The do Loop

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.
Table 4-21 lists commands to control the execution of counted loops.
Table 4-21 do
CommandStack DiagramDescription
+loop( n -- )End a do...+loop construct; add n to loop index and return to do (if n < 0, index goes from start to end inclusive).
?do( end start -- )Begin ?do...loop to be executed 0 or more times. Index goes from start to end-1 inclusive. If end = start, loop is not executed.
?leave( flag -- )Exit from a do...loop if flag is non-zero.
do( end start -- )Begin a do...loop. Index goes from start to end-1 inclusive. Example: 10 0 do i . loop (prints 0 1 2...d e f).
i( -- n )Loop index.
Table 4-21 do(Continued)
CommandStack DiagramDescription
j( -- n )Loop index for next enclosing loop.
leave( -- )Exit from do...loop.
loop( -- )End of do...loop.
The following screen shows several examples of how loops are used.

  ok 10 5 do  i .  loop  
  5 6 7 8 9 a b c d e f  
  ok  
  ok 2000 1000 do i .  i c@ . cr   i c@ ff = if leave then  4 +loop  
  1000 23  
  1004 0  
  1008 fe  
  100c 0  
  1010 78  
  1014 ff  
  ok : scan ( byte -- )  
  ]    6000 5000     (Scan memory 5000 - 6000 for bytes not equal to the specified byte)  
  ]    do dup i c@ <> (  byte error? )  
  ]       if i . then  ( byte )  
  ]    loop  
  ]    drop ( the original byte was still on the stack, discard it )  
  ]  ;  
  ok 55 scan  
  5005 5224 5f99  
  ok 6000 5000 do i i c! loop     (Fill a region of memory with a stepped pattern)  
  ok  
  ok 500 value testloc  
  ok : test16 ( -- ) 1.0000 0 ( do 0-ffff )          (Write different 16-bit values to a location)  
  ]      do i testloc w! testloc w@ i <> ( error? )  (Also check the location)  
  ]        if ." Error - wrote " i . ." read " testloc w@ . cr  
  ]         leave ( exit after first error found )  (This line is optional)  
  ]        then  
  ]      loop  
  ]  ;  
  ok test16  
  ok 6000 is testloc  
  ok test16  
  Error - wrote 200 read 300  
  ok  

Additional Control Commands

Table 4-22 contains descriptions of additional program execution control commands.
Table 4-22
CommandStack DiagramDescription
abort( -- )Abort current execution and interpret keyboard commands.
abort" ccc"( abort? -- )If flag is true, abort and display message.
eval( adr len -- )Interpret Forth source from an array.
execute( acf -- )Execute the word whose code field address is on the stack.
exit( -- )Return from the current word. Cannot be used in counted loops.
quit( -- )Same as abort, but leave stack intact.
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, "Loading and Executing Programs,"), then eval can be used to compile the definitions contained in the file.