next up previous contents index
Next: Index Up: Dreams Previous: Stacks.4th   Contents   Index



Util.4th


(

                  Utility words
                  Copyright (c) 1988 
                  Elijah Laboratories Inc.

    Written by:   R. J. Brown
                  Elijah Laboratories Inc.
                  201 West High Street
                  P. O. Box 833
                  Warsaw KY 41095
                  1 606 567-4613
                  rj brown @ ecfb / lmi


    This file defines various useful utility words.  It is
basically a catch-all repository for miscelaneous widgets.

   The material contained in this file is Copyright [c] 1988
Elijah Laboratories Inc.  All rights reserved world wide.

   Permission is hereby granted to reproduce this document in
whole or in part provided that such reproductions refer to the
fact that the copied material is subject to copyright by Elijah
Laboratories, Inc. No changes or modifications may be made to
the copied material unless it is clearly indicated that such
changes were not incorporated in the original copyrighted work.

)


( Machine independent word size tools. )
  WSIZE CONSTANT 1w      ( -- #bytes/word ; machine word size )
      1 CONSTANT 1b      ( -- #bytes/byte == 1 always in F83! )

  1w CONSTANT 1W  1b CONSTANT 1B  ( allow upper or lower case )

: w+ 1w +  ; : W+  w+  ;      ( n -- n+1w ; add 1 word offset )
: w- 1w -  ; : W-  w-  ; ( n -- n-1w ; subtract 1 word offset )
: w* 1w *  ; : W*  w*  ;         ( n -- n*1w ; n words offset )
: w*+ w* + ; : W*+ w*+ ; ( k n -- k+n*1w ; add n words offset )
: w/ 1w /  ; : W/  w/  ;      ( nb -- nw ; # bytes to # words )

 1w 2* CONSTANT 1d  \ no upper case equiv because of name clash
: d+ 1d + ;         \ with Forth-83 standard D-words.
: d- 1d - ;
: d* 1d * ;
: d*+ d* + ;
: d/ 1d / ;


 EXISTS? FPSIZE .IF FPSIZE CONSTANT 1f  1f CONSTANT 1F
: f+ 1f +  ; : F+  f+  ; : f-  1f - ; : F-  f-  ;
: f* 1f *  ; : F*  f*  ; : f*+ f* + ; : F*+ f*+ ;
: f/ 1f /  ; : F/  f/  ;
: IFIX FIX DROP ; ( float -- int ) .THEN


\ Determine the implementation dependant pointer size.
\ HERE                  \ dictionary position before ptr
\   NULPTR PTR P        \ allocate a pointer
\ HERE                  \ dictionary position after ptr
\   FORGET P            \ get rid of pointer
\ SWAP - CONSTANT 1p    \ compute size and give it a name
       1w CONSTANT 1p	\ for UR/Forth-386 !!!

\ Define pointer word size tools.
: p+    1p + ;
: p-    1p - ;
: p*    1p * ;
: p*+   p* + ;
: p/    1p / ;


( Words for handling segmented address space transparently. )

\ 1w 4 = .IF
\     : >S:O     ADDR>S&O ;
\     :  S:O>    S&O>ADDR ;
\ .ELSE
    : >S:O   ( Do nothing! )  ;  IMMEDIATE
    :  S:O>  ( Do nothing! )  ;  IMMEDIATE
\ .THEN

( The above is for LMI Forths.  Do whatever you have to here
  for your own favorite brand of the language.  )


( Convenient words to have around. )
        0 CONSTANT NIL       ( -- false ; logical False value )
  NIL NOT CONSTANT T           ( -- true ; logical True value )

: T!    T SWAP ! ;                        ( &v -- ; sets to T )
: NIL!  NIL SWAP ! ;                    ( &v -- ; sets to NIL )
: FLIP  DUP @ 0= SWAP ! ;            ( &v -- ; reverses truth )
: SETQ ' SWAP ! ;                ( &vec -- \ &vec SETQ <word> )
: ++    1 SWAP +! ;               ( addr -- ; increments word )
: --   -1 SWAP +! ;               ( addr -- ; decrements word )
: R++ R> 1+ >R ;              ( -- ; increment top of R-stack )
: R-- R> 1- >R ;              ( -- ; decrement top of R-stack )
: SWOOP SWAP DUP ;       ( x y -- y x x ; for combining tests )
: ... ; IMMEDIATE      ( elipsis "noise word" for stubs, etc. )
: 3DUP   2 PICK 2 PICK 2 PICK ;   ( copy top 3 stack elements )


: ?IF COMPILE ?DUP [COMPILE] IF ; IMMEDIATE

: NDUP BEGIN DUP WHILE 1-    ( ... n -- ... ... ; DUP n items )
       OVER SWAP REPEAT DROP ;

: NDROP 0 ?DO DROP LOOP ;          ( ... n -- ; DROPs n items )

: NSWAP ( a..b c..d n -- c..d a..b ; SWAPs n word elements )
        DUP 2* 1- SWAP
        0 ?DO
            DUP >R ROLL R>
        LOOP DROP ;


( Compile time, or "early binding", literal definition.

  Use as:  #[ bit1 bit2 ... ]#  for autocombining bit names,
      as:  #[ fld1 fld2 ... ]+  for constant structure offset,
   or as:   [ 2 3 + 7 * 5 / ]#  for compile time expression.  )

: #[ 0 [COMPILE] [ ; IMMEDIATE   ( begin compile time literal )

: ]# [COMPILE] ] [COMPILE] LITERAL ; IMMEDIATE       ( end it )

: ]+ [COMPILE] ]# COMPILE + ; IMMEDIATE ( end cmp time offset )

: ]- [COMPILE] ]# COMPILE - ; IMMEDIATE ( end negative offset )


(
  These words are used to give names to bits.

  Usage:
          #bits{ #bit <bit-1> ... #bit <bit-n> }#bits
)

: #bits{ 1 ;              ( begin a series of bit definitions )

: #bit CREATE DUP , 2* DOES> @ OR ; ( n -- 2n \ 2n ; name bit )

: }#bits DROP ;             ( end a series of bit definitions )


  8 CONSTANT BITS/BYTE              \ number of bits in a byte

: TRANSLATE-TABLE CREATE  ( -- \ TRANSLATE-TABLE name n , ... )
                  DOES> + C@ ;    ( n -- m ; translate a byte )

TRANSLATE-TABLE >MASK  ( bit# -- bit-mask ; return a bit-mask )
    1 C, 2 C, 4 C, 8 C, 16 C, 32 C, 64 C, 128 C,

: BIT[]       ( n -- mask offset ; to index into a bit string )
    BITS/BYTE /MOD SWAP >MASK SWAP ;

: BIT[]@ ( n base -- flag ; fetch truth value of a bit )
    SWAP BIT[] ROT + C@ AND 0<> ;

: BIT[]! ( flag n base -- ; store truth value to a bit )
    SWAP BIT[] SWAP >R +    \ flag addr <-P  R-> mask
    DUP @ R@ NOT AND        \ turn off addressed bit
    ROT 0<> R> AND OR       \ OR in flag's truth value
    SWAP ! ;                \ replace entire byte

: +BIT[] T -ROT BIT[]! ;              ( n base -- ; set a bit )
: -BIT[] NIL -ROT BIT[]! ;          ( n base -- ; clear a bit )

: ~BIT[]                           ( n base -- ; toggle a bit )
        2DUP BIT[]@ NOT -ROT BIT[]! ;   \ could be faster...


\ symbols ala Lisp

( Retrieve the unique tag associated with a symbol.  If the
  symbol is not defined, then create it, otherwise just return
  its address. )

: $           ( -- cfa \ $ <token> ; create <token> if needed )
    >IN @ BL WORD FIND          ( save input ptr & find token )
    IF NIP                  ( trash ptr & return cfa if found )
    ELSE DROP >IN ! CREATE                   ( else create it )
    LAST @ NAME> THEN ;                  ( and return its cfa )

: [$]                             ( compile time version of $ )
      LAST @ $         ( save so UNSMUDGE won't get confused! )
      [COMPILE] LITERAL LAST ! ; IMMEDIATE ( fix for UNSMUDGE )


\ embedded colon defs 

( Braces define "literal words" similar to unnamed LAMBDA
  expressions in Lisp.  The code> : foo bar baz ;
                                  : moby ... ['] foo ... ;
  may be replaced by -----------> : moby ... { bar baz } ... ;
  and acheive the same effect without making foo a word too. )

: { ( -- branch-patch-addr init-state unnamed-pfa )
    STATE @ DUP >R IF             ( begin an unnamed word definition )
      COMPILE branch        ( build skeleton branch around it )
      HERE 0 , 
    ELSE ] THEN 
    R> HERE ; IMMEDIATE  

: } ( branch-patch-addr init-state unnamed-pfa -- unnamed-cfa )
    COMPILE EXIT                   ( end definition with EXIT )
    CP @ SWAP PFA, nest JMP, SWAP	\ build code field
    IF SWAP HERE OVER - SWAP ! ( patch offset into branch skel )
       [COMPILE] LITERAL  
    ELSE [COMPILE] [
    THEN ; IMMEDIATE                 ( compile cfa as literal )


\ odd exits & tock

( A good old fashioned GOTO is sometimes quite useful. )
: GOTO R> DROP >BODY >S:O >R ;     ( &word -- \ ['] word GOTO )
: GO ' [COMPILE] LITERAL COMPILE GOTO ; IMMEDIATE     \ GO word

( These words return from the word that called them. )
: 2EXIT R> R> 2DROP ;                  ( double whammy return )
: ;; COMPILE R> COMPILE DROP [COMPILE] ; ; IMMEDIATE  ( ditto )

: ?EXIT IF R> DROP THEN ;       ( conditional exit ala muLisp )

( `, pronounced "tock" does either a : or R: as needed.  If
  tick provides the address, tock provides the data.  )
: ` >IN @ >R BL WORD R> >IN ! FIND IF R: ELSE : THEN ;


\ debugging aids

: X. BL EMIT BASE @ 16 BASE !
     SWAP 4 U.R BL EMIT BASE ! ; ( n -- ; hex print )

: .' ' CR DUP       ."  cfa " X.    \ show name & addresses
          DUP >BODY ."  pfa " X.
      BL EMIT >NAME .NAME ;

\ stolen from C
: |! OVER @ OR SWAP ! ;        ( addr bits -- ; *addr |= bits )

\ stolen from FORTRAN IV
: ** ( k n -- k**n ; raise integer to an integer power )
        1 SWAP 0 DO OVER * LOOP NIP ;


(   This word delays execution for the specified number of
timer ticks.  Since the need to delay occurs frequently in
the source code, and it is handled differently depending on
whether multitasking is being used or not, it is isolated here
to provide a single point of change for maintenence reasons. )

   VARIABLE #ticks                      \ timer cell

: ticks-delay ( n -- )                  \ delay n ticks
        #ticks !                        \ initialize ticker
        #ticks TICKER DROP              \ start ticker
        BEGIN #ticks @ WHILE REPEAT ;   \ wait till expired

: BETWEEN? ( x i j -- flag ) 	       \ T if i <= x <= j else NIL
	   >R OVER <= SWAP R> <= AND 0<> ;

ASCII A CONSTANT 'A'	ASCII Z CONSTANT 'Z'
ASCII a CONSTANT 'a'	ASCII z CONSTANT 'z'

: TO-UPPER ( ^string ^STRING -- ^STRING ) \ convert a string to upper case
	   OVER C@ OVER C!		  \ copy length
	   OVER C@ 1+ 1 ?DO		  \ copy & convert string 
		OVER I + C@		  \ get source char
		DUP 'a' 'z' BETWEEN?	  \ is it lower case?
		IF 'a' - 'A' + THEN	  \ yes, make it upper
		OVER I + C! LOOP NIP ;	  \ put in STRING, &ct.

DECIMAL 13 CONSTANT <CR>   \ carriage return is line delimiter

: .\ <CR> WORD COUNT TYPE CR ; IMMEDIATE    \ for messages in INCLUDE files

: D>S COMPILE DROP ; IMMEDIATE		    \ for symetry with S>D



Robert J. Brown
1999-09-26