Link here

A Brief Introduction to Forth Programming

Using the Forth programming language on the PDQ Board.

This chapter examines the basic concepts and syntax of the QED-Forth implementation of the FORTH language which is ideal for instrument control and automation applications. It describes how to add to the dictionary, how to use integer, double precision and floating point numbers, how to code decisions and loops, and the details of stack operations, memory access, serial input/output (I/O), and integer mathematics.

 

The QED-Forth high level language

The PDQ Single Board computer (SBC) supports more than just C-language programming. For those who prefer to program in FORTH, no external compiler is needed. You interact with the QED-Forth operating system (an RTOS, interpreter and compiler, all rolled into one) using your PC as a terminal. When programming in Forth you can use the Mosaic IDE Plus (or you can use any other editor you prefer) to write your code and download the source code directly to the controller where it is compiled as it downloads. The built-in Forth language provides a quick and easy way to interactively "talk to" your PDQ Board while debugging your programs.

 

Tips for Forth programmers

Most of the chapters in this User Guide is targeted for C programmers. However, Forth programmers will benefit from reading those chapters to learn about the PDQ Board hardware, memory map, and interactive debugging. The C functions described in this document are also available as QED-Forth functions with slightly different names.

The companion document titled "Forth V6 Function Glossary" describes all of the functions in the QED-Forth kernel, provides categorized function lists, and a comparison with prior QED-Forth Kernel versions. Forth programmers should study its categorized word lists to become familiar with all of the available pre-programmed routines.

C and Forth use characteristically different function naming conventions, and mastering these naming rules will make it easy to translate descriptions of C functions to be useable in Forth. The QED-Forth functions in the kernels use the . (period) character to distinguish subwords in the function name, while the corresponding C functions use a capital letter at the start of each subword in the function name. Recall that any printable character can be part of a Forth function name, while C function names are limited to the alphanumeric characters plus the _ (underscore) character. In addition, Forth is case insensitive (and often Forth function names are typed in all capital letters), while C is case sensitive, with capital letters used to denote the start of function name subwords. For example, the parameter-less function that starts the timeslicer is

START.TIMESLICER

in QED-Forth, and the corresponding C function is

StartTimeslicer()

Forth function parameters are placed on the stack before the function name, and C parameters are placed between the function’s parentheses, separated by commas.

If a C function name contains both upper- and lower-case letters and uses the _ (underscore) character to delimit subwords, then the corresponding QED-Forth function name is typically spelled in exactly the same way. This Forth/C invariant naming convention is used for device driver libraries such as the Graphical User Interface Toolkit. C "macros" are typed as all capital letters, and are not needed in Forth. The macros typically insert a page parameter to simplify C calling, whereas passing a page parameter in Forth if trivially easy because there is no linker to hide the page information.

In summary, Forth programmers can profit by reading this User Guide, studying these Forth programming chapters, referring to the "PDQ Controller Glossary of Forth Functions", and consulting the excellent Starting Forth book by Leo Brodie which at the time of this writing is available free online at: http://www.forth.org/tutorials.html

 

An important note for users of C and Forth

When a C program is loaded onto the PDQ Board, the area of RAM which holds C code is write protected. This feature protects the integrity of your program code in the event of a crash or a bug. This protection also means that a Forth program will fail to load after a C program has been sent to the board. To solve this problem, put the following lines at the beginning of your Forth program:

1 write.enable
2 write.enable  \ Make sure ram is writable
 

QED-Forth basics

This chapter examines the basic concepts and syntax of the QED-Forth implementation of the FORTH language. It describes how to add to the dictionary, how to use integer, double precision and floating point numbers, how to code decisions and loops, and the details of stack operations, memory access, serial input/output (I/O), and integer mathematics.

We recommend that you read the first five chapters of this document before reading this chapter. Even though they are targeted at C programmers, these earlier chapters contain a wealth of information about the hardware and the operating system, and many of the commands illustrated in those chapters actually invoke Forth routines. Even FORTH experts will want to read this chapter, paying close attention to the descriptions of memory access, local and self-fetching variables, and how to create "defining words".

QED-Forth implements these features in a way that may not be described in other FORTH references. QED-Forth is similar to an "F-83" implementation of the Forth programming language.

 

Initializing the operating system

Now let’s enter our first command. We want to make sure that we’re starting with a clean slate from a known initialized condition. To accomplish this, type the command

COLD↓

where the ↓ symbol represents a carriage return (typically labeled "return" on your keyboard, but called "enter" on some terminals). QED-Forth executes the command as soon as it receives the carriage return. The COLD command tells QED-Forth to perform a "cold" restart, initializing the system to a known state and causing it to FORGET all user-added words. (Normally, when you turn the power on or push the reset button, QED-Forth does a WARM restart, retaining all of the words that had been defined before the restart occurred). QED-Forth responds with its cold startup message and memory map status report:

Coldstart
XFlash->RAM pages loaded: Area 1: None 2: None 3: None  WP1: Off WP2: Off
QED-Forth V6.0x
DP: 0x1D8000  NP: 0x1D9000  VP: 0x2000  EEP: 0x680  HEAP: 0x188000 to 0x1CBFFF
Segment: Kernel ID: 0x0 Code base: 0x0 Name base: 0x0  Type: Kernel

QED-Forth doesn’t care whether you use capital letters, small letters, or a combination of both. It automatically performs case conversion so that COLD, Cold, and even CoLd are treated as the same command.

To set up a memory map that gives plenty of room for programming, type the command

DEFAULT.MAP ↓

QED-Forth’s "ok" response tells us that it has executed the command. To see a summary of the newly established memory map, you can type:

.MAP↓

If a bug or error causes the COLDSTART message to be printed to your terminal while you are programming, you can re-initialize the memory map with the DEFAULT.MAP command.

 

How commands are interpreted

After the PDQ Board is turned on, the QED-Forth interpreter waits for commands from your terminal. As the characters on a single line are received, the interpreter acts as a simple line editor. It echoes the characters that you enter, and the "backspace" or "delete" key on your terminal may be used to correct errors on the line. After you type a carriage return to end the line, QED-Forth interprets the commands on the line, executes them if it can, and says "ok" to indicate that it executed them. If it doesn’t understand the command or encounters some error while attempting to execute it, it prints an error message. Let’s look at a simple command:

CR ." Hi There!"  ↓
Hi There!   ok

Make sure to put at least one space after the CR command and after the ." command; spaces are important in FORTH! QED-Forth’s response is underlined. QED-Forth "parses" the text on the line you entered into words that are separated ("delimited") by spaces. The space character is the universal delimiter in FORTH; two words separated by spaces are treated as separate commands by FORTH. A carriage return is also a valid delimiter. Multiple spaces are treated the same as a single space. This means that you can type multiple spaces between words to make your commands easier for people to read, and QED-Forth won’t care. QED-Forth converts each TAB character to a space, so feel free to use TABs to format your code for readability.

After receiving the command line, the interpreter searches for the first space-delimited command in the line. It decides that the 2-character sequence (also known as a "character string") CR is a command. QED-Forth looks up CR in its dictionary. The dictionary consists of two parts, a "name area" where the names known by QED-Forth are kept, and a "definitions area" where the code representing the behavior of each known word is kept. QED-Forth searches its name area, starting with the most recently defined word. It looks until it finds a match for the character string CR . The entry for this word (called its "header") contains a reference to the location in the definitions area where the behavior of CR is defined. Because QED-Forth is in the "execution mode" (more about this later), this behavior code is executed, and a carriage return character is sent to the serial line. This causes a new line to be started on your terminal.

Having executed the CR command, QED-Forth sees that the next word on the line is ." (pronounced dot-quote). It looks this word up in its dictionary, and finds that its behavior, if written in English, would read:

print each of the succeeding characters to the output terminal until the terminating " character is encountered; don’t print the terminating "

QED-Forth does this; it prints Hi There! to the terminal. It then sees that there are no remaining command words on the line so it prints ok to signal that it has accomplished what we asked it to do. Then it waits for another command to interpret.

 

Error and warning messages

What happens if an error is made while typing a command, and it is not corrected before the carriage return is entered? QED-Forth attempts to interpret the command and, if it can’t find the command’s name in its dictionary, it prints an error message. For example, if the space after ." is omitted, as

."Hi There!"↓      ."Hi   ?  ok 

QED-Forth interprets the character string ."Hi as a command and tries to find it in its dictionary. It can’t find it, so it tries to convert it to a valid integer or floating point number. It can’t do that either, so it gives up, beeps, repeats the unrecognized name followed by a ?, and executes ABORT which clears the data and return stacks and waits for a new command line.

Let’s look at another type of error. The . (dot) command prints the top number on the stack. The command

45 . ↓     45   ok 

places the number 45 on the data stack and then prints the number. But if we start with an empty stack and then call the printing word, a stack underflow is detected, and QED-Forth executes ABORT after printing an error message:

.  ↓   Data stack underflow!  ok 

QED-Forth also has "warnings" that alert you to some event but do not ABORT the current operation. The most common warning occurs when you define a new word that is already in the QED-Forth dictionary; QED-Forth wants you to know about this situation. For example, if a new variable called NEWVAR is defined as

VARIABLE NEWVAR

a warning is issued if it is defined a second time:

VARIABLE NEWVAR ↓         NEWVAR isn’t unique   ok 

QED-Forth compiles both definitions of the variable (they refer to different memory locations), but only the most recently defined one can be found by the QED-Forth interpreter.

Error and warning messages are written in plain English (instead of inscrutable numerical error codes) to help you zero in on the cause of the problem.

anew globals_section        Error at GLOBALS_SECTION   Can't compile--Not RAM!

If you receive this error, it most likely means that RAM on your PDQ Board is write protected. See this note.

 

Adding to the Forth dictionary

The FORTH language is based on the dictionary concept. The set of pre-defined functions (called "words") in the FORTH dictionary is called the "kernel". You can define new words as combinations of the previously defined words or as assembly coded definitions. The kernel contains all of the tools that you need to extend the language, so you can customize the language to meet your needs.

Colon definitions

How the name is recorded

Compilation of commands

 

Numbers and literals

When an integer number is placed on the stack, such as

1234 . ↓     1234   ok 

QED-Forth treats the character string 1234 as a word and searches for it in the dictionary. It is not found, so QED-Forth tries to convert it to a valid number in the current base. In this case it is successful, and the converted number is placed on the stack.

Number base

Number representation

Signed and unsigned numbers

Floating point numbers

Literals

Double numbers

Conversion among number types

 

Memory access

Paged memory expands the memory space

Extended addresses and the X-prefix

Storing to a memory location

Fetching from a memory location

Using variables to hold logical flags

Using variables to hold extended addresses

Floating point and double number fetch and store commands

Byte-Size memory access commands

Other useful memory operations

Memory operators are page-smart

Representation of numbers in memory

Page changing for experts and the curious

Memory access without page changing

Writing to EEPROM

Moving blocks of memory

Calculations involving extended addresses

 

Stack operations

Because the stack is central to the operation of the language, QED-Forth provides dozens of operators that manipulate the contents of the data stack. The basic operations DUP (duplicate) the top item on the stack, DROP the top item, SWAP the top 2 stack items, or ROT (rotate) the top three items. For example, try these:

1 2 3↓  ok ( 3 ) \ 1 \ 2 \ 3
dup↓  ok ( 4 ) \ 1 \ 2 \ 3 \ 3
drop↓  ok ( 3 ) \ 1 \ 2 \ 3
swap↓  ok ( 3 ) \ 1 \ 3 \ 2
rot↓  ok ( 3 ) \ 3 \ 2 \ 1
sp!↓  ok

Stack operations for 32-bit quantities

 

A definition using local variables and floating point math

Let’s define a new word that calculates the volume and cross-sectional area of a cylinder given its radius and height. This definition introduces more of QED-Forth’s features:

:  CYLINDER.STATISTICS  ( r1\r2 -- r3\r4 )
   \  r1 = radius, r2 = height, r3 = volume, r4 = cross-sectional area
   LOCALS{ f&height f&radius | f&area }
   f&radius  FDUP F*   PI  F*     \ cross-sectional area = πR**2
   TO f&area
   f&area f&height F*             ( -- volume = height * area )
   f&area                         ( -- volume\area )
;

The stack picture shows that this word expects two floating point inputs represented as r1 and r2 (the "r" stands for real number), and leaves two floating point results r3 and r4 on the stack. The comment on the next line identifies the inputs and outputs.

The next line of the definition removes two floating point numbers from the data stack, starting with the top number, and loads them into named "local variables". r2 is loaded into f&height, and r1 is loaded into f&radius. The | (bar) signals that the remaining locals are uninitialized (and thus no item corresponding to this local is removed from the data stack). The } character terminates the LOCALS{ statement. Local variables provide a way of manipulating up to 16 named quantities inside a definition. They help make QED-Forth definitions easy to read, understand, and modify. The use of the "&" character in the name is a stylistic convention that helps identify local variables, and the use of "f&" as the first 2 characters in the local variable’s name flags it as a 32-bit floating point quantity. Local variable names that start with "&" indicate 16-bit quantities and locals that start with D&, d&, F&, f&, X&, or x& are treated as 32-bit quantities. The d, f and x suggest double, floating point and extended address quantities, respectively.

The next line of the definition calculates the cross-sectional area of the cylinder. The radius is placed on the stack by stating the local variable’s name (f&radius), and is squared by duplicating the floating point item on the stack using the FDUP command and multiplying the duplicated radius by itself with the floating point multiplication operator F* (floating point operators start with the letter F). The squared radius is then multiplied by the pre-defined constant PI to calculate the cross-sectional area, which is on the data stack at this point. The next statement uses the TO operator to remove the top floating point number from the data stack and load it into the local variable f&area. TO is smart enough to know whether the local that follows it is a 16- or 32-bit quantity. Floating point locals are 32-bit values, so TO removes 2 cells from the stack to load the local variable.

The next line calculates the volume by multiplying the area by the height and leaving the result on the stack. The cross-sectional area is placed on the top of the stack to complete the definition. This definition demonstrates the convenience of using floating point math. Imagine trying to accomplish this function with integer math–it’s a hassle to worry about how to represent π with integer math and to be concerned with preserving resolution regardless of the magnitude of the numbers. QED-Forth’s floating point capability handles all of this for you.

The definition also shows the convenience of local variables. By naming stack items, they make programs easy to read, and they make handling different size stack items transparent. They reduce a lot of the "stack juggling" that makes other versions of FORTH harder to use. Finally, using local variables to replace standard "global variables" results in re-entrant code that is suitable for multitasking. The next chapter will discuss re-entrancy in detail.

 

Logical and arithmetic domparisons

QED-Forth supports a full set of comparison operators for integers, double and floating point numbers, and extended addresses. These operators take operands from the data stack, perform the comparison, and leave a "boolean flag" on the stack. "Boolean" refers to the algebra of logic developed by George Boole. A boolean flag is either true or false; in QED-Forth, true flags have the value -1 (all bits are set in the 16-bit flag), and false flags have the value 0 (all bits are cleared).

Try the following commands:

3 4 = .↓     0   ok \ false flag: 3 isn’t "equal to" 4
3 4 > .↓     0   ok \ false flag: 3 isn’t "greater than" 4
3 4 < .↓     -1   ok \ true flag: 3 is "less than" 4
4 4 <= .↓     -1   ok \ true flag: 4 is "less than  or equal to" 4
4 5 <> .↓     -1   ok \ true flag: 4 is "not equal to" 5
4 0= .↓     0   ok \ false flag: 4 isn’t "equal to 0"

The comments explains the results, and the name of each operator is in quotations. Versions of most of these operators are defined for floating point (F= F> F< F<= F<> F0= ), double number (D= D> D< D<> D0= ), and extended address (X= XU> XU< X<>) operands. The word RANGE tests whether a signed number lies in a range greater than or equal to a specified minimum value and less than or equal to a specified maximum value. For example, to test whether 2 is between -5 and 3, execute

2  -5  3  RANGE↓  ok ( 2 ) \ 2 \ -1
SP!↓  ok

The number being tested remains on the stack under a flag. In this case the flag is true because 2 lies in the range between -5 and 3. The variants URANGE, DRANGE, and XRANGE perform the range-comparison function for unsigned numbers, double numbers, and extended addresses, respectively. Consult the glossary for descriptions of their use.

Notice that all of the comparison operators use standard FORTH-style postfix notation. The operator is called after the operands are placed on the stack, and the result is left on the stack.

The operators AND OR XOR and COMPLEMENT perform bit-by-bit logical and, or, exclusive-or, and complement (logical inversion) operations, respectively. Because QED-Forth defines a true flag as a cell with all bits set, and a false flag as a cell with all bits clear, these bit-by-bit operators work correctly with boolean flags.

Performing logical operations on arithmetic values

 

Decision making in FORTH

Simple control structures are used to implement conditional operations. For example, we can define a word that decides if the number on the stack is equal to 3 by typing this definition:

: =3? ( n -- )
  3 =                       \ compare n to 3
  IF                        \ if equal...
    CR  ." Equals 3"        \ print message
  ELSE                      \ else if not equal...
    CR  ." Doesn’t equal 3" \ print this message
  ENDIF
;

The stack picture shows that this word expects to find one data item, designated as n, on the stack. The Forth word = (equals) removes 2 numbers from the data stack (in this case, the input number n and the 3 that was placed on the stack inside the definition) and leaves on the stack a true flag (-1) if the numbers are equal, and a false flag (0) if they are not equal. The word IF removes the logical flag from the data stack. If the flag is true, the code between IF and ELSE is executed; if it is false, the code between ELSE and ENDIF is executed.

The standard FORTH syntax is IFELSETHEN instead of the IFELSEENDIF shown here. Because many newcomers to FORTH find the THEN name confusing, QED-Forth defines ENDIF and THEN as synonyms with identical behavior. Use the name that appeals to you the most.

After defining this word, it can be executed as

2 1 +   =3?↓
Equals 3   ok

or as

1234   =3?↓
Doesn’t equal 3   ok

The ELSE clause in a conditional structure is optional. For example,

: =4? ( n -- )
  4 =            \ compare n to 4
  IF             \ if equal, print message
    CR  ." Equals 4"
  ENDIF
;

is a valid definition. It prints a statement if the input equals 4; otherwise it does nothing.

The IFELSEENDIF or IFENDIF construction may be used only in the compilation mode. An error message will be printed if they are used outside a colon definition. Moreover, the compiler will issue an error message if the IFELSEENDIF or IFENDIF commands are not properly paired within a definition.

The IFTRUEOTHERWISEENDIFTRUE construction is available for decision making outside of colon definitions. See the glossary entries of these words for details.

Case statement

 

Looping in Forth

DO loops

FOR...NEXT loops

Indefinite loop structures

 

Variables, constants, and self-fetching variables

The word VARIABLE has already been introduced. It is a "defining" or "parent" word that can create "child" words. (The parent and child terminology is borrowed from object-oriented programming). The programmer specifies the child’s name, and the parent word imparts a run time behavior to the child.

Variables

Constants

Self-fetching variables

 

The parameter field address

Words that are created by defining words other than : CODE CREATE (and their variants that start with the letter X) have a parameter field address (pfa). This includes variables, constants, self-fetching variables, matrices, arrays, and words defined by <DBUILDSDOES> and <VBUILDSDOES> as discussed in the next section. The contents of variables, self-fetchers, and constants are stored in their parameter fields. The parameter field of an array or matrix holds information about the dimensions of the structure as well as a reference to the heap where the data structure is stored.

The parameter fields of constants, constant arrays, and constant matrices (described in the next chapter) are located in the definitions area of the dictionary and cannot be changed once the dictionary is write-protected. The parameter fields of all of the other items are in the variable area which can be modified during program execution. This allows the contents of variables to be modified, and permits arrays and matrices to be dimensioned and re-dimensioned at run time (this is known as "dynamic memory allocation").

The kernel word ‘ ("tick") finds the extended pfa (xpfa) of the next word in the input stream and leaves it on the data stack. For example, executing ‘ followed by the name of a variable produces the same result as executing the variable’s name: the xpfa is left on the stack.

 

How to create defining words

A "defining word" is a word that can itself define new words with specified actions. These kernel words described above,

  :   VARIABLE   CONSTANT   INTEGER:   REAL:

and these words, described in Advanced Forth Programming Topics,

  ARRAY:   DIM.CONSTANT.ARRAY:   MATRIX:   DIM.CONSTANT.MATRIX:

are all defining words. Each of the "parent" defining words can define "child" words that inherit a specified run time action from the parent. One of the key advantages of FORTH is that the programmer can create new defining words, thereby extending the capabilities of the language.

A defining word must specify two actions. The first action is performed when the child word is being created, and typically involves configuring and initializing the parameter field of the child word. We’ll call this the "creation time action". The second action is the "run time action" imparted to the child; it will be performed when the child word executes.

Most FORTHs use a CREATEDOES> or a <BUILDSDOES> syntax to create defining words. QED-Forth uses a slightly different form to allow the programmer to specify whether the parameter field of the child word will be located in the dictionary or in the variable area. If you want the contents of the child’s parameter field to be un-alterable (as with a CONSTANT), put the pfa in the definitions area of the dictionary which will eventually be in write-protected memory. If you want the contents to be variable (as with a VARIABLE), put the pfa in the variable area which will always be in modifiable RAM.

To create a defining word whose children have their pfas in the dictionary, QED-Forth uses the kernel words <DBUILDS and DOES> to specify the creation time action and run time action. The < and > characters emphasize that these words must be paired inside a definition. The "D" in <DBUILDS means that the pfa will be in the Definitions area of the dictionary (just as DP means Definitions Pointer). For example, the following word defines children that behave like words defined by CONSTANT:

: MY.CONSTANT  ( n <name> -- )   \ stack picture during creation of child
( -- n )         \ stack picture when child executes
  <DBUILDS       \ to create the child...
    ,            \ ...allocate & initialize 16bits in ROM
  DOES>          ( xpfa -- )    \ when child executes...
    @ ( -- n )   \ ...put contents of pfa on stack
;

Note that two stack pictures are given, one for when the child is created (that is, when MY.CONSTANT executes), and one for when the child executes. The code between <DBUILDS and DOES> specifies the creation time action, and the code after DOES> specifies the run time action of the child. We can create a new constant called 1HUNDRED by executing

100 MY.CONSTANT 1HUNDRED

When MY.CONSTANT executes, <DBUILDS removes the next name (in this case, 1HUNDRED) from the input stream and creates a header for it in the name area of the dictionary, and also sets up the next available location in the definitions area as the parameter field address of 1HUNDRED. Then the code between <DBUILDS and DOES> executes. The , (comma) command removes the 100 from the stack and places it at the pfa in the definitions area.

The rest of the definition specifies the run time action of the child. The default action installed by DOES> is to place the extended parameter field address on the data stack. In the definition of MY.CONSTANT, the remainder of the run time action is to perform a fetch from the xpfa. Thus, when 1HUNDRED is executed, 1HUNDRED’s xpfa is put on the stack, and then @ fetches the value stored at the xpfa and leaves it on the stack.

CONSTANT is defined with <DBUILDS so that the pfa is in the dictionary which may eventually be in write-protected memory. The pfa of a variable, on the other hand, must always be in writable memory (RAM). The following equivalent definition of VARIABLE uses <VBUILDSDOES> to ensure that the pfa is in the variable area:

: MY.VARIABLE  ( <name> -- )     \ stack picture when child is defined
  ( -- xpfa )   \ stack picture when child executes
  <VBUILDS      \ to create the child...
    2 VALLOT    \ ...allocate 16 bits in RAM
  DOES>         ( xpfa -- ) \ leaves xpfa when child executes
;

The creation action is 2 VALLOT which increments the variable pointer VP by 2 bytes. Be certain that the creation action operates on the same memory area that the parameter field occupies. In other words, if <VBUILDS is used, then V, (v-comma) and VALLOT should be used to emplace and allot in the variable area. If <DBUILDS is used in the defining word, then , (comma) and ALLOT are appropriate to emplace and allot in the definitions area.

The run time action of a variable is to leave the parameter field address (which is in the variable area) on the stack. This is the default action installed in the child by DOES> so no further run time action is specified between DOES> and ;.

 

Formatted output and serial I/O

String Format

Long sStrings

String editing and comparison

Serial I/O routines

Inputting a line of text

Inputting a number

 

Formatted number output

The basic numeric printing word . (dot) removes an integer from the stack and prints it with no leading spaces and a single trailing space to the terminal. If the number base is decimal, . prints the integer as a signed number (i.e., as a negative number if the top bit of the number is set). The printing word U. (u-dot) forces the integer to be printed as an unsigned positive number. In non-decimal bases, . always prints the integer as an unsigned positive number. HEX. prints the number in hexadecimal base with a leading 0x sequence.

The word .R (dot-R, where the R stands for "right-justified") expects an integer under a field width, and prints the number right-justified in the specified field. D.R is the analog of .R for double numbers; it expects a double number under a field width. UD.R has the same stack picture as D.R but it always prints the integer as an unsigned positive number, even if its top bit is set. HEXD.R prints the double number using hexadecimal base with a leading 0x sequence.

The standard FORTH "pictured numeric output" words <# # #S #> SIGN and HOLD enable the programmer to specify an arbitrary format for number output. The glossary explains how each word operates. As an example of formatted numeric display, assume that a program uses double numbers to represent amounts of money in cents. The following word prints an amount using standard dollar format with a minus sign if needed, a dollar sign, number of dollars, decimal point, and 2-digit number of cents:

: D>DOLLAR     ( d -- )
  \ d = an amount in cents; print it in dollar format in decimal base
  BASE @
  >R              \ save number base on rstack
    DECIMAL       \ must be decimal during execution of this word!
    DUP  >R       \ save most significant cell on rstack for sign
    DABS          \ use absolute value for conversion
    <#            \ start pictured numeric output
    # #           \ convert 2-digit cents
    ASCII . HOLD  \ insert decimal point
    #S            \ convert remainder of number
    ASCII $ HOLD  \ insert dollar sign
    R>
    SIGN          \ insert negative sign if necessary
    #>            ( --xaddr\cnt) \ end conversion, leave string specification
    CR TYPE       \ print the string on a new line
  R> BASE ! ;     \ restore prior number base

The comments in the definition explain what’s happening. The current number base is irrelevant while D>DOLLAR is compiling because there are no numbers in the definition to be interpreted at compile time. (If the base at compile time were important, it would be set using DECIMAL before the colon definition began.) But at run time, the base must be decimal for the conversion words # and #S to generate a decimal dollar amount. Thus, inside the definition, the current base is saved on the return stack (so it can be restored later) and the base is set to decimal.

The most significant cell of the double number is duplicated and saved on the return stack by DUP>R; this cell (in fact, the most significant bit of the cell) will be used by SIGN to decide whether to print a minus sign. DABS takes the absolute value of the input number. <# opens the pictured numeric output sequence, and # converts a single digit in the current base. Conversion starts with the least significant digit.

Note the use of the word ASCII. It converts the first character of the next word in the input stream into its ascii equivalent value and compiles it as a literal (if inside a colon definition) or leaves it on the stack (if in execution mode). In either case, its effect is to leave the ascii equivalent value on the stack at run time. HOLD removes the ascii value from the stack and inserts the character into the pictured numeric output. It is used to insert the decimal point into the string. #S converts the remaining digits (if any) in the number. R> removes the saved most significant cell of the input number from the return stack, and SIGN inserts a minus sign in the string if the most significant bit is set.

#> closes the pictured numeric conversion, leaving the extended address and count of the string on the stack. The address is below PAD which is where all floating point and integer number conversion occurs; the area above PAD is available as a scratchpad area for the programmer. TYPE prints the formatted number. Finally, the contents of BASE are restored to the value they contained when the word started executing.

To print the quantity 15 thousand dollars and 34 cents, enter

DECIMAL  DIN 1500034 D>DOLLAR↓
$15000.34   ok

DECIMAL ensures that DIN inputs the following number as a decimal 32-bit signed double number, and D>DOLLAR converts and prints the dollar amount.

Floating point output

 

Integer and double number mathematics

The standard FORTH philosophy is to use integer mathematics with little or no error checking to attain the highest execution speed. A wide variety of operations are available for signed or unsigned math using single or double precision numbers. The programmer decides which number types suit his or her needs, and selects the appropriate mathematical operators.

QED-Forth follows this philosophy for integer mathematics. The routines are coded for maximum speed and no error checking is performed. This section presents many of the operators and gives examples of their use.

Basic operations

Double number addition and subtraction

Mixed operations

Negation and absolute value

Fast scaling operations

 

Extended address arithmetic

Addition and subtraction of address offsets

Subtraction of extended addresses

Concatenations of extended address arithmetic operators



See also → Advanced Forth Programming Topics

 
This page is about: Forth Basics and Syntax, Forth Commands, Numbers in Forth, Forth Memory Access and Data Stack Operations, Local Variables in Forth, Integer and Floating Point Math in Forth, Decision Making in Forth, Formatted Output in Forth, Serial IO in Forth, Variables and Constants in Forth – Describes how to program in FORTH, an ideal microcontroller language for instrument control and automation, with emphasis on its basic concepts and syntax.
 
 
Navigation