Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Embedded Systems

A Timed Event Network Scheduler in Forth


FEB89: A TIMED EVENT NETWORK SCHEDULER IN FORTH

A TIMED EVENT NETWORK SCHEDULER IN FORTH

Forth is particularly well suited to the development of real-time kernels

Gregory R.S. Ilg and R.J. Brown

Gregory Ilg and R.J. Brown are consultants involved in the development of microprocessor- and minicomputer-based real-time systems. Mr. Brown can be reached at Elijah Laboratories Inc., P.O. Box 833, Warsaw, KY 41095. Mr. Ilg can be reached at Computer Strategies, Inc., P.O. Box 180, Evon Lake, OH 44012.


The Timed Event Network Scheduler (TENS) is a scheduler we have developed that is well suited for real-time process control and industrial automation applications. One of its main features is that it has its own high-level language, which is an extension of Forth, in which dependencies among different events are specified. When any action or time duration in the system is altered, other modifications are not necessary in order to maintain and preserve these dependencies. At compilation time, the critical path through the network and its path length (measured in units of time) are displayed. Because of this approach, you can easily see which processes need to be streamlined in order to increase system throughput. The TENS paradigm was inspired by the critical path method (CPM) scheduling techniques used to manage large projects.

We implemented TENS using Laboratory Microsystems' UR/FORTH, Version 1.03, under PC-DOS 3.3 running on several XT- and AT-compatible computers. So far, TENS has been used to control the time sequencing of the fluid and pneumatic valves in a piece of complex medical equipment. Our intent was for a mechanical engineer to be able to tune the system timing without the aid of a software engineer. The results were quite favorable. The mechanical engineer quickly grasped the concepts of network sequencing and was able to develop some of the network topology without software engineering assistance and was easily able to tune the timing of the network to meet stringent design requirements.

This article describes the TENS system and provides examples of its use. The programs shown in Listings One and Two, page 92, do, however, make use of several Forth packages that, because of their length, are not reproduced here. The packages are included in the companion DDJ source disk and in the DDJ Forum on CompuServe. The TENS system can be used for any purpose as long as credit is given to Elijah Laboratories.

Description of TENS

Each node in a TENS network has associated with it an action to be performed when the node is first activated, a delay time, and an action to be performed after the delay time. The node is then exited. Both actions behave like normal Forth colon definitions and may use any number of normal Forth words.

Each node knows its immediate successors and predecessors in the network and cannot be activated until all its predecessors have exited. When a node exits, it informs all its successors that it has done so. Each node keeps track of which of its predecessors have already exited by means of a flag for each predecessor. When notified, this node examines all these flags to determine if it is now eligible to run. When the conditions for node activation have been met, all the flags are cleared before the node is activated.

Each TENS network is distinguished by a single entry and a single exit, called the head and tail, respectively. Between the network head and tail there may be zero or more nodes, subject to the constraint that there are no cycles. (A cycle would exist in the situation that an ultimate successor of a node could be that node's predecessor.) A network is known by its head, and typically the head has a name that appears as a Forth word in the dictionary. To run such a network, you execute its name as you would any other Forth word. These networks can in turn be used to construct larger networks.

A TENS Example

Let's suppose TENS were used in the development of the Egg-Master vending machine illustrated in Leo Brodie's book entitled Thinking FORTH. (Figure 1 is an adoption of the concept.) One of the networks it might use could be represented as shown in Figure 2 and Listing One. This network is responsible for handling the omelette selection available on the Egg-Master. Similar networks would handle the other selections. The word EGG_MASTER in Listing One handles the selection of which network to run and would be hooked to the power-up vector of the machine. The network in Figure 2 , whose head is Start-Cook-Omelette, runs the nodes Preheat-Griddle and Mix-Omelette in parallel. The node Pour-Mixture waits for both the nodes Mix-Omelette and Preheat-Griddle to complete before it runs. This is obviously necessary because the ingredients must be mixed and the griddle must be hot before the mixture is poured onto the griddle.

The node Mix-Omelette' in the Cook-Omelette network is itself a network that is a subnetwork of Cook-Omelette, but it can also be a subnetwork of other networks (such as a Cook-Scrambled network, should the Egg-Master Supreme ever make it out of the R&D lab). This is the network analogy to the linear-threaded concept of factoring. Because factoring is fundamental to writing good Forth code, we felt it was important to carry this practice into the parallel data-flow paradigm. TENS permits factoring of networks, thereby increasing their reusability and test-ability.

Run-Time Data Structures

Each network is implemented by a network-head data structure that contains pointers to the initial and terminal nodes of the network. The network head also provides storage for the critical path length.

Each node is implemented as a compound data structure. It is composed of a fixed area containing the CFAs of the entry and exit action words, the delay time, its path length from the start node, a pointer to a linked list of successors, and a variable-length array of pointers to its predecessors. This array is terminated with a NULL pointer. Each node in the linked list of successors is implemented as a pair of cells. The first cell points to the next node in the linked list, and the second cell points to the successor node in the TENS network (see Figure 3).

Run-Time Processing

Each TENS network is created by a CREATE DOES> word whose DOES> clause references the word TENS (see Listing Two). This word sets a trapdoor with CATCH; entry-action throws back to it when the entire network has finished executing. After this trapdoor is established, the timer is started and a pointer to the first node is passed to entry-action, which throws back to the trapdoor if the pointer is NULL. If it is not NULL, then the entry-action CFA is performed. Next, this node is put on the timer queue for the required delay time.

The timer queue is managed in accord with "An Efficient Algorithm for Large Priority Queues," by R. J. Brown (DDJ, June 1987). The priority is the dispatch time, which causes each node to be removed from the queue when its dispatch time occurs. As the flow of control proceeds through the network, there will be many nodes on the timer queue.

When a node is dispatched from the timer queue, its pointer is passed to exit-action, which causes that node's Exit-Action to be performed. At this point, the node is exiting, and its successors are notified. If there are no successors, then the entire network has finished running and the trapdoor is thrown back to. As each node receives notification, it checks to see if all its other predecessors have also exited. If they have, then that node is activated. This activation and exiting process is applied iteratively as the TENS scheduler traverses the network. When subnetworks are present, recursion occurs because more than one network is active at a time.

Although TENS implements a data-flow paradigm for parallelism, it is not a multitasking system. All of the parallelism results from having active nodes on the timer queue waiting for the delay interval between their Entry-Action and their Exit-Action. For this reason, these actions should have negligible processing time relative to the delay time.

Compile-Time Processing

During compilation the run-time data structures must be built (see Listing Two). The network head is instantiated by the defining word NETWORK, which leaves a pointer to this head on the stack for use by END-NETWORK.

Each node in the network can know only its predecessors at compile time because Forth does not generally allow forward references. After all nodes of the network have been compiled, the word END-NETWORK lexically delimits the entire network and causes additional compile-time processing to occur.

The first (and the only essential) post-processing operation is the generation of successor links. This is performed by a depth-first traversal of the network, starting at the tail and penetrating toward the head. Each pair of cells on a successor list is allocated as it is needed.

Once the successor links have been established, the length from the head to each node is computed by a similar depth-first search and then is saved with that node. Therefore, the critical path length will be found in the tail node.

The names of the nodes on the critical path are displayed starting with the tail. By choosing the predecessor with the largest path length, we are able to stay on the critical path. As each node on the critical path is encountered, its name (as stored in the dictionary header) is printed on the screen.

As a result of this compile-time post-processing, not only is the network linked in both directions but also the length of the critical path is computed and the names of the nodes it contains are displayed. The length of the critical path is a useful statistic for the designers because only those nodes that are actually on the critical path can have an effect on the critical path length. Thus, designers need direct their tuning efforts only to these nodes -- by changing the delay time of any node, they can affect the time required to run the entire network. When the duration of a node is changed, it is not necessary to change the times of any other nodes to keep the process in step. This is the beauty of the network-scheduling data-flow paradigm.

The display of the critical path and its length, together with the cell in each node to hold that node's path length, can be removed after development to produce a cross-compiled system without the extra overhead if so desired.

Conclusion

By having nontrivial processing done at compile time, a large amount of work is factored into the compilation of the network instead of into its execution. The Forth language is particularly well suited to the implementation of this approach because the full facilities of the language are available not only to execute the program but also to build that same program. Most other higher-level languages have no such facility. The C language has a limited compile-time processing facility. Most good assemblers have a macro capability, but these are separate languages distinct from the language that specifies the run-time behavior of the program. Lisp is the only other well-known language that has as good a compile-time processing facility; however, Lisp is generally too slow to be effective in real-time applications on microprocessors. Forth is deliberately designed to provide real-time performance on micros.

Bibliography

Brodie, Leo. Thinking FORTH. Engelwood Cliffs, N.J.: Prentice Hall, 1984.

Brown, Robert Jay. "An Efficient Algorithm for Large Priority Queues." DDJ 128 (June 1987).

_A Timed Event Network Scheduler in Forth_ by Gregory Ilg and R.J. Brown

[NOTE: FORTH SCREENS ACCOMPANY THESE LISTINGS]

[LISTING ONE]

<a name="0066_000b">

\   Hypothetical Network To Handle Omelette Selection On
\   Brodie's Egg-Master


        CONSULT TENS            \ Timed Event Network Scheduler

184 EQU ticks/10secs            \ system clock time constant

: MS ( miliseconds -- ticks )   \ time units conversion
    184 10000 */ ;

: sec ( seconds -- ticks )
    184 10 */ ;

: min ( minutes -- ticks )
    60 * sec ;


(  Define stubs for un-implemented words...  )

  STUB[ Break-Egg           seasoning-valve     open
        close               mixer               on
        off                 griddle             pour-valve
        wait-for-coins      Cook-Fried          Cook-Poached
        Cook-Hard-Boiled    Cook-Benedict   ]


(   Define buttons as an enumeration set...  )

  1 ENUM[   Fried-button
            Poached-button
            Hard-Boiled-button
            Benedict-button
            Omelette-button     ]

: read-a-button Omelette-button ;   \ always choose "omelette"

: None ;  IMMEDIATE  ( noise word to improve readability )



NETWORK Mix-Omelette                \ subnetwork also used
                                    \ in fancier omelettes

    NODE Start-Mix-Omelette         \ dummy for single entry
        Predecessors    NIL
        Entry-Action    None
        Delay           0 sec
        Exit-Action     None
    END-NODE


\ Wrap up subnetwork Break-Egg.  Break-Egg not detailed here

    NODE Crack-Egg
        Predecessors    Start-Mix-Omelette
        Entry-Action    Break-Egg
        Delay           0 sec
        Exit-Action     None
    END-NODE


\ concurrent with Crack-Egg

    NODE Add-Seasoning
        Predecessors    Start-Mix-Omelette
        Entry-Action    seasoning-valve open
        Delay           100 MS
        Exit-Action     seasoning-valve close
    END-NODE


    NODE Blend
        Predecessors    Crack-Egg
                        Add-Seasoning
        Entry-Action    mixer on
        Delay           3 sec
        Exit-Action     mixer off
    END-NODE


END-NETWORK  ( Mix-Omelette )




NETWORK Cook-Omelette               \ net head to process...
                                    \ ...Omelette-button

    NODE Start-Cook-Omelette        \ dummy for single entry
        Predecessors    NIL
        Entry-Action    None
        Delay           0 sec
        Exit-Action     None
    END-NODE


    NODE Preheat-Griddle
        Predecessors    Start-Cook-Omelette
        Entry-Action    griddle on
        Delay           30 sec
        Exit-Action     None
    END-NODE


\ subnetwork...

    NODE Mix-Omelette'
        Predecessors    Start-Cook-Omelette
        Entry-Action    Mix-Omelette
        Delay           0 sec
        Exit-Action     None
    END-NODE


    NODE Pour-Mixture
        Predecessors    Preheat-Griddle
                        Mix-Omelette'
        Entry-Action    pour-valve open
        Delay           2 min
        Exit-Action     pour-valve close
                        griddle off
    END-NODE


END-NETWORK  ( Cook-Omelette )



\ This word is hooked to the power-up vector on the
\ Egg-Master vending machine.


: EGG_MASTER ( -- )
    BEGIN  wait-for-coins  read-a-button  CASE
        Fried-button        OF Cook-Fried       ENDOF
        Poached-button      OF Cook-Poached     ENDOF
        Hard-Boiled-button  OF Cook-Hard-Boiled ENDOF
        Benedict-button     OF Cook-Benedict    ENDOF
        Omelette-button     OF Cook-Omelette    ENDOF
    ENDCASE AGAIN ;




<a name="0066_000c"><a name="0066_000c">
<a name="0066_000d">
[LISTING TWO]
<a name="0066_000d">

\                  TIMED EVENT NETWORK SCHEDULER
\           Copyright (c) 1988 Elijah Laboratories Inc.


( This package makes use of a number of programs that are
not detailed here.  The word CONSULT is used to load these
packages.  The packages are included in the companion DDJ
source disk. )


    CONSULT     STRUC           \ structure definitions
    CONSULT     PRIQUE          \ priority queue manager
    CONSULT     MACROS          \ for the evaluator
    CONSULT     BALLS           \ for backtracking control
    CONSULT     XSHEETS         \ transient storage words


  CREATE TQ NIL ,               \ the timer queue

  : nothing ; *MITT* SETQ nothing \ no default THROW handler
  6 INFLATE BALL tens.ball      \ trap door for exit
            BALL te.ball        \ ditto


(  A timed event network is a linked data structure composed of
the following timed event nodes. )

sizeof pq struc TEnode              \ a timed event node
1w              TEnode  word-1      \ execute before delay
1w              TEnode  delay       \ # ticks to delay
1w              TEnode  path-length \ path length from start
1w              TEnode  word-2      \ execute after delay
1w              TEnode  ^Succ's     \ ptr to successor list
0               TEnode  Pred's      \ start of predecessor list


(  These words start and stop the timer cell, and fetch its
value, and add its value to the top-of-stack, thereby
converting a time interval into an absolute time value.  )

  VARIABLE now                                \ the timer cell

: start-now ( -- ) -2 now ! now TICKER DROP ; \ start the timer

: stop-now ( -- ) now -TICKER ;               \ stop the timer

: now@ ( -- #tics )  now @ NEGATE ;           \ fetch the timer

: now+ ( delay -- time ) now@ + ;             \ add timer value


(  This word defines the processing that occurs when a node is
activated.  This consists of performing the entry action
routine and enqueueing for the required delay time.  )

: entry-action ( node -- )              \ start up a node
    >R                                  \ save node pointer
    R@ NIL = IF T tens.ball THROW THEN  \ if none, we're done!
    R@ BODY> >NAME CR .NAME             \ display its name
    R@ word-1 PERFORM                   \ do pre-delay stuff
    R@ delay @ now+ R@ key !            \ figure dispatch time
    TQ R> pq-enque ;                    \ put on timer queue


(  This word waits until the head of the timer queue is past
its dispatch time, and then dequeues the head element from the
timer queue for subsequent processing. )

: wait-till ( -- node )             \ take next node off queue
    TQ @ 0= ABORT" Empty timer queue "
    BEGIN TQ @ key @ now@ < UNTIL   \ wait until its time
    TQ pq-deque                     \ then remove it
." DQ " ;


(  The following structures are used in the word "notify".  The
first is passed as a parameter block, and the second is SPREAD
as a SHEET.  )

0 struc n.parm              \ passed parameters...
1w      n.parm  n.pred      \ ptr to predecessor
1w      n.parm  n.succ      \ ptr to its successor

0 struc n.temp              \ temporaries...
1w      n.temp  n.trig      \ trigger indicator


(  This word searches the predecessor list of a node for a
match with the cfa of a completing predecessor.  When the match
is found, the low order bit of the predecessor address is set
as a flag to indicate that that predecessor has completed.  If
all such flags are set, "n.trig" is set to trigger the node.  )

: set-done-flag ( _n.temp -- _n.temp )     \ set pred's done fg
    AT #[ parm n.parm n.succ ]+ SH@ Pred's \ pt to succ's preds
    BEGIN DUP @ ?DUP WHILE                 \ for all preds
        AT #[ parm n.parm n.pred ]+ SH@ =  \ we got a match?
        IF DUP 1 |! THEN                   \ yes, set done flag
        DUP @ AT n.trig SH@ AND            \ figure new trigger
        AT n.trig SH! w+ REPEAT DROP ;     \ save it & continue


(  This word clears all the done flags that were set to trigger
the activation of a node.  This word is performed just prior to
activating the node, thus preparing it for re-triggering at a
later time.  )

: clear-done-flags                  \ clear succ's done flags
    AT #[ parm n.parm n.succ ]+     \ point to successor
    SH@ Pred's                      \ point to his predecessors
    BEGIN DUP @ ?DUP WHILE          \ for all predecessors
        [ 1 NOT ]# AND OVER !       \ clear its done flag
    w+ REPEAT ;                     \ loop to end of list


(  The "notify" word tells a successor to a node that one of
its predecessors has completed.  If all of its predecessors
have completed, then that successor node is started.  )

: notify ( pred succ -- )       \ notify a successor
    sizeof n.temp SPREAD        \ make room for temps
    1 AT n.trig SH!             \ cock the trigger
    set-done-flag               \ set pred's done flag
    AT n.trig SH@ IF            \ has succ been triggered?
        clear-done-flags        \ yes, clear his done flags
        CRUSH NIP entry-action  \ & start him up!
    ELSE CRUSH 2DROP THEN ;     \ no, just return...


(  This word is executed after a node has been removed from the
timer queue after waiting its required delay time.  It causes
the exit action routine for that node to be performed, and then
notifies all the successors of that node that it  has completed
execution.  )

: exit-action ( node -- )           \ complete a node
    DUP word-2 PERFORM              \ do after delay stuff
    DUP ^Succ's DUP @ NIL =         \ point to successor chain
    IF T tens.ball THROW THEN       \ if none, we're done!
    BEGIN @ ?DUP WHILE              \ for all his successors
        2DUP w+ @ notify            \ notify them he's done
    REPEAT DROP ;                   \ then clean up & exit


(  This is the "Timed Event Network Scheduler", the entry point
for the DOES> word of a timed event network.  Running such a
network is done by executing the name of the network, which
calls TENS with the address of the network list head.  )

: TENS ( net -- )                       \ run a timed event net
    tens.ball CATCH IF                  \ exit via trap door?
                 DROP EXIT THEN         \ yes, stop timer, exit
    start-now @ entry-action BEGIN      \ no, start first node
        wait-till exit-action AGAIN ;   \ wait, then finish it


(  This word links a node into the successor lists of all of
its predecessors.  After this has been done recursively for
all the successors of that node, ad infinitum, then the entire
event network will be linked both forwards and backwards.  )

F: link-to-pred                 \ declare forward reference

: link-to-pred's ( node -- )    \ fix up forward lnks in network
    DUP Pred's >R               \ point to it predecessor list
    BEGIN R@ @ ?DUP WHILE       \ for all its predecessors...
        OVER SWAP link-to-pred  \ link this node to its pred
        R> w+ >R REPEAT         \ point to next pred ptr
    DROP R> DROP ;              \ tidy up stacks

R: link-to-pred ( node pred -- )    \ ping pongs with word above
    DUP >R                          \ remember predecessor ptr
    ^Succ's                         \ point to successor ptr
    BEGIN DUP @ ?DUP WHILE          \ for all successors...
        NIP DUP w+ @ 2 PICK =       \ duplicate?
        IF R> 3DROP EXIT THEN       \ yes, skip this node
    REPEAT                          \ until end of list
    HERE SWAP !                     \ append new CONS cell
    NIL ,                           \ CDR is NIL
    ,                               \ CAR is successor node
    R> link-to-pred's ;             \ fix his predecessors too!


(  This word kicks off the action to reverse link the event
network by passing the address of the first node to the
recursive network traversal algorithm.  It takes the address
of the network list head as its parameter.  )

: fix-Succ's ( head -- )        \ generate successor lists
    w+ @                        \ point to last node
    link-to-pred's ;            \ link it to its predecessors


( This word recursively traverses the network accumulating the
maximum path length to each node.  This length from the first
to last nodes is the critical path length, or network delay. )

: set-path-len ( ^node old-path-length -- )
    SWAP >R                         \ save this node's pointer
    R@ delay @ +                    \ add this delay to old len
    R@ path-length @                \ get current len
    MAX DUP                         \ new len is max of the two
    R@ path-length !                \ save new len
    R> Pred's >R                    \ point to pred ptrs list
    BEGIN R> DUP w+ >R @ ?DUP WHILE \ for all predecessors...
        OVER RECURSE REPEAT         \ set their path length too
    DROP R> DROP ;                  \ clean up and exit


(  This word calls the recursive network traversal routine to
compute the length of the critical path through the network.
The result is stored in the network's list head.  )

: set-path-lengths ( nethead -- )
    DUP w+ @                \ point to last node
    0                       \ initial path length is zero
    set-path-len            \ recursively set path lengths
    DUP @ path-length @     \ get computed critical path length
    SWAP 2 w*+ ! ;          \ save it in network head


(  This word displays the name of a network and its critical
path length in milliseconds, It is used as a tuning aid.  )

: .pathlen ( nethead -- )       \ display critical path length
        CR                      \ start on a new line
        ." Critical path of "   \ indentify it
        DUP BODY> >NAME .NAME   \ show network name
        ."  is "                \ more verbiage
        2 w*+ @                 \ fetch length in ticks
        10 184 */               \ convert to seconds
        .                       \ display the number
        ." seconds long. "      \ more verbiage
        CR ;                    \ and a new line


(  This word displays the critical path on the screen at
compilation time to facilitate tuning.  )

: .critpath ( nethead -- )
    ." Critical path is: " w+ @     \ point to last node
    BEGIN ?DUP WHILE                \ for each node on critpath
        CR 4 SPACES                 \ indent to look pretty
        DUP BODY> >NAME .NAME       \ display its name
        Pred's >R 0 0 BEGIN         \ for each predecessor
            R> DUP w+ >R @          \ get a predecessor pointer
            ?DUP WHILE              \ as long as they exist
            DUP path-length @       \ get its path length
            2 PICK OVER MAX         \ take maximum of old & new
            OVER = IF 2SWAP THEN    \ update max & ptr
            2DROP REPEAT R> 2DROP   \ discard excess baggage
        REPEAT CR ;                 \ loop till done


(  The Top-of-Stack is used by END-NETWORK to generate the
successor lists for all of the nodes in the network. For this
reason, the last node instantiated *MUST* be the unique
terminal node for the network. Likewise, the first node
instantiated *MUST* be the unique initial node for the
network. )

: END-NETWORK ( head first last -- )
        ROT >R SWAP             \ save head, put first on top
        R@ !                    \ save first in head
        R@ w+ !                 \ save last in head
        R@ fix-Succ's           \ generate successor lists
        R@ set-path-lengths     \ compute critical path length
        R@ .pathlen             \ display critical path length
        R> .critpath ;          \ display critical path


(  The following word is used to begin the definition of a
timed event network.  The network is terminated by the word
END-NETWORK.  )

: NETWORK ( -- )            \ begin a timed event network
        CREATE HERE         \ give it a name and remember where
        NIL , NIL ,         \ initialize first & last pointers
        NIL   NIL           \ initialize 2 pointers on stack
        0 ,                 \ initialize critical path length
        DOES> TENS ;        \ runs the scheduler when called


( This is the format for declaring a node in a TENS network.

        NODE <node-name>
            Predecessor     pred1 ... predn
            Entry-Action    word1 ... wordn
            Delay           n  \ tics
            Exit-Action     word1 ... wordn
        END-NODE
)


  NIL EQU ^NODE                     \ pointer to current node

: NODE ( -- )                       \ define network step node
        CREATE                      \ make a dictionary header
        HERE EQU ^NODE              \ remember where it is
        sizeof TEnode ALLOT         \ make room for it
        0 ^NODE path-length !       \ initialize path length
        NIL ^NODE ^Succ's !         \ terminate successor chain
        DROP ?IF ^NODE              \ set 1st
        ELSE ^NODE DUP THEN ;       \ & last pointers


: Predecessors                      \ start predecessor list
        DUP                         \ insert cushion
        te.ball CATCH NIL =         \ to end predecessor list
        IF BEGIN DEPTH >R EVAL      \ mark stack & eval token
                DEPTH R>            \ did eval return a value
                - 1 = IF ,          \ yes, store it as a pred
                      ELSE          \ no, more than one value?
    T ABORT" Illegal Predecessor "  \ yes, abort with a msg!
                      THEN          \ no value, treat as comment
            AGAIN THEN              \ build predecessor list
        DROP                        \ remove cushion
        CP @ ^NODE word-1 !         \ make entry action header
        HERE PFA, nest JMP,         \ stuff cfa and pfa into it
        ] ;                         \ compile entry action


: Entry-Action NIL , T te.ball THROW ; \ end predecessor list


: Delay                             \ specify delay time
    COMPILE EXIT                    \ last word in entry action
    [COMPILE] [                     \ set interpret state
    ; IMMEDIATE                     \ must run while compiling


: Exit-Action                       \ specify exit action
        ^NODE delay !               \ save delay time
        CP @ ^NODE word-2 !         \ make exit action header
        HERE PFA, nest JMP,         \ stuff cfa & pfa into it
        ] ;                         \ force compile state


: END-NODE                      \ terminate node definition
        COMPILE EXIT            \ last word in exit action
        [COMPILE] [             \ set interpret mode
        ; IMMEDIATE             \ must run while compiling













Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.