Periodic Jobs

Date:2017-08-09

Intro

With the previous sections we do have clock ticks, we do have the tools to keep track of time, so now what? We need a means to update the display periodically or do other fancy stuff, perhaps periodically too? So this section will show one way to call functions, every time a second, a minute, etc. is over.

Design Decisions

  • There will be a list of functions called jobs
  • There will be a section of code in the run-loop function, which inspects the overflow flags set in timeup and calls the above jobs and clears their flags
  • After each call of a job the main loop is run again to keep track of ticks and their duties. So this delays jobs for higher counters a little but not by much, but it calls job.ticks sooner again

Jobs

The jobs are regular functions which refrain from changing the stack. They are defined, their XTs are stored in a flash table.

: job.tick   ;
: job.sec    ( count uptime? toggle LED? update display? ) ;
: job.min    ( update display? ) ;
: job.hour   ( hit the gong? ) ;
: job.day    ;
: job.month  year @  month @ 1+  tu.upd.limits ;
: job.year   ;

create Jobs
  ' job.tick ,
  ' job.sec , ' job.min ,   ' job.hour ,
  ' job.day , ' job.month , ' job.year ,

In order to call them in a loop, an index variable jobCount is used. It is advanced from 1 (job.sec) to 6 every round through the main loop.

variable jobCount
: jobCount++ ( -- )  jobCount @  #6 < if  1 jobCount +!  then ;

TODO: give the magic #6 a name?

Measuring Uptime

A simple application of the whole idea is to count the uptime. A 2variable (32 bit) is defined. It is cleared at startup and incremented every second.

2variable uptime
: .uptime   ( -- )  uptime 2@  decimal ud. [char] s emit   ;
: ++uptime  ( -- )  1.  uptime 2@  d+  uptime 2! ;

: init
  ...
  0. uptime  2!
;

: job.sec   ( -- )   ++uptime ;

The uptime can be displayed with .uptime, however, either after exiting the main loop, or in the same or another job, or on the serial prompt after adding the multitasker.

After some time a more elaborate version .uptime.dhms looks better (# u0.r is used, because . places a space after the last digit):

: .uptime.dhms  ( -- )
  uptime 2@
  #60 ud/mod  #60 ud/mod  #24 ud/mod
  drop
  #3 u0.r [char] d emit space
  #2 u0.r [char] : emit
  #2 u0.r [char] : emit
  #2 u0.r
;

Putting it all together

All jobs and their handling is defined in the main program file. Checking the flags and calling the jobs needs to be done in run-loop.

\ main-....fs
include ewlib/clockticks_clock_crystal.fs
include ewlib/timeup_v1.fs
include ewlib/leap_year_q.fs

\ --- uptime
2variable uptime
: .uptime   ( -- )  uptime 2@  decimal ud. [char] s emit   ;
: ++uptime  ( -- )  1.  uptime 2@  d+  uptime 2! ;

\ --- timeup jobs ---------------------------
: job.tick  ;
: job.sec
  ++uptime
;
: job.min
  \ update display?
;
: job.hour
  \ hit the gong?
;
: job.day   ;
: job.month
  year @  month @ 1+  tu.upd.limits
;
: job.year  ;

create Jobs
  ' job.tick ,
  ' job.sec , ' job.min ,   ' job.hour ,
  ' job.day , ' job.month , ' job.year ,

variable jobCount
: jobCount++
  jobCount @
  #6 < if
    1 jobCount +!
  then
;

variable ticks
: init
  ...
  0  ticks    !
  #6 jobCount !
  0. uptime  2!
  timeup.init
  +ticks
;

: sec.over? ( -- t/f)  ticks @ 1+  ticks/sec > ;

: run-loop
  init
  begin
    tick.over? if
      tick.over!                     \ acknowledge
      1 ticks +!                     \ increment ticks
      job.tick                       \ do something
    then

    sec.over? if
      ticks @ ticks/sec - ticks !    \ reduce ticks
      timeup                         \ advance clock counters
      1 jobCount !                   \ start jobs
    then


    jobCount @ bv tu.flags fset? if  \ run one job per loop
      jobCount @
      dup Jobs + @i execute
      bv tu.flags fclr
    then
    jobCount++

  again
;

This code is and looks very old, to my eyes it could use a little refresh, /me thinks. On the other hand, it works :-)

The Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
\ 2015-10-11 ewlib/timeup_v0.0.fs
\
\ Written in 2015 by Erich Wälde <erich.waelde@forth-ev.de>
\
\ To the extent possible under law, the author(s) have dedicated
\ all copyright and related and neighboring rights to this software
\ to the public domain worldwide. This software is distributed
\ without any warranty.
\
\ You should have received a copy of the CC0 Public Domain
\ Dedication along with this software. If not, see
\ <http://creativecommons.org/publicdomain/zero/1.0/>.
\
\
\ variables
\     tu.counts -- fields available as:
\     tick sec min hour day month year
\ words:
\     timeup.init
\     timeup
\     lastday_of_month ( year month -- last_day )
\     tu.get  ( -- S M H d m Y )
\     tu.set  ( Y m d H M S -- )
\     tu.show ( -- )

#include leap_year_q.fs

variable tu.flags

variable tu.counts     #7 cells allot
tu.counts            constant tick
tu.counts #1 cells + constant sec
tu.counts #2 cells + constant min
tu.counts #3 cells + constant hour
tu.counts #4 cells + constant day
tu.counts #5 cells + constant month
tu.counts #6 cells + constant year

variable tu.limits     #6       allot

create tu.lastday_of_month
   #31 , #28 , #31 , #30 , #31 , #30 ,
   #31 , #31 , #30 , #31 , #30 , #31 ,

: lastday_of_month ( year month -- last_day )
  dup 1-                \ array starts at 0
  tu.lastday_of_month + @i
  swap #2 = if          \ if month == 2
    swap leap_year? if  \   if leap_year
      1+                \     month += 1
    then
  else                  \ else
    swap drop           \   remove year
  then
;

: timeup.init
  0      tu.flags !
  tu.counts #8 erase
  #60    tu.limits 1 + c!
  #60    tu.limits 2 + c!
  #24    tu.limits 3 + c!
  #31    tu.limits 4 + c! \ fixme: may be wrong later!
  #12    tu.limits 5 + c!
;

: timeup ( -- )
  $02 tu.flags fset                     \ secflag++
  1 sec +!                              \ sec++

  \ for ( sec ) min hour day month year
  #6 1 do
    i cells tu.counts + @   1+          \ Counts[i]+1
    i       tu.limits + c@              \ Limits[i]
    > if                                \ if C[i]+1 > L[i]
      0  i cells tu.counts +  !         \ . C[i]=0
      i 1+ bv tu.flags fset             \ . F[i+1]++
      1 i 1+ cells tu.counts + +!       \ . C[i+1]++
    then                                \ fi
  loop
;

\ update lastday_of_month in tu.limits
\ once current date is known
: tu.upd.limits ( Y m -- )
  ( Y m ) lastday_of_month  tu.limits #4 + c!
;