Menu

Circular buffer implementation

2024-07-20
2024-08-08
  • David C. Norris

    David C. Norris - 2024-07-20

    Just wanted to share this PIC24 implementation of a circular buffer, for collecting the last n readings from a set of sensors. (It iterates in temporal order, from oldest to latest entry, so that more-recent data will overplot earlier data.) This is my first Forth data structure, so I would welcome tips or comments from the experts!

    \ circbuf.fs
    \ Generic circular buffer
    \ https://en.wikipedia.org/wiki/Circular_buffer
    \
    \ David C. Norris  2024-07-18
    
    -circbuf
    marker -circbuf
    decimal ram
    
    \ "If one or more parameters are to be compiled, or if space for
    \  variable data is to be allocated, it is convenient to use a
    \  previously defined defining word to handle that."
    \   -- Conklin & Rather p.172
    
    : all0t ( u -- ) here swap dup allot erase ;
    \ Allocate w-word records in a length-n array prefixed by w, n, and index i.
    : records: ( "name" +w +n -- ) ram create 2dup * rot , swap , -1 , cells all0t ;
    : circbuf: ( "name" +w +n -- ) records: does> ( -- w &i n ) @+ swap @+ ;
    
    : @- ( a-addr -- a-addr-2 x ) dup 2- swap @ ;
    : (+cb) ( &i -- &[i+1]%n ) dup @- 1+ ( &i &n i+1 ) swap @ mod over ! ;
    : (r0-) ( w &i' -- w &r0-2 ) 2dup @ * cells + ; \ thus 2+ @ reads first record
    : (+>) ( w &i -- &r0-2 w ) (+cb) (r0-) swap ;
    
    : into ( "bufname" x1..xw ) ' execute drop (+>) for 2+ tuck ! next drop ;
    : each ( w &i << xw..x1 w ) 2dup (+>) tuck for 2+ tuck @ swap rot next drop ;
    : cb. ( w &i n -- ) cr for each for . ."  " next cr next 2drop ;
    
    marker -tests
    
    3 7 circbuf: cbuf
    : test1 ( -- ) cbuf swap @ -1 = swap 7 = and swap 3 = and abort" ERROR" ." OK" ;
    test1
    1 2 3 into cbuf
    4 5 6 into cbuf
    cbuf cb.
    7 8 9 into cbuf
    cbuf cb.
    
    -tests
    
     
    • David C. Norris

      David C. Norris - 2024-08-08

      Here's an updated version, pursuant to this advice from Mikael. It works very nicely in my application.

      \ circbuf.fs
      \ Generic circular buffer
      \ https://en.wikipedia.org/wiki/Circular_buffer
      \
      \ David C. Norris  2024-08-05
      
      -circbuf
      marker -circbuf
      decimal ram
      
      : all0t ( u -- ) here swap dup allot erase ;
      \ Allocate w-word records in length-n array: (w,n,&i) ~> flash, index i ~> ram.
      : (index,) ( u i -- ) ram here flash , ram , cells all0t ;
      : records: ( "name" +n +w -- ) flash create 2dup , , * -1 (index,) ;
      : circbuf: ( "name" +n +w -- ) records: does> ( -- w &&i n ) @+ swap @+ ;
      
      : @- ( a-addr -- a-addr-2 x ) dup 2- swap @ ; ( The missing counterpart to @+ )
      : (+>) ( w &&i -- &xw-2 w ) @- dup @ 1+ rot @ mod over ! 2dup @ * cells + swap ;
      
      : each ( w &&i << xw..x1 w ) 2dup (+>) tuck for 2+ dup @ rot rot next drop ;
      : circbuf. ( w &&i n -- ) cr for each for . ."  " next cr next 2drop ;
      
      : >circbuf ( x1..xw w &&i n -- ) drop (+>) for 2+ tuck ! next drop ;
      
      marker -tests
      
      7 3 circbuf: cbuf
      : >cbuf ( x1 x2 x3 w &&i n -- ) cbuf >circbuf ;
      : .cbuf ( -- ) cbuf circbuf. ;
      
      : test1 ( -- )
        cbuf swap @ @ -1 = swap 7 = and swap 3 = and abort" ERROR" ." OK"
      ;
      test1
      
      1 2 3 >cbuf
      4 5 6 >cbuf
      cbuf circbuf.
      7 8 9 >cbuf
      cbuf circbuf.
      
      : test2 ( x1 x2 x3 -- ) cbuf >circbuf cbuf circbuf. ;
      2 2 2 test2
      
      : test3 ( x1 x2 x3 -- ) >cbuf .cbuf ;
      3 3 3 test3
      
      ( The CIRCBUF: defining word can be curried )
      : htbuf: ( "name" +n -- ) 4 circbuf: ; \ <-- ie, a CIRCBUF: with 4-word records
      
      5 htbuf: humtemp
      : >humtemp ( h1 t1 h2 t2 4 &&i n -- ) humtemp >circbuf ;
      : .humtemp ( -- ) humtemp circbuf. ;
      
      7624 7577 7790 7357 >humtemp
      .humtemp
      
      -tests
      
       

      Last edit: David C. Norris 2024-08-08

Log in to post a comment.