?

Log in

Forth is bliss - Luke's Weblog [entries|archive|friends|userinfo]
Luke Gorrie

[ website | My Website ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Forth is bliss [Mar. 22nd, 2008|10:03 am]
Luke Gorrie
[Tags|]
[Current Location |koh phi phi]

I'm programming in Forth on an exotic island and life is just fine. I've done two weeks of serious hacking now: the first to bring up Forth on the Keil MCB2100 board and the second to write drivers and make some extensions to Forth. Serious fun!

Forth is amazing. There's hardly any compiler or runtime system and yet it has all the sophistication of Lisp. This feels like discovering Gödel, Escher, Bach faithfully expressed in a few dozen cave paintings.

The best part about embedded programming is starting from scratch without using a single line of code that we didn't write outselves. Right now we don't have memory protection, dynamic memory allocation, concurrency, garbage collection, etc. So we're going to rediscover first-hand at what point it's worth developing these fancy features. Truly we're retreading some giant steps :-)

Here's some Forth I wrote that may be clever or stupid, I'm not sure which yet:

\ Running time profiling: t( ... )t
: t(
    r> tick      ( r t1 )
    >r >r        ( r: t1 r )
;
: )t
    tick r> r>   ( t2 r t1 )
    swap >r      ( t2 t1 )
    -            ( tdelta )
    . ." ms" cr
;
Ah the rapture of learning a new programming language :-)
LinkReply

Comments:
From: (Anonymous)
2008-03-22 05:42 am (UTC)

Factor

I'm sure you've heard of it, but just in case you haven't you should look at Factor. It's a forth like language, with all of those things you are rediscovering. http://factorcode.org/
(Reply) (Thread)
From: ext_26971
2008-03-22 08:40 am (UTC)

Messing with the return stack

Nice to see a Forth post. You could also do: (totally untested)
: t( postpone tick postpone >r ; immediate
: (t)) tick swap - . ." ms" cr ;
: t) postpone r> postpone (t)) ; immediate
which doesn't make any assumption about the return stack being anything other than a secondary stack ("retain stack" as Factor calls it). And it adds less runtime overhead. This also allows you to let profiling information in place for your critical routines without any penalty if you don't want to use it:
0 value profiling? (change 0 to 1 to activate profiling)
: t( profiling? if postpone tick postpone >r then ; immediate
: (t)) tick swap - . ." ms" cr ;
: t) profiling? if postpone r> postpone (t)) then ; immediate
(Reply) (Thread)
[User Picture]From: lukego
2008-03-22 12:26 pm (UTC)

Re: Messing with the return stack

Nice!

Is there a simple backquote trick to avoid typing "postpone" so much?

I'm imagining something like:

: t( ` tick >r ` ; immediate


Edited at 2008-03-22 12:29 pm (UTC)
(Reply) (Parent) (Thread)
From: ext_26971
2008-03-22 03:44 pm (UTC)

Re: Messing with the return stack

Not easily as you describe (you would have to handle numerical literals separately anyway, as they are put in by using 3 postpone literal rather than postpone 3 are they are not words by themselves).

However, you can define ` as being a synonym for postpone:

: ` postpone postpone ; immediate


This is Forth after all :)
(Reply) (Parent) (Thread)
[User Picture]From: graydon
2008-03-22 05:24 pm (UTC)
It has a wonderful charm for expressing yourself, and a wonderful economy of primitives; I wish it translated better to the ability to read the code of others.

I think some languages are, in a sense, too good at factoring and minimalism for their own good: by the time you're done factoring things down, only you can tell what the code means anymore. The Forth and APL families are the clear winners. The Prolog and Snobol families are perhaps runners-up, with the Lisp and Smalltalk families taking the bronze (and inching into territory where team programming seems possible).
(Reply) (Thread)
[User Picture]From: lukego
2008-03-23 10:49 am (UTC)
Lots of people do regard Forth as write-only but I'll have to discover this first hand. I'm working on a codebase written by a friend who had a head-start and so far I find the code pleasantly easy to understand (and fun -- learning tricks). Hardware is pretty friendly too - I wonder if Unix's serial port interface is any easier than poking the UART registers directly?

But I do always love the shiny new language I'm learning so this is all subject to post-novelty revision later :-)
(Reply) (Parent) (Thread)
[User Picture]From: lukego
2008-03-23 11:25 am (UTC)
For concreteness here's the actual UART driver:
\ UART Driver:
: uart-tx? ( uart -- flag )
    ULSR +		      ( register )
    @ $20 and                 \ TEMT flag
;

: uart-tx ( char uart -- )
    BEGIN dup uart-tx? UNTIL  ( char uart )
    UTHR + !
;

: uart-rx? ( uart -- flag )
    ULSR +		      ( register )
    @ $01 and		      \ RDR flag
;

: uart-rx ( uart -- char )
    BEGIN dup uart-rx? UNTIL  ( uart )
    URBR + @
;
And here's the higher-level string-based parts:
\ Convert from file descriptor number to UART base address.
: FD->UART ( fd -- uart )
    CASE
	0 OF $E000C000 ENDOF
	1 OF $E0010000 ENDOF
    ENDCASE
;

: sys-write ( c-addr len fd -- ior )
    dup fd-ok? IF
        fd->uart      ( c-addr len uart )
        swap rot      ( uart len c-addr )
        bounds DO     ( uart )
            i c@ over ( uart char uart )
            uart-tx   ( uart )
        LOOP
        drop 0        ( ior )
    ELSE
        drop drop drop
        9
    THEN
;        

\ FIXME: Not tested.
: sys-read ( c-addr len fd -- sz ior )
    dup fd-ok? IF
        over >r         \ r: len
        fd->uart        ( c-addr len uart )
        swap rot        ( uart len c-addr )
        bounds DO       ( uart )
            dup uart-rx ( uart char )
            i c!        ( uart )
        LOOP
        r> 0            ( sz ior )
    ELSE
        drop drop drop
        0 9
    THEN
;
ok and in honesty there's a little more code for setting up the baud rate:
\ Initialize pins for UART functions.
: init-tty-PINSEL ( fd -- )
    0 = IF
	1 0 io-0-function     \ P0.0 Function TxD UART0
	1 1 io-0-function     \ P0.1 Function RxD UART0
    ELSE
	1 8 io-0-function     \ P0.8 Function RxD UART1
	1 9 io-0-function     \ P0.8 Function RxD UART1
    THEN
;    

\ Convert from file descriptor number to UART base address.
: FD->UART ( fd -- uart )
    CASE
	0 OF $E000C000 ENDOF
	1 OF $E0010000 ENDOF
    ENDCASE
;

: uart-baud-divisor ( baud -- )
    16 *              \ baud*16
    dup PCLK + 1 -    \ baud*16 baud*16+PCLK-1
    swap /            \ (pclk+baud*16-1)/baud*16  ( rounded divisor )
;

: set-tty-baud ( baud fd -- )
    FD->UART >r		      ( baud  r: uart )
    $80 r@ ULCR + !
    uart-baud-divisor
    dup $FF and   r@ UDLL + !
        8 rshift  r@ UDLM + !
    $4F r@ UFCR + !
    $33 r@ ULCR + !
    r> drop
;

: init-tty ( baud fd -- )
    dup init-tty-PINSEL			( baud fd )
    set-tty-baud
;
so it does add up a bit but it's pretty straightforward. :-)
(Reply) (Parent) (Thread)
From: grettke
2008-03-22 11:35 pm (UTC)

What do you do for living?

In Tibet working on the OLPC XO, now Thailand coding Forth... are you guys hiring? :)
(Reply) (Thread)
[User Picture]From: lukego
2008-03-23 10:49 am (UTC)
I'd reckon that most programming jobs could be done from here. Work on your boss :-)
(Reply) (Parent) (Thread)
From: doublecnz
2008-03-24 11:13 pm (UTC)
Where is here? Looks nice :)
(Reply) (Parent) (Thread)
[User Picture]From: lukego
2008-03-26 02:52 am (UTC)
This is Koh Phi Phi in Thailand. Beautiful beaches, great food, and wireless internet :-) but the perfect weather will turn stormy hereabouts in the coming weeks so I don't recommend coming this way now.
(Reply) (Parent) (Thread)
From: (Anonymous)
2008-04-08 04:21 am (UTC)

Sooo jealous

Maybe I need to reconsider my big company ways and try another startup... someplace with coconuts!

-Tim
(Reply) (Parent) (Thread)