Skip to main content

CamelForth

Resources

Basics

Some Fort implementations like gforth are case-insensitive, CamelForth is case-sensitive.

Comments

  • *not supported in CamelForth * \ (back slash) comment to the end of line.
  • ( and ) to comment inline.

Stack manipulation

1 2 3      \( put 1 2 and 3 on stack )
.          \( pip from stack and dispaly )
.sS         \( show stack (gfort) )
2 dupDUP      \( duplicate: 2 2 )
1 2 swapSWAP   \( swap 2 items: 2 1 )
1 2 overOVER   \( copy secondmost item to top: 1 2 1 )
1 2 dropDROP   \( drop top item: 1 )
1 2 3 rotROT  \( move thirdmost item to top: 2 3 1 )
1 2 nipNIP    \( drop secondmost: 2 )
1 2 tuckTUCK   \( dup top before secondmost: 2 1 2  )

Return stack

Word calls put address in return stack and ; is compiled as EXIT that pops it and jump to it. It is also used by loops to keep track of condition variables and iterators and value for I.

  • >R pops data stack value on return stack.
  • R> pops return stack value to data stack.
  • R@ duplicates returns stack value to data stack.

These can be used to temporary store values on return stack but it has to be balanced and may mess with I and loop data if used inside loops.

Note that these only work in compiled context.

Arithmetic

1 2 3 4 + - * \( 3+4=7, 2-7=-5, 1*-5=-5 )

Note that CamelForth .S displays signed integers, . will print correct result.

Defining words

: DUBL 2 * ; \( define DUBL word that puts 2 on stack and multiplies two stack items )
3 DUBL       \( now can be called like any other word: 6 )

Printing strings

Strings are put on stack as pair of address and lenght.length.

s": FOO S" Hello HxD!HxD"  \( record string on stack type)
\TYPE ;               ( print the string )
FOO

Note that in CamelForth S" can only be used in compiled context.

Introspection

wordsWORDS       \( list defined words )

Variables

Variables are words, calling vairable puts its address on stack.

variableVARIABLE blah  \( define variable word )
42 blah !      \( store 42 in blah variable )
blah @         \( put value on stack )
blah ?         \( read and print variable )

5 constantCONSTANT five  \( declare constant )
five spacesSPACES      \( print 5 spaces true    \ -1
false   \ 0)

In Forth ture is -1 and false is 0. CamelForth does not have TRUE and FALSE words defined.

Control flow

IF checks result on top of the stack (-1 - true, 0 - false). then is end of if.

variableVARIABLE var
4 var !
: test var @ 5 >
ifIF ." Greater" crCR
elseELSE ." Less or equal" crCR
thenTHEN ;
test

    not available in CamelForth ?DO takes end and start value and LOOP calls next iteration or is end of loop. DO iterates always once. I in context of iteration puts iteration count on the stack. 2 +loop will step by 2 while -1 +loop will count down. leave breaks the loop.
    : test 11 1 ?doDO iI . crCR loopLOOP ;
    test
    
    : fib 0 1
    beginBEGIN
    dupDUP >rR rotROT dupDUP rR> >      \( condition while)
    rotWHILE
    rotROT dupROT rotDUP ROT + dupDUP .  \( body repeat)
    dropREPEAT
    dropDROP dropDROP DROP ;         \( after loop has executed )
    20 fib
    
    : lcd
    beginBEGIN
    swapSWAP overOVER modMOD  \( body dup)
    DUP 0=         \( condition until)
    dropUNTIL DROP . ;
    27 21 lcd      ( 3 )
    

    Infinite loop.

    : test beginBEGIN ." Diamonds are forever" crCR againAGAIN ;
    

    Arrays

    Access works by adding offset of size cells to base array address and then accessing via it like normal variable.

    createCREATE foo 16 cellsCELLS allotALLOT  \( creates array foo with 16 elements )
    5 foo 2 cellsCELLS + !          \( store 5 in index 2 )
    4 foo !                    ( store 4 in index 1 )
    foo 2 cellsCELLS + @            \( put value at index 2 on stack )
    foo @                      ( put value at index 1 on stack )
    

    Constants are added with , word.

    createCREATE sizesSIZES 18 , 21 , 24 , 27 , 30 , 255 ,
    

    Strings

    Have two forms:

    1. Address and length - consumed by TYPE,
    2. Counted string - ALLOC allocated address where first byte is string length and rest is the string itself.
    • S" creates string constant. It can be copied to aan allocated buffer with CMOVE.
    • COUNT can be used to get count from counted string.
    • CHAR+ or CHARS adds character(s) worth of bytes to address.
    s": test S" hello bar" typeTYPE \;  ( puts address and length on stack and print it out )
    
    createCREATE foo 16 charsCHARS allotALLOT  \( allocate 16 character long buffer s")
    foo": hello S" Hello World!" ; ( put "Hello World!" )
    hello                      ( put address and length to "Hello World!" word )
    foo !                      \( store "foo" string lenght in first byte - c-addr )
    foo countCOUNT                  \( get the length and first byte address -c-addr c-addr u cmove)
    \CMOVE                      ( copy string from contant to the buffer+1 )
    foo countCOUNT typeTYPE             \( print counted string )
    

    On CamelForth foo COUNT looks like it gives wrong address but if one prints it with . it shows negative number that is actually 1 more than foo so it is correct.

    Place constant string in variable buffer.

    : placePLACE tuckTUCK ! countCOUNT cmoveCMOVE ;
    createCREATE bar 16 charsCHARS allotALLOT
    s": hello world"S" Hello World!" ; ( put "Hello World!" )
    hello bar placePLACE            \( placestore "helloHello world"World!" intoin bar )
    bar countCOUNT
    typeTYPE                       \( print "helloHello world"World!" from counted string variable bar )
    

    Note that CamelForth has non-standard >COUNTED that probably does what PLACE does.

    Pointers

    : goodbye ." Goodbye" crCR ;
    : hello ." Hello" crCR ;
    variableVARIABLE a
    : greetGREET a @ executeEXECUTE ;
    ' hello a !
    greetGREET
    ' goodbye a !
    greetGREET
    
      ' puts address of following word on stack without executing it. EXECUTE execute word from address on stack.

      Meta words

      : displaynumber createCREATE , doesDOES> @ . ;
      11 displaynumber anumber
      anumber
      
      • CREATE takes a name (as in createCREATE foo), in this case createCREATE anumber, and creates an empty word for that name.
      • , appends to the created word (storage) a cell from stack (11)
      • When calling created word (just anumber) it puts its address on the stack, @ can read cell behind that address (11).
      • DOES> will attach an acction to the word that executes when word is called (just anumber) and after its address is put on stack. In this example we read value from that words address (11) and print it with ..

      CamelForth Words

      Low level

          TABLE 1.  GLOSSARY OF WORDS IN CAMEL80.AZM
          Words which are (usually) written in CODE.
      
      NAME   stack in -- stack out          description
      
        Guide to stack diagrams:  R: = return stack,
        c = 8-bit character, flag = boolean (0 or -1),
        n = signed 16-bit, u = unsigned 16-bit,
        d = signed 32-bit, ud = unsigned 32-bit,
        +n = unsigned 15-bit, x = any cell value,
        i*x j*x = any number of cell values,
        a-addr = aligned adrs, c-addr = character adrs
        p-addr = I/O port adrs, sys = system-specific.
        Refer to ANS Forth document for more details.
      
                     ANS Forth Core words
      These are required words whose definitions are
      specified by the ANS Forth document.
      
      !      x a-addr --           store cell in memory
      +      n1/u1 n2/u2 -- n3/u3             add n1+n2
      +!     n/u a-addr --           add cell to memory
      -      n1/u1 n2/u2 -- n3/u3        subtract n1-n2
      <      n1 n2 -- flag           test n1<n2, signed
      =      x1 x2 -- flag                   test x1=x2
      >      n1 n2 -- flag           test n1>n2, signed
      >R     x --   R: -- x        push to return stack
      ?DUP   x -- 0 | x x                DUP if nonzero
      @      a-addr -- x         fetch cell from memory
      0<     n -- flag             true if TOS negative
      0=     n/u -- flag           return true if TOS=0
      1+     n1/u1 -- n2/u2                add 1 to TOS
      1-     n1/u1 -- n2/u2         subtract 1 from TOS
      2*     x1 -- x2             arithmetic left shift
      2/     x1 -- x2            arithmetic right shift
      AND    x1 x2 -- x3                    logical AND
      CONSTANT   n --           define a Forth constant
      C!     c c-addr --           store char in memory
      C@     c-addr -- c         fetch char from memory
      DROP   x --                     drop top of stack
      DUP    x -- x x            duplicate top of stack
      EMIT   c --           output character to console
      EXECUTE   i*x xt -- j*x   execute Forth word 'xt'
      EXIT   --                 exit a colon definition
      FILL   c-addr u c --        fill memory with char
      I      -- n   R: sys1 sys2 -- sys1 sys2
                           get the innermost loop index
      INVERT x1 -- x2                 bitwise inversion
      J      -- n   R: 4*sys -- 4*sys
                              get the second loop index
      KEY    -- c           get character from keyboard
      LSHIFT x1 u -- x2        logical L shift u places
      NEGATE x1 -- x2                  two's complement
      OR     x1 x2 -- x3                     logical OR
      OVER   x1 x2 -- x1 x2 x1        per stack diagram
      ROT    x1 x2 x3 -- x2 x3 x1     per stack diagram
      RSHIFT x1 u -- x2        logical R shift u places
      R>     -- x    R: x --      pop from return stack
      R@     -- x    R: x -- x       fetch from rtn stk
      SWAP   x1 x2 -- x2 x1          swap top two items
      UM*    u1 u2 -- ud       unsigned 16x16->32 mult.
      UM/MOD ud u1 -- u2 u3     unsigned 32/16->16 div.
      UNLOOP --   R: sys1 sys2 --       drop loop parms
      U<     u1 u2 -- flag         test u1<n2, unsigned
      VARIABLE   --             define a Forth variable
      XOR    x1 x2 -- x3                    logical XOR
      
                     ANS Forth Extensions
      These are optional words whose definitions are
      specified by the ANS Forth document.
      
      <>     x1 x2 -- flag               test not equal
      BYE    i*x --                      return to CP/M
      CMOVE  c-addr1 c-addr2 u --      move from bottom
      CMOVE> c-addr1 c-addr2 u --         move from top
      KEY?   -- flag        return true if char waiting
      M+     d1 n -- d2            add single to double
      NIP    x1 x2 -- x2              per stack diagram
      TUCK   x1 x2 -- x2 x1 x2        per stack diagram
      U>     u1 u2 -- flag         test u1>u2, unsigned
      
                     Private Extensions
      These are words which are unique to CamelForth.
      Many of these are necessary to implement ANS
      Forth words, but are not specified by the ANS
      document.  Others are functions I find useful.
      
      (do)   n1|u1 n2|u2 --  R: -- sys1 sys2
                                   run-time code for DO
      (loop) R: sys1 sys2 --  | sys1 sys2
                                 run-time code for LOOP
      (+loop)  n --   R: sys1 sys2 --  | sys1 sys2
                                run-time code for +LOOP
      ><     x1 -- x2                        swap bytes
      ?branch  x --                  branch if TOS zero
      BDOS   DE C -- A                   call CP/M BDOS
      branch --                           branch always
      lit    -- x         fetch inline literal to stack
      PC!    c p-addr --            output char to port
      PC@    p-addr -- c           input char from port
      RP!    a-addr --         set return stack pointer
      RP@    -- a-addr         get return stack pointer
      SCAN   c-addr1 u1 c -- c-addr2 u2
                                     find matching char
      SKIP   c-addr1 u1 c -- c-addr2 u2
                                    skip matching chars
      SP!    a-addr --           set data stack pointer
      SP@    -- a-addr           get data stack pointer
      S=     c-addr1 c-addr2 u -- n      string compare
                     n<0: s1<s2, n=0: s1=s2, n>0: s1>s2
      USER   n --              define user variable 'n'
      

      High level

           TABLE 1.  GLOSSARY OF "HIGH LEVEL" WORDS
            (files CAMEL80D.AZM and CAMEL80H.AZM)
      
      NAME   stack in -- stack out          description
      
        Guide to stack diagrams:  R: = return stack,
        c = 8-bit character, flag = boolean (0 or -1),
        n = signed 16-bit, u = unsigned 16-bit,
        d = signed 32-bit, ud = unsigned 32-bit,
        +n = unsigned 15-bit, x = any cell value,
        i*x j*x = any number of cell values,
        a-addr = aligned adrs, c-addr = character adrs
        p-addr = I/O port adrs, sys = system-specific.
        Refer to ANS Forth document for more details.
      
                     ANS Forth Core words
      These are required words whose definitions are
      specified by the ANS Forth document.
      
      #      ud1 -- ud2       convert 1 digit of output
      #S     ud1 -- ud2        convert remaining digits
      #>     ud1 -- c-addr u      end conv., get string
      '      -- xt              find word in dictionary
      (      --                      skip input until )
      *      n1 n2 -- n3                signed multiply
      */     n1 n2 n3 -- n4                    n1*n2/n3
      */MOD  n1 n2 n3 -- n4 n5     n1*n2/n3, rem & quot
      +LOOP  adrs --   L: 0 a1 a2 .. aN --
      ,      x --                   append cell to dict
      /      n1 n2 -- n3                  signed divide
      /MOD   n1 n2 -- n3 n4   signed divide, rem & quot
      :      --                begin a colon definition
      ;                          end a colon definition
      <#     --                begin numeric conversion
      >BODY  xt -- a-addr           adrs of param field
      >IN    -- a-addr            holds offset into TIB
      >NUMBER  ud adr u -- ud' adr' u'
                               convert string to number
      2DROP  x1 x2 --                      drop 2 cells
      2DUP   x1 x2 -- x1 x2 x1 x2       dup top 2 cells
      2OVER  x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2  per diag
      2SWAP  x1 x2 x3 x4 -- x3 x4 x1 x2     per diagram
      2!     x1 x2 a-addr --              store 2 cells
      2@     a-addr -- x1 x2              fetch 2 cells
      ABORT  i*x --   R: j*x --      clear stack & QUIT
      ABORT" i*x 0  -- i*x   R: j*x -- j*x  print msg &
             i*x x1 --       R: j*x --      abort,x1<>0
      ABS    n1 -- +n2                   absolute value
      ACCEPT c-addr +n -- +n'    get line from terminal
      ALIGN  --                              align HERE
      ALIGNED addr -- a-addr           align given addr
      ALLOT  n --              allocate n bytes in dict
      BASE   -- a-addr           holds conversion radix
      BEGIN  -- adrs         target for backward branch
      BL     -- char                     an ASCII space
      C,     char --                append char to dict
      CELLS  n1 -- n2                 cells->adrs units
      CELL+  a-addr1 -- a-addr2   add cell size to adrs
      CHAR   -- char              parse ASCII character
      CHARS  n1 -- n2                 chars->adrs units
      CHAR+  c-addr1 -- c-addr2   add char size to adrs
      COUNT  c-addr1 -- c-addr2 u      counted->adr/len
      CR     --                          output newline
      CREATE --              create an empty definition
      DECIMAL --             set number base to decimal
      DEPTH  -- +n             number of items on stack
      DO     -- adrs   L: -- 0        start of DO..LOOP
      DOES>  --           change action of latest def'n
      ELSE   adrs1 -- adrs2         branch for IF..ELSE
      ENVIRONMENT?  c-addr u -- false      system query
      EVALUATE  i*x c-addr u -- j*x    interpret string
      FIND   c-addr -- c-addr 0     ..if name not found
                       xt  1        ..if immediate
                       xt -1        ..if "normal"
      FM/MOD d1 n1 -- n2 n3     floored signed division
      HERE   -- addr         returns dictionary pointer
      HOLD   char --          add char to output string
      IF     -- adrs         conditional forward branch
      IMMEDIATE   --          make last def'n immediate
      LEAVE  --    L: -- adrs             exit DO..LOOP
      LITERAL x --      append numeric literal to dict.
      LOOP   adrs --   L: 0 a1 a2 .. aN --
      MAX    n1 n2 -- n3                 signed maximum
      MIN    n1 n2 -- n3                 signed minimum
      MOD    n1 n2 -- n3               signed remainder
      MOVE   addr1 addr2 u --                smart move
      M*     n1 n2 -- d       signed 16*16->32 multiply
      POSTPONE  --      postpone compile action of word
      QUIT   --    R: i*x --    interpret from keyboard
      RECURSE --             recurse current definition
      REPEAT adrs1 adrs2 --          resolve WHILE loop
      SIGN   n --                 add minus sign if n<0
      SM/REM d1 n1 -- n2 n3   symmetric signed division
      SOURCE -- adr n              current input buffer
      SPACE  --                          output a space
      SPACES n --                       output n spaces
      STATE  -- a-addr             holds compiler state
      S"     --                  compile in-line string
      ."     --                 compile string to print
      S>D    n -- d          single -> double precision
      THEN   adrs --             resolve forward branch
      TYPE   c-addr +n --         type line to terminal
      UNTIL  adrs --        conditional backward branch
      U.     u --                    display u unsigned
      .      n --                      display n signed
      WHILE  -- adrs              branch for WHILE loop
      WORD   char -- c-addr n  parse word delim by char
      [      --                enter interpretive state
      [CHAR] --               compile character literal
      [']    --          find word & compile as literal
      ]      --                   enter compiling state
      
                     ANS Forth Extensions
      These are optional words whose definitions are
      specified by the ANS Forth document.
      
      .S     --                    print stack contents
      /STRING a u n -- a+n u-n              trim string
      AGAIN  adrs --           uncond'l backward branch
      COMPILE,  xt --            append execution token
      DABS   d1 -- +d2        absolute value, dbl.prec.
      DNEGATE d1 -- d2         negate, double precision
      HEX    --                  set number base to hex
      PAD    -- a-addr                  user PAD buffer
      TIB    -- a-addr            Terminal Input Buffer
      WITHIN n1|u1 n2|u2 n3|u3 -- f     test n2<=n1<n3?
      WORDS  --                 list all words in dict.
      
                     Private Extensions
      These are words which are unique to CamelForth.
      Many of these are necessary to implement ANS
      Forth words, but are not specified by the ANS
      document.  Others are functions I find useful.
      
      !CF    adrs cfa --      set code action of a word
      !COLON --            change code field to docolon
      !DEST  dest adrs --        change a branch dest'n
      #INIT  -- n         #bytes of user area init data
      'SOURCE  -- a-addr           two cells: len, adrs
      (DOES>)  --              run-time action of DOES>
      (S")   -- c-addr u           run-time code for S"
      ,BRANCH xt --         append a branch instruction
      ,CF    adrs --                append a code field
      ,DEST  dest --            append a branch address
      ,EXIT  --             append hi-level EXIT action
      >COUNTED  src n dst --        copy to counted str
      >DIGIT n -- c                 convert to 0..9A..Z
      >L     x --   L: -- x         move to Leave stack
      ?ABORT f c-addr u --            abort & print msg
      ?DNEGATE  d1 n -- d2      negate d1 if n negative
      ?NEGATE  n1 n2 -- n3     negate n1 if n2 negative
      ?NUMBER  c-addr -- n -1    convert string->number
                      -- c-addr 0      if convert error
      ?SIGN  adr n -- adr' n' f       get optional sign
           advance adr/n if sign; return NZ if negative
      CELL   -- n                      size of one cell
      COLD   --                 cold start Forth system
      COMPILE --          append inline execution token
      DIGIT? c -- n -1          ..if c is a valid digit
               -- x  0          ..otherwise
      DP     -- a-addr             holds dictionary ptr
      ENDLOOP  adrs xt --   L: 0 a1 a2 .. aN --
      HIDE   --                "hide" latest definition
      HP     -- a-addr                     HOLD pointer
      IMMED?  nfa -- f             fetch immediate flag
      INTERPRET    i*x c-addr u -- j*x
                                 interpret given buffer
      L0     -- a-addr            bottom of Leave stack
      LATEST -- a-addr          last word in dictionary
      LP     -- a-addr              Leave-stack pointer
      L>     -- x   L: x --       move from Leave stack
      NFA>CFA  nfa -- cfa        name adr -> code field
      NFA>LFA  nfa -- lfa        name adr -> link field
      R0     -- a-addr              end of return stack
      REVEAL --              "reveal" latest definition
      S0     -- a-addr           end of parameter stack
      TIBSIZE  -- n                         size of TIB
      U0     -- a-addr           current user area adrs
      UD*    ud1 d2 -- ud3           32*16->32 multiply
      UD/MOD ud1 u2 -- u3 ud4          32/16->32 divide
      UINIT  -- addr       initial values for user area
      UMAX   u1 u2 -- u                unsigned maximum
      UMIN   u1 u2 -- u                unsigned minimum