?

Log in

No account? Create an account
Interrupts - Luke's Weblog [entries|archive|friends|userinfo]
Luke Gorrie

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

Interrupts [Mar. 29th, 2008|07:31 pm]
Luke Gorrie
[Tags|]
[Current Location |koh phi phi]

Been writing Forth from the hammock for a while now.

I'm reasonably happy with my little interrupt dispatcher which has an interface like this:

\ interrupt service routine for TIMER1 interrupt
: ticker ." tick " TIMER1 clear-timer-interrupt ;

['] ticker int$TIMER1 isr!
int$TIMER1 enable-interrupt
and the machinery looks like this:
\
\ VIC: Vectored Interrupt Controller
\

VARIABLE isr-table 32 cells ALLOT

: isr-entry ( n -- ) cells isr-table + ;
: isr! ( cfa int# -- ) isr-entry ! ;
: isr@ ( int# -- cfa ) isr-entry @ ;

: service-interrupt ( n -- )
    dup isr@   ( n isr|0 )
    ?dup 0<> IF
        execute
        drop
    ELSE
        ." Unhandled interrupt #" . cr
    THEN
;

: ack-IRQs ( -- ) $0 VICVectAddr ! ;

\ Call the ISR for each enabled and asserted IRQ.
: isr-dispatch ( -- )
    VICIRQStatus @
    32 0 DO ( IRQs )
        dup i bit-set? IF
            i service-interrupt
        THEN
    LOOP
    drop
    ack-IRQs
    EXIT-ISR
;

\ Setup generic interrupt dispatching.
: init-VIC
    32 0 DO  0 i isr!  LOOP
    ['] isr-dispatch forth-isr !
    c-isr @ VICDefVectAddr !  \ default non-vectored IRQ handler
;

\ Utilities for ISRs

: clear-timer-interrupt ( TIMER -- )
    $FF swap T-IR + !   ( clear all interrupt flags )
;

: enable-interrupt ( int# -- ) bit VICIntEnable ! ;
For now there's some C glue code to catch the interrupt and call back into Forth and I'm doing the vectoring in Forth instead of the hardware. One step at a time.

To communicate between I/O interrupt handlers and "normal" code I spent most of the day writing a simple ring (bounded buffer) data structure. I'm not thrilled at how much stack-munging code this turned out to be. I reckon that either I didn't write it very well, or I could have taken a much simpler approach in general, or both.

\ Ring data structure to use as a bounded-buffer.
\ The ring is initialized with two parameters:
\   MAX: The most items that the ring can contain.
\   ITEM-SIZE: The number of words per item in the ring.
\ The basic operations are RING-EMPTY? RING-FULL? >RING RING@ RING>
\ Each add/remove from the queue moves ITEM-SIZE words between the
\ queue and the stack.
\ 
\ For example if ITEM-SIZE is 3 then the stack effect of
\ >RING is ( a b c ring -- ) and RING@ or RING> is ( ring -- a b c ).
\
\ The idea is to have an interrupt handler like:
\ : can-send   ( dB dA len id ) can-tx-ring >ring ;
\ : can-tx-ISR ( -- ) can-tx-ring ring> ( dB dA len id ) can-tx
\ where this ring has four words per item.

\ Data structure layout:
\ MAX is the most items that the ring can hold.
: ring-max          ( ring -- addr ) ;
\ FIRST is the index (0..MAX-1) of the first item i.e. next to be taken.
: ring-first        ( ring -- addr ) cell+ ;
\ LENGTH is the number of items currently in the ring.
: ring-length       ( ring -- addr ) 2 cells + ;
\ ITEM-SIZE is the number of words per item.
: ring-item-size    ( ring -- addr ) 3 cells + ;
\ DATA is the start address of the real data area.
: ring-data         ( ring -- addr ) 4 cells + ;
\ See the example down below for how to initialize a ring.

: ring-empty? ( ring -- flag )
    ring-length @ 0=
;

: ring-full? ( ring -- flag )
    dup ring-length @ ( ring len )
    swap ring-max @   ( len max )
    =
;

\ Return the address of the first (next) data element.
: ring-first-addr ( ring -- addr )
    dup ring-first @          ( ring n )
    over ring-item-size @ cells ( ring n size )
    *                         ( ring offset )
    swap ring-data +          ( element-addr )
;

\ Return the address of the last data element. 
: ring-last-addr ( ring -- addr )
    dup ring-first @          ( ring n )
    over ring-length @ +      ( ring n )
    over ring-max @ mod       ( ring n )
    over ring-item-size @ cells ( ring n size )
    *                         ( ring offset )
    swap ring-data +          ( element-addr )
;

\ Copy the elements of the next data element onto the stack.
: ring@ ( ring -- ... )
    dup ring-first-addr swap ( addr ring )
    ring-item-size @         ( addr size )
    0 DO                     ( addr )
        dup @ swap           ( ... v addr )
\        ." fetched " over . ." from " dup . cr
        cell+
    LOOP                     ( v1 .. vn addr )
    drop                     ( v1 .. vn )
;

\ Drop the next element from the ring.
: ring-drop ( ring -- )
    dup ring-length --
    dup ring-first @ 1+  ( ring first' )
    over ring-max @ mod  ( ring first'' )
    swap ring-first !
;

\ Move the next element of the ring onto the stack.
: ring> ( ring -- ... )
    dup >r
    ring@
    r> ring-drop
;

\ Move a data element from the stack onto the ring.
: >ring ( v1 .. vn ring -- )
    dup ring-last-addr            ( ... ring addr )
    over ring-length ++           ( ... ring addr )
    \ insert the elements backwards so that they'll be taken out forwards
    over ring-item-size @         ( ... ring addr elem-size )
    1- cells +                    ( ... ring addr )
    swap ring-item-size @ 0 DO    ( ... addr )
\        cr ." store " over . ." at " dup .
        swap over                 ( v1 ... addr vn addr )
        !                         ( v1 ... vn-1 addr )
        cell-
    LOOP
    drop
;

: .ring ( ring -- )
    cr
    ." max:    " dup ring-max @ . cr
    ." first:  " dup ring-first @ . cr
    ." length: " dup ring-length @ . cr
    ." esize:  " ring-item-size @ .
;

\
\ Test/example code
\

\ Ring for testing
VARIABLE r 4 3 * ( four 3-word items ) 4 + ( and 4 words of header ) cells ALLOT

: init-r ( ) 
    \ Configure the ring
    4 r ring-max !
    0 r ring-first !
    0 r ring-length !
    3 r ring-item-size !
    \ Then you can do e.g.
    \ 1 2 3 r >ring   s:
    \ 4 5 6 r >ring   s:
    \ r ring>         s: 1 2 3
    \ r ring>         s: 1 2 3 4 5 6
    \ etc..
;
LinkReply

Comments:
From: (Anonymous)
2008-03-29 03:47 pm (UTC)

A couple possibilities

It looks pretty readable to me...but here are a couple thoughts which you may or may not find useful:

You could use the Forth 200x structures <http://www.forth200x.org/structures.html>:

0
 field: ring-max
 field: ring-length
 field: ring-first
 field: ring-item-size
 0 +field ring-data
constant /ring

You could also then make a defining word for rings:

: init-ring ( max item-size ring -- )
    dup >r ring-item-size !
    r@ ring-max !  
    0 r@ ring-length !
    0 r> ring-first ! ;

: ring ( max item-size "name" -- )
    create here >r  2dup * cells /ring + allot  r> init-ring ;

\ Ring for testing
4 3 ring r  \ four items of three cells each

You could use 'bounds' and 'I' in your DO loops:

: bounds ( addr size -- end-addr start-addr )  over + swap ;

: foo ... ( addr size ) bounds DO I @ LOOP ... ;

It might possibly be helpful to define indexing words...not sure about names, and haven't tried the refactoring, but maybe something like this?

: ring-index ( n ring -- offset )  
  dup >r  ring-first @ +  r> ring-max @ mod ;
  
: index>addr ( offset ring -- addr )
  dup >r  ring-item-size @ *  r> ring-data + ;

: ring-item ( n ring -- addr )
  tuck ring-index swap index>addr ;

--Josh

(Reply) (Thread)
[User Picture]From: lukego
2008-03-30 01:11 am (UTC)

Re: A couple possibilities

Thanks for the pointers!

I'm a bit amazed at how little code the structure machinery takes. Certainly makes things like DEFSTRUCT look a tad verbose.

(Reply) (Parent) (Thread)
[User Picture]From: darius
2008-03-29 05:44 pm (UTC)
Howdy -- I'm sorry for not answering your mail. Been bugged by RSI!

It looks like all the stack juggling comes from needing to hold on to the ring parameter. It could be avoided by making the ring implicit -- e.g. with a current-ring variable. Though I'd hesitate to actually do that since it adds more state to the system. (Another way, without that fault, but obviously with others: expand out all the ring words for every different ring, so you'd say can-tx-ring-ring> (or some such) instead of can-tx-ring ring>.)
(Reply) (Thread)
[User Picture]From: lukego
2008-03-30 01:17 am (UTC)
RSI! Use your keystrokes carefully.

Actually I had been wondering if Forth would make a nice language for keyboardless programming: just clicking on the names of words from a palette. I wonder if much work has been put into fancy Lisp/Smalltalk'ey development environments for Forth. I do like the Factor environment but it's not an Emacs-replacement as I understand it.
(Reply) (Parent) (Thread)
[User Picture]From: darius
2008-03-30 05:00 am (UTC)
Yeah, I was thinking Forth would make a good starting point for a spoken programming language. Recently I bought a new MacBook in order to run a new voice-recognition program, MacSpeech Dictate, which as it turns out crashes every time before I can even say anything to it. It's the first program I've bought in years -- that'll teach me. :)

Dunno about environments for Forth, but I liked the direction this was going in: http://www.accesscom.com/~darius/hacks/alph.tar.gz -- I think I've pointed it out before.

How would you keep the palette down to a reasonable size? Actually that suggests something like http://en.wikipedia.org/wiki/Dasher
(Reply) (Parent) (Thread)
From: ext_52574
2008-04-01 10:07 am (UTC)
Hey!
If I'm gonna be able to follow your forays into Forth land I better read up a bit. Can you recommend a good book or something?

Cheers, Tobbe
(Reply) (Thread)
[User Picture]From: lukego
2008-04-01 11:21 am (UTC)
Howdy Tobbe!

Classic Forth books have started appearing online for free. For basic language tutorial there is Starting Forth and for the philosophy of Forth there's Thinking Forth (especially recommended). I currently have Tony's copy of Forth for Professionals which is just amazing but hard to find!
(Reply) (Parent) (Thread)
From: (Anonymous)
2008-04-01 02:43 pm (UTC)

Synchronicity !

Hi, I am having a lot of fun with this Forth system : www.reda4.org which is a Colorforth inspired Forth system. It is especially well suited to games programming. It is being used by its creator to teach kids about programming.

Anyway, have fun !

Manuel
(Reply) (Parent) (Thread)
From: ext_52574
2008-04-01 03:16 pm (UTC)
Thanx, I'll have a look.
What system do you recommend of I want to play?

--Tobbe
(Reply) (Parent) (Thread)
From: (Anonymous)
2008-04-01 05:23 pm (UTC)
Try gforth for a first system...

There's also Stephen Pelc's book _Programming Forth_ (http://www.mpeforth.com/arena/ProgramForth.pdf). And if you're looking to buy an actual book, Forth, Inc.'s Forth Application Techniques is reportedly pretty good...

--Josh
(Reply) (Parent) (Thread)