\documentclass[11pt,a4paper]{article}

% 2006-07-22 EW Adventures-2.tex
% 2006-08-14 EW Adventures-2a.tex
%
% Aufgebessert nachdem ich 'ne Reihe Optimierungen gemacht hatte
% Und dann nochmal komplett umgeräumt!
%

% language support
\usepackage[german]{babel}

%\usepackage[utf8]{inputenc} % can use Umlauts now "u instead of "u
%\usepackage{lmodern} % better fonts
%\usepackage{pslatex} % use native PostScript fonts

\usepackage{url} % \url{} \path{} with correct "hyphenation"
%\usepackage{fancyvrb} % for code examples

% \voffset-10mm
% \pagestyle{empty}
% \pagestyle{plain}
% \pagestyle{headings}
% \renewcommand{\baselinestretch}{1.08}
%\usepackage{xspace}
\parindent=0pt
%\newcommand{\Forth}{\textsc{forth}\xspace}
\begin{document}

\title{Adventures in Forth 2}

\author{Erich W"alde}

\maketitle

\begin{multicols}{2}

Im ersten Teil (\ref{Adv1}) wurde gezeigt, wie man dem Renesas--R8C13--Kontroller
beibringt, mit einem i2c--Bus zu reden. Inzwischen hat Bernd Paysan das
r8c--Forth und das zugeh"orige \texttt{terminal.fs} so aufgebohrt, dass man
in einem Forth--Programm die Anweisung \texttt{include} verwenden kann.
Daher kann man ein l"angeres Programm in mehrere Dateien zerlegen. Man kann
die Dateien auch so schreiben, dass sie von mehreren Projekten benutzt
werden k"onnen, eine angenehme Erleichterung.

Ich m"ochte mit dem R8C--Kontroller eine Me"sstation aufbauen, die permanent
laufen soll. Um damit zeitliche Abl"aufe zu organisieren, w"are eine Art Uhr
nicht schlecht. Dann kann ich einfacher sagen, \textit{alle 10 Minuten
  sollen die Messwerte verschickt werden}.

\section{Eine Forth Uhr}

Um das Verstreichen der Zeit zu messen, bedient man sich am besten
der Timer, die der Kontroller mitbringt. Man konfiguriert einen Timer so, dass er immer
nach einer (oder wenigen) Millisekunden einen Interrupt ausl"ost. Die
dazugeh"orige Interrupt--Service--Routine z"ahlt einen Z"ahler hoch und setzt
einen \textit{flag}, der dem "ubrigen Programm anzeigt, dass eine
Millisekunde vergangen ist. Jetzt kann man sich im Quelltext von
gforth--r8c schlau machen, wie man einem timer eine Interrupt--Service--Routine 
unterjubelt. Allerdings habe ich das als Anf"anger nicht gen"ugend
durchschaut. Es geht auch ohne Durchblick.


Bei ebendieser Suche (\path{arch/r8c/prim.fs}) findet sich eine Variable
\texttt{timer}, die vom Wort \texttt{ms-irq} hochgez"ahlt wird. Es gibt
also bereits eine Art Uhren-Tick (TimerC--Zyklus), den ich f"ur die Uhr
verwenden kann.

In einem Buch "uber PIC--Mikrokontroller (\ref{Koenig}) findet sich eine
geeignete Methode, um eine Zeitz"ahlung zu realisieren. Das Programm l"auft
durch eine Schleife, in der immer wieder "uberpr"uft wird, ob ein Tick
abgelaufen ist. Wenn ja, dann werden die Z"ahler, die die Zeit verwalten,
hochgez"ahlt (Ticks oder Millisekunden, Sekunden, Minuten, Stunden \ldots).
Wenn eine ganze Sekunde verstrichen ist, dann wird der Sekundenz"ahler
ebenfalls erh"oht, und ein flag gesetzt, welcher ebendies anzeigt.
Diese Routine hei"st in dem o.g.\ Buch \texttt{timeup}\footnote{Ich hab's
  grad wieder nachgelesen: TIMUP hei"st sie dort.}. Wenn \texttt{timeup}
zur"uckkehrt, sind alle Z"ahler aktualisiert, und die flags gesetzt.

Im darauf folgenden Teil der Schleife werden die flags "uberpr"uft, die
dazugeh"origen Aufgaben (\textit{jobs)} aufgerufen, und danach die flags
gel"oscht. Wenn mehrere flags gesetzt sind, kann man entweder alle
zugeh"origen Aufgaben ausf"uhren und erst danach die Schleife von vorne
beginnen (\textit{serielle Schleife}\footnote{Aus der Struktur des
  zugeh"origen Ablaufdiagramms.}) --- oder man kehrt in die Schleife nach
jeder Aufgabe zur"uck (\textit{parallele} Form). Das kann von Vorteil sein,
wenn man eine Aufgabe hat, die alle $n$ Millisekunden (Tick) auszuf"uhren
ist --- z.B. die Bedienung einer LED--Ziffernanzeige. Dann kann man zwischen
den einzelnen Jobs immer auf den n"achsten Tick warten und diese spezielle
Aufgabe einschieben.

Der Nachteil, dass beispielsweise eine Stundenaufgabe erst 3 Ticks nach der
vollen Stunde aufgerufen wird, ist dagegen klein. Der \textit{Pseudocode}
f"ur die serielle Schleife sieht dann etwa so aus:

\begin{verbatim}
while (true)
{   if (tickover?)
    {   timeup aufrufen
    }
    if ( Tick-flag? )
    {   Tick-Aufgabe aufrufen
        Tick-flag loeschen
    }
    if ( Sekunden-flag? )
    {   Sekunden-Aufgabe aufrufen
        Sekunden-flag loeschen
    }
    if ( Minuten-flag? )
    ...
}
\end{verbatim}

Es gilt also, die Funktion \texttt{timeup} nachzubilden und die Schleife
zu schreiben.

\section{Zeit messen: \texttt{timeup}}

Das folgende Verfahren hat sich f"ur das Innenleben von \texttt{timeup}
bew"ahrt: zuerst lesen wir \texttt{timer} aus, z"ahlen einen bestimmten
Wert (z.B.\ 500) dazu und speichern das Ergebnis in \texttt{newtimer}. Wenn
\texttt{timer} den Wert von \texttt{newtimer} erreicht oder "uberschritten
hat, dann sind $500$\,ms verstrichen. Dann rufen wir \texttt{timeup} auf,
um die Z"ahler zu aktualisieren. Anschlie"send wird zu \texttt{newtimer}
wieder $500$ addiert. Sollte \texttt{timeup} etwas zu sp"at aufgerufen
werden, macht das nichts, weil sich diese Fehler nicht addieren. Wir
z"ahlen immer die gleiche Zahl zu \texttt{newtimer}. Anders w"are es, wenn
wir den Z"ahler f"ur den Timer l"oschen und den Timer dann neu starten.

Wir schreiben also erst mal eine abgespeckte Version von \texttt{timeup}.
TimerC gibt Zyklen vor, die in der Variablen \texttt{timer} gespeichert
werden. Im Beispiel ist nach $500$ Zyklen ein Tick vergangen, nach $2$
Ticks eine Sekunde. \texttt{timeup} wird aufgerufen, wenn ein Tick
verstrichen ist. Die niedrigste Stufe von \texttt{timeup} z"ahlt diese
Ticks zu Sekunden. Mit Zyklen und Ticks lassen sich auch jobs realisieren,
die mehrfach pro Sekunde laufen m"ussen.

\begin{verbatim}
Variable newtimer
Variable lastsec
Variable tickflag
Variable tick
Variable secflag
Variable sec
Variable minflag
Variable min
500 Constant cycles.tick \ timerC Zykl./Tick
2   Constant ticks.sec   \ Ticks/Sekunde

: tickover? ( -- ) newtimer @ timer @ - 0< ;
: timeup ( -- )
  cycles.tick newtimer +!
  1 tickflag !                  \ tick over!
  1 tick +!
  tick @ ticks.sec >= IF
    0 tick !
    1 secflag !                 \ sec over!
    1 sec +!
  ENDIF
  sec @ 60 >= IF
    0 sec !
    1 minflag !                 \ min over!
    1 min +!
  ENDIF
  min @ 60 >= IF
    0 min !
  ENDIF
;
\end{verbatim}


\section{Was schaffen: \texttt{run}}

Die Hauptschleife ist auch keine Zauberei:

\begin{verbatim}
: run
  init-loop
  BEGIN
    tickover?  IF timeup ENDIF
    
    tickflag @ IF job.tick 0 tickflag ! ENDIF
    secflag  @ IF job.sec  0 secflag !  ENDIF
    minflag  @ IF job.min  0 minflag !  ENDIF
    
  key? UNTIL
;
\end{verbatim}

Zu Beginn der Schleife wird lediglich \texttt{newtimer} initialisiert.
Bleiben nur die Jobs, damit auch was passiert:

\begin{verbatim}
: led0 ( -- ) 3 port1 bclr ;
: led1 ( -- ) 3 port1 bset ;
: job.tick
  tick @ $01 and IF led0 ELSE led1 ENDIF
;
: job.sec
  timer @ dup lastsec @ - swap dup lastsec !
  sec @ min @ 
  cr . . . .
;
: job.min
  cr ." running minute job ..."
;
: init-loop ( -- )
  cr ." min sec timer timer-lastsec"
  timer @ cycles.tick + newtimer !
;
\end{verbatim}

Der zu einem abgelaufenen Tick geh"orige \texttt{job.tick}
l"asst hier die LED an Pin P1.3 im Sekundentakt blinken. Der Sekundenjob
gibt die aktuellen Z"ahler aus, sowie den Abstand seit dem letzten
Sekundenjob in TimerC--Zyklen.

\begin{verbatim}
include adv2_1.fs  ok
58 sec !  ok
run 
min sec timer timer-lastsec
0 59 -26991 1000 
1 0 -25991 1000 
running minute job ...
1 1 -24991 1000 
...  
\end{verbatim}

Die Z"ahler werden hochgez"ahlt, ganz so, wie man sich das vorstellt. Diese
Schleife l"asst sich schon mal mit der Stopuhr testen. Man beachte auch,
dass die Hauptschleife recht simpel aussieht. Die einzelnen Worte sind auch
"uberschaubar. 

\section{Optimierungen 1}

Ein komplettes Wort, also 16 Bit, zu belegen, wo auch ein einziges Bit
ausreicht, das ist Verschwendung. Unsere flags finden in einer Variablen
bequem Platz. Daf"ur m"ussen wir uns merken, welches flag in welcher
Bitposition wohnt:


\begin{verbatim}
Variable Flags
2 Flags bset      \ Setze   Flag[Minute]
1 Flags bclr      \ Loesche Flag[Sekunde]
0 Flags btst IF   \ Flag[Tick] gesetzt?
  timeup
ENDIF
\end{verbatim}

Gespart werden hier 6 Worte im RAM: die Variablen \texttt{tickflag secflag
  .. yearflag} werden alle in \texttt{Flags} untergebracht.

\section{Uhr und Kalender}

\texttt{timeup} wird zwar l"anglich, wenn man die Behandlung von Stunden,
Tagen, Monaten und Jahren dazupackt, aber nicht komplexer. \texttt{timeup}
erh"alt im n"achsten Schritt die restlichen Z"ahler. Sofort braucht man ein
Wort, welches die L"ange eines bestimmten Monats ausgibt, inklusive
Behandlung der Schaltjahre. Ein Schaltjahr ist ein Jahr, welches durch $4$
aber nicht durch $100$ teilbar ist, oder welches durch $400$ teilbar ist. In
Stephen Pelcs Buch \textit{Programming Forth}  (\ref{SPelc}) findet
sich diese Anweisung in sehr sch"oner, kompakter Form:

\begin{verbatim}
: leap_year ( year -- t/f )
  dup    4 mod 0=
  over 100 mod 0<> and
  swap 400 mod 0=  or ;
\end{verbatim}

F"ur die Monate habe ich eine Tabelle \texttt{MaxDay} als \textit{byte
  array} angelegt, weil alle Werte unter $255$ liegen. Der gesuchte Monat
wird um $1$ vermindert und dient dann als Index in dieses array. Dazu kommt
die korrekte Behandlung des Februars in Schaltjahren.


\begin{verbatim}
ram
create MaxDay  31 c, 28 c, 31 c, 30 c, 31 c,
   30 c, 31 c, 31 c, 30 c, 31 c, 30 c, 31 c,
rom
: length_of_month ( year month -- maxday )
  dup 1-                \ array starts at 0
  MaxDay + c@           \ get length
  swap 2 = IF           \ if month == 2
    swap leap_year IF   \ and leap_year
      1+                \   length += 1
    ENDIF
  ELSE
    swap drop           \ remove year
  ENDIF ;
\end{verbatim}

Diese Worte lassen sich testen --- wie immer in
Forth direkt von der Eingabezeile:

\begin{verbatim}
1900 leap_year . 0  ok
2000 leap_year . -1  ok
2001 leap_year . 0  ok
2004 leap_year . -1  ok
2004 2 length_of_month . 29  ok
2006 2 length_of_month . 28  ok
2006 3 length_of_month . 31  ok
2006 4 length_of_month . 30  ok
...
\end{verbatim}

Bei \texttt{timeup} muss man noch darauf achten, dass die Z"ahler f"ur
Sekunden, Minuten, Stunden gegen den ersten ung"ultigen Wert (60, 60, 24)
mit \verb,>=, getestet werden. Bei der L"ange des Monats produziert
\texttt{length\_of\_month} aber den h"ochsten g"ultigen Wert, also ist der
Vergleich ein \verb,>, und der Startwert $1$ und nicht $0$. Analog f"ur den
\texttt{month} Z"ahler:

\begin{verbatim}
: timeup ( -- )
...
  day @ year @ month @ length_of_month > IF
    1 day !                    \ offset 1!
    5 Flags bset               \ month over
    1 month +!
  ENDIF
  month @ 12 > IF
    1 month !                  \ offset 1!
    6 Flags bset               \ year over
    1 year +!
  ENDIF
;
\end{verbatim}

Der komplette Programmtext findet sich in \path{adv2_2.fs} und produziert
etwa folgende Ausgabe: 

\begin{verbatim}
include adv2_2.fs  ok
23 hour ! 59 min ! 57 sec ! 0 tick !  ok
2006 year ! 12 month ! 31 day !  ok
run 
year month day hour min sec timer...
2006 12 31 23 59 57 7812 7812 
2006 12 31 23 59 58 8813 1001 
2006 12 31 23 59 59 9813 1000 
2007 1 1 0 0 0 10813 1000 
running minute job ...
running hour job ...
running day job ...
running month job ...
running year job ...
2007 1 1 0 0 1 11813 1000 
2007 1 1 0 0 2 12813 1000 
2007 1 1 0 0 3 13813 1000
...
\end{verbatim}

\section{Optimierungen 2}

Der Block in \texttt{run}, welcher die Flags pr"uft und die dazugeh"origen
Jobs aufruft, ist ein Kandidat f"ur eine Schleife.

\begin{verbatim}
: run
  ...
  BEGIN
   tickover? IF timeup ENDIF
   0 Flags btst IF job.tick 0 Flags bclr ELSE
   1 Flags btst IF job.sec  1 Flags bclr ELSE
   2 Flags btst IF job.min  2 Flags bclr ELSE
   3 Flags btst IF job.hour 3 Flags bclr ELSE
   4 Flags btst IF job.day  4 Flags bclr ELSE
  5 Flags btst IF job.month 5 Flags bclr ELSE
   6 Flags btst IF job.year 6 Flags bclr 
   ENDIF ENDIF ENDIF ENDIF ENDIF ENDIF ENDIF
  key? UNTIL
;
\end{verbatim}

F"ur eine Schleife ben"otigt man lediglich eine M"oglichkeit, die Jobs
"uber den Schleifenindex aufzurufen. Dazu erzeugt man eine Tabelle
\texttt{Jobs}, in der die Einsprungadressen der einzelnen
\texttt{job}--Worte aufgehoben werden. Das Wort \verb,', produziert diese
Adresse f"ur das nachfolgende Wort:

\begin{verbatim}
' job.tick . 8842  ok
' job.sec  . 8880  ok
' job.min  . 8958  ok
\end{verbatim}

liegt eine solche Adresse auf dem Stack, dann kann diese Adresse mit dem
Wort \texttt{execute} ausgef"uhrt werden:

\begin{verbatim}
job.sec 
2007 1 1 0 0 4 1581 -2641  ok
' job.sec execute 
2007 1 1 0 0 4 10299 8718  ok
\end{verbatim}

Die \texttt{Jobs} Liste und die Schleife sehen dann so aus:

\begin{verbatim}
ram
create Jobs ' job.tick , 
  ' job.sec , ' job.min , ' job.hour ,
  ' job.day , ' job.month , ' job.year ,
rom
: run
  init-loop
  BEGIN
    tickover? IF timeup ENDIF

    7 0 DO
      I Flags btst IF
      I cells Jobs + @ execute
      I Flags bclr
      ENDIF
    LOOP

  key? UNTIL
;
\end{verbatim}



Dieser Programmtext ist etwas k"urzer als der urspr"ungliche Block. Wenn
man wissen will, wie gro"s ein Programm ist, dann macht man mit
\texttt{dump} einen Speicherabzug. Das Programm wohnt im Datenflash,
welcher bei der Adresse \texttt{\$2000} beginnt und $4$\,kByte
(\texttt{\$1000}) gro"s ist. Der freie Speicher enth"alt lediglich
\texttt{FF} Werte. Im folgenden Beispiel sind die Zeilen f"ur den Druck
umgebrochen: 
% adv2_2.fs: 1318 Byte
% adv2_3.fs: 1208 Byte!

\begin{verbatim}
empty  ok
include adv2_2.fs  ok
$2000 $1000 dump 
    2000: D4 FD E5 46  6C 61 67 73 - 
          68 C0 FF FF  6A 04 00 20  
          ...Flagsh...j.. 
    2010: E8 6E 65 77  74 69 6D 65 - 
          72 20 68 C0  FF FF 6C 04  
          .newtimer h...l.
...
    2510: 5C C1 1E 25  D8 23 24 C5 - 
          06 00 08 20  FC C9 36 DC  
          \..%.#$.... ..6.
    2520: 5C C1 7C 24  12 C1 FF FF - 
          FF FF FF FF  FF FF FF FF  
          \.|$............
$2526 $2000 - . 1318  ok
\end{verbatim}

\path{adv2_2.fs} belegt also $1318$\,Byte. Das Programm mit der ge"anderten
Hauptschleife (\path{adv2_3.fs}) belegt $1208$\,Byte. Damit hat man
immerhin $110$\,Byte oder $8$\,\% eingespart. Ich finde, das ist eine ganze
Menge, auch wenn das durch $14$ Byte mehr belegtes RAM bezahlt wird.


\section{Uhrzeit auf's LCDisplay}

Sch"on w"ar's jetzt, wenn die Uhrzeit auf dem Display erscheint und nicht
(nur) im Terminal--Fenster. Dazu
verwenden wir ein paar einfache Worte, die die Angaben formatieren und
anzeigen. Das genaue Format ist nat"urlich Geschmacksache. Die daf"ur
notwendigen Worte haben wir uns aus dem ersten Artikel ausgeliehen und
stehen in den Listings (\path{adv2_lcd.fs})

\texttt{show.DT} schreibt Datum und Uhrzeit in meinem bevorzugten Format
auf das LCDisplay. Ich habe entschieden, \texttt{show.DT} nur jede Minute
aufzurufen, und jede Sekunde lediglich die Sekundenanzeige zu
aktualisieren:

\begin{verbatim}
: job.sec ( -- )
  ...
  0 13 lcdpos sec @ p2! lcdtype 
;
: job.min ( -- )
  ...
  0 0 lcdpos show.DT
;
\end{verbatim}

Au"serdem wird \texttt{show.DT} vor der Schleife einmal aufgerufen. Die
Anzeige funktioniert mit diesen Erg"anzungen wunderbar. Allerdings muss nach
dem Einschalten des Kontrollers die Zeit erst gestellt werden. Das ist
nichts f"ur faule Kerle wie meinereiner. Also habe ich die i2c--Bus
Funktionen vom letzten Mal hergenommen und eine batteriegepufferte PCF8583
Uhr an den i2c--Bus angeschlossen.

\section{Wider das Vergessen: i2c--Uhr}

Die Uhr anzusprechen, ist keine gro"se Kunst mehr, hier etwa um die Zeit zu
lesen. Der einzige Trick hier ist der, dass die Uhr selbst keinen
Jahresz"ahler hat. Lediglich 4 Jahre werden gez"ahlt, um die (meisten)
Schaltjahre zu behandeln. Daher habe ich an Adresse 0x10 im EEPROM der Uhr
einen 2--Byte--Z"ahler (nicht BCD, low Byte zuerst) f"ur das ganze Jahr
verwendet.

\begin{verbatim}
: get.rtc
  $01                   \ start address to rtc
  1 i2c_addr_rtc NB>i2c \ send start address
  6 i2c_addr_rtc NB<i2c \ read 6 Bytes
  $10                   \ YEAR address
  1 i2c_addr_rtc NB>i2c \ start address to rtc
  2 i2c_addr_rtc NB<i2c \ read 2 Bytes: xl xh
  8 lshift +            \ convert to YYYY
;
\end{verbatim}

Allerdings handelt man sich bei diesem Modell Zahlen im BCD--Format ein
(\textit{binary coded decimal}), d.h.\ die obere und die untere H"alfte
eines Bytes (\textit{nibbles} enthalten je eine Ziffer von 0 bis 9. Die
Konvertierung k"onnte evtl.\ in Assembler gemacht werden, wenn der
Kontroller einen entsprechenden Befehl beinhaltet. Im gforth--r8c hab ich
aber kein Wort gefunden. Also basteln wir uns das geschwind selber.

\begin{verbatim}
: bcd>dec ( n.bcd -- n.dec )
  $ff and
  dup 
  4 rshift 10 *   \ extract high nibble as 10s
  swap
  $0f and         \ extract low nibble as 1s
  +               \ add
;
: dec>bcd ( n.dec -- n.bcd )
  &100 mod        \ 99 is largest number for
  &10 /mod        \    8 bit bcd 
  4 lshift
  +
  $ff and         \ truncate to 8 bit
;
\end{verbatim}

Meine L"osungen sind ganz sicher verbesserungsf"ahig, aber f"ur diesen
Zweck ausreichend. 

\begin{verbatim}
include adv2_4.fs  ok
2006 8 15 18 35 55 0 set.rtc  ok
run 
year month day hour min sec timertimer-lastsec
2006 8 15 18 35 56 25877 -25261 
2006 8 15 18 35 57 26353 476 
2006 8 15 18 35 58 27353 1000 
2006 8 15 18 35 59 28353 1000 
2006 8 15 18 36 0 29353 1000 
running minute job ...
2006 8 15 18 36 1 30353 1000 
2006 8 15 18 36 2 31353 1000 
...
\end{verbatim}

Die Uhrzeit wird zun"achst an die i2c--Uhr "ubertragen, die letzte Zahl vor
\texttt{set.rtc} sind die Hundertstelsekunden. So ausger"ustet "uberlebt
die Uhr jetzt auch das Ausschalten des Kontrollerboards. Ein
$0.47$\,F--GoldCap Kondensator kann meine Uhr l"anger als 2 Wochen
betreiben. Als Zugabe enth"alt das Programm noch das LM75--Thermometer von
der ersten Folge. Der vollst"andige Programmtext findet sich in
\path{adv2_4.fs} und den darin eingeschlossenen Dateien.

\section{Optimierungen 3}

In der Funktion, "ah, im Wort \texttt{timeup} sind f"ur jeden Z"ahler die
gleichen Dinge zu erledigen: Z"ahler erh"ohen, falls Grenzwert erreicht:
Z"ahler zur"ucksetzen und den n"achsth"oheren Z"ahler erh"ohen, sowie den
flag f"ur den n"achsth"oheren Z"ahler. Um diese Funktion vollst"andig in
eine Schleife umzuwandeln, sind allerhand Vorarbeiten n"otig.

Die Z"ahler--Variablen \texttt{tick} bis \texttt{year} werden in einen
Datenblock mit dem Namen \texttt{Counts} gesteckt. Aus \verb,year @, wird
dann \verb,6 cells Counts + @, und analog f"ur alle "ubrigen Z"ahler.

Ebenso werden die Grenzwerte in ein Bytearray \texttt{Limits} verwandelt.
Das klappt zun"achst f"ur alle au"ser dem Monat. Aus \verb,60 >= IF,
wird dann eben \verb,2 Limits + c@ >= IF,.

Um den Block f"ur den Monat ebenfalls in die Schleife aufzunehmen, habe ich
entschieden, dass ich daf"ur sorge, dass im Feld $4$ von \texttt{Limits}
immer die L"ange des aktuellen Monats steht. Also muss vor Beginn der
Schleife, wenn die Uhrzeit aus der i2c--Uhr gelesen wurde, die L"ange des
aktuellen Monats nach \texttt{Limits} kopiert werden:

\begin{verbatim}
year @ month c@ length_of_month 4 Limits + c!
\end{verbatim}

Au"serdem muss \texttt{job.month} den Eintrag f"ur den neuen Monat
aktualisieren. Damit ist unter normalen Umst"anden die korrekte Behandlung
des Monats gesichert.

Verbleibt noch der \textit{Offset 1} Fehler in Tag und Monat. F"ur beide
Z"ahler habe ich den Startwert auf $0$ gesetzt. Damit hat etwa der Januar
Tage von $0$ bis $30$. Die $31$ in \texttt{Limits} ist jetzt der erste
ung"ultige Wert und der Vergleich im entsprechenden Block von
\texttt{timeup} wird von \verb,>, zu \verb,>=,, wie bei den anderen Blocks.
An allen Stellen, an denen Tag und Monat benutzt werden, muss jetzt $1$
dazugez"ahlt oder abgezogen werden. Das ist un"ubersichtlich, aber es
funktioniert. Die "Anderungen betreffen auch das Lesen der i2c--Uhr und die
Anzeige auf dem LCDisplay.

Nach diesen Ver"anderungen sehen jetzt alle Blocks gleich
aus, bis auf den Index. Jetzt kann man \texttt{timeup} tats"achlich enorm
reduzieren: 

\begin{verbatim}
: timeup ( -- )
  cycles.tick newtimer +!
  0 Flags bset                 \ tickflag++
  1 Counts +!                  \ tick++
  \ for i in tick sec min hour day month
  6 0 DO
    I cells Counts +  @
    I       Limits + c@ 
    >= IF                 \ if C[i] >= L[i]
      0 I cells Counts + !     \  C[i]=0;
      I 1+ Flags bset          \  F[i+1]++;
      1 I 1+ cells Counts + +! \  C[i+1]++;
    ENDIF                      \ endif
  LOOP
;
\end{verbatim}

Das Programm \path{adv2_4.fs} belegt $2708$ Bytes, das neue Programm
\path{adv2_5.fs} $2686$ Bytes. Wenn man die Worte \texttt{tick} bis
\texttt{year} konsequent durch \verb,n cells Counts +, ersetzt, dann spart
man erstaunlicherweise nochmal $2686-2652=34$ Bytes. Danach ist das
Programm aber deutlich unleserlich geworden.

Fazit: Das Programm wird nicht deutlich kleiner, wenn man \texttt{timeup}
in eine Schleife umwandelt, weil die n"otige Infrastruktur die Ersparnis
auffrisst.



\section{Uhr als Task}

Jetzt bleibt nur noch, die Uhr als Hintergrunds--\textit{Task} laufen zu
lassen, so wie das in den Beispielen auf der Wiki--Seite schon geschieht.
Dazu habe ich bei Rafael Deliano (\ref{Deliano}) eine Erkl"arung gefunden,
wie ein \textit{PAUSE--Tasker} generell funktioniert. An der Stelle, an der
ein Task unterbrechbar ist, ruft er die Funktion \texttt{pause} auf. Dann
l"auft der n"achste Task. Der hier verwendete Tasker kann genau zwei Tasks
verwalten. In den Beispielen ist der zweite Task eine Variante von
\texttt{key}, der unsere Eingaben am Terminal bedient. Man kann also mit
dem System reden, obwohl die Uhr l"auft.

Die Umwandlung von \texttt{run} in ein Task ist fast trivial: \texttt{run}
wird als \texttt{task} gekennzeichnet, die \texttt{BEGIN ... UNTIL}
Schleife wird in eine \texttt{BEGIN ... AGAIN} Schleife umgewandelt, und
vor dem Ende der Schleife wird \texttt{pause} aufgerufen. Dort darf
\texttt{run} unterbrochen werden. Ausgehend von \path{adv2_4.fs} muss man
nur \texttt{run} ersetzen und den tasker bekannt machen:

\begin{verbatim}
include adv2_tasker.fs
...
: run task
  init-loop
  BEGIN
    tickover? IF timeup ENDIF

    7 0 DO
      I Flags btst IF
      I cells Jobs + @ execute
      I Flags bclr
      ENDIF
    LOOP
    
    pause
  AGAIN
;
\end{verbatim}

Wenn man jetzt noch daf"ur sorgen will, dass die Show sofort nach dem Einschalten
losgeht, dann helfen die folgenden Zeilen:

\begin{verbatim}
include adv2_6.fs
rom
' run IS bootmessge
ram
savesystem
\end{verbatim}

% include adv2_6.fs  -> 2850 Byte
% nach savesystem    -> 3094 Byte + 244 Byte

Die Adresse des ausf"uhrbaren Teils von \texttt{run} wird
\texttt{bootmessage} untergeschoben (\texttt{is}). \texttt{savesystem}
rettet alle Variableninhalte aus dem RAM ins ROM und hinterlegt weiteren
Code, der beim Start die Inhalte wieder ins RAM transferiert, bevor das
Anwenderprogramm startet. Sehr feine Sache das. Nachdem \path{adv2_6.fs}
geladen und gesichert wurde, belegt es $3094$\,Byte. Allerdings kann man
die Ausgaben am Terminal noch sparen, wenn die Uhr alleine laufen soll. 


Fast "uberfl"ussig zu erw"ahnen \ldots mit den \texttt{job}-Worten kann ich
jetzt sehr bequem regelm"a"sige Aufgaben verteilen. Die Anzeige der Sekunde
jede Sekunde, das L"oschen und Wiederbeschreiben des LCDisplays jede
Minute, das Ein-/Ausschalten der LED und die Anzeige der Temperatur alle
halbe Sekunde (slow tick) sind die derzeit eingebauten regelm"a"sigen
Aufgaben. Mit etwas Fantasie l"asst sich der Kontroller nun zum
Schalten zeitlich bestimmter Vorg"ange (Roll"aden, Licht, Kaffeemaschine)
oder sensorgesteuerter Dinge (Gew"achshausl"uftung, Dachfenster)
gebrauchen. Der Haussteuerung in Forth steht nichts mehr im Weg au"ser dem
nun doch etwas begrenzten Speicher von ROM \ldots

\section{Ausblick}

Der damit erreichte Stand der Dinge ist ganz ordentlich. Dennoch verbleiben
eine Menge Dinge f"ur einen weiteren Artikel --- falls ich die Leser nicht
schon "uber Geb"uhr strapaziert habe.

Der aufmerksame Leser hat l"angst erkannt, dass \texttt{job.year} die Kopie
von \texttt{year} im RAM der i2c--Uhr aktualisieren sollte. Die
M"oglichkeiten und Grenzen des gew"ahlten Verfahrens sollten ausgelotet
werden. Messwerte wollen verschickt werden, am besten "uber die andere
serielle Schnittstelle (uart0). Und wie kann man nun dem Forth--System eine
neue Interrupt Service Routine unterjubeln, etwa um die Flanken eines
DCF77--Empf"angers (Zeitsignal) zu detektieren und bedienen? Unbelegte Pins
f"ur Experimente sind noch gen"ugend vorhanden.

Als praktische Anwendung m"ochte ich einen Sensor aufbauen, der die
Sonnenscheindauer erfasst --- bitte ohne irgendwelche mechanisch bewegten
Teile. Das Auslesen von 4 Fotodioden an einem AD--Wandler "uber den
i2c--Bus funktioniert schon. Messungen am Sonnenlicht und das Entwickeln
der Datenauswertung fehlen noch.

Erg"anzungen, Kommentare, Korrekturen sind ausdr"ucklich erw"unscht. Sie
erreichen mich unter \url{ew.forth@nassur.net}

\section{Referenzen}

\begin{enumerate}
\item \label{Adv1} E. W"alde, Adventures in Forth, Die 4. Dimension 3/2006, Jahrgang 22
\item \label{Koenig} A.\ K"onig und M.\ K"onig, Das PICmicro Profi Buch, Franzis
  Verlag 1999, ISBN 3-7723-4284-1
\item \label{SPelc} Stephen Pelc, Programming Forth, \\ \url{http://www.mpeforth.com/arena/ProgramForth.pdf}
\item \label{Deliano} \url{http://www.embeddedforth.de/emb3.pdf} S.\ 9
\end{enumerate}
\end{multicols}

\section{Listings}

\begin{quote}
\begin{small}
\begin{multicols}{2}
\listinginput[1]{1}{2006-04/adv2_1.fs}         % abgespecktes timeup + Schleife
\listinginput[1]{1}{2006-04/adv2_2.fs}         % Jobs + Schleife
\listinginput[1]{1}{2006-04/adv2_timeup.fs}    % . vollständiges timeup
\listinginput[1]{1}{2006-04/adv2_3.fs}         % Jobs + vereinfachte Schleife
\listinginput[1]{1}{2006-04/adv2_4.fs}         % Uhr mit LCDisplay und i2c-rtc
\listinginput[1]{1}{2006-04/adv2_lcd.fs}       % . Formatierung fürs LCD
\listinginput[1]{1}{2006-04/adv2_i2c.fs}       % . i2c Bus
\listinginput[1]{1}{2006-04/adv2_i2c_rtc.fs}   % . i2c-rtc Uhr
\listinginput[1]{1}{2006-04/adv2_i2c_lm75.fs}  % . i2c-lm75 Thermometer
\listinginput[1]{1}{2006-04/adv2_5.fs}         % wie 4, aber mit timeup als Schleife
%\listinginput[1]{1}{2006-04/adv2_6.fs}         % wie 4, aber run als task
\listinginput[1]{1}{2006-04/adv2_tasker.fs}    % . tasker
\end{multicols}
\end{small}
\end{quote}

\end{document}
