diff --git a/20191128/dates.c b/20191128/dates.c
new file mode 100644
index 0000000000000000000000000000000000000000..97a49feb32b4dc3388facb89732464e68af2a30e
--- /dev/null
+++ b/20191128/dates.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include "dates.h"
+
+int is_leap_year (int year)
+{
+  if (year % 4 == 0)
+    if (year % 100 == 0)
+      if (year % 400 == 0)
+        return 1;
+      else
+        return 0;
+    else
+      return 1;
+  else
+    return 0;
+}
+
+int days_in_month (int month, int year)
+{
+  if (month == 2)
+    if (is_leap_year (year))
+      return 29;
+    else
+      return 28;
+  else if (month == 4 || month == 6 || month == 9 || month == 11)
+    return 30;
+  else
+    return 31;
+}
+
+void date_print (date *d)
+{
+  printf ("%02d.%02d.%04d", d->day, d->month, d->year);
+}
+
+int date_set (date *d, char day, char month, int year)
+{
+  d->year = year;
+  if (month > 0 && month <= 12)
+    d->month = month;
+  else
+    return 0;
+  if (day > 0 && day <= days_in_month (month, year))
+    d->day = day;
+  else
+    return 0;
+  return 1;
+}
+
+void date_next (date *d)
+{
+  d->day++;
+  if (d->day > days_in_month (d->month, d->year))
+    {
+      d->month++;
+      d->day = 1;
+      if (d->month > 12)
+        {
+          d->year++;
+          d->month = 1;
+        }
+    }
+}
diff --git a/20191128/dates.h b/20191128/dates.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f931a6fb73e2dc493a6f517227e5c37a75252cb
--- /dev/null
+++ b/20191128/dates.h
@@ -0,0 +1,10 @@
+typedef struct
+{
+  char day, month;
+  int year;
+}
+date;
+
+extern void date_print (date *d);
+extern int date_set (date *d, char day, char month, int year);
+extern void date_next (date *d);
diff --git a/20191128/hp-musterloesung-20191121.pdf b/20191128/hp-musterloesung-20191121.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..a8adbf2b369d7073ba44f6aa9791ac4bb8d4ac2d
Binary files /dev/null and b/20191128/hp-musterloesung-20191121.pdf differ
diff --git a/20191128/hp-musterloesung-20191121.tex b/20191128/hp-musterloesung-20191121.tex
new file mode 100644
index 0000000000000000000000000000000000000000..fcfee6bff213e6bb5404d0c0bfdae3918e7b4d70
--- /dev/null
+++ b/20191128/hp-musterloesung-20191121.tex
@@ -0,0 +1,278 @@
+% hp-musterloesung-20191128.pdf - Solutions to the Exercises on Low-Level Programming
+% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019  Peter Gerwinski
+%
+% This document is free software: you can redistribute it and/or
+% modify it either under the terms of the Creative Commons
+% Attribution-ShareAlike 3.0 License, or under the terms of the
+% GNU General Public License as published by the Free Software
+% Foundation, either version 3 of the License, or (at your option)
+% any later version.
+%
+% This document is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this document.  If not, see <http://www.gnu.org/licenses/>.
+%
+% You should have received a copy of the Creative Commons
+% Attribution-ShareAlike 3.0 Unported License along with this
+% document.  If not, see <http://creativecommons.org/licenses/>.
+
+% README: Datum-Bibliothek, Text-Grafik-Bibliothek, LED-Blinkmuster
+
+\documentclass[a4paper]{article}
+
+\usepackage{pgscript}
+\usepackage{sfmath}
+
+\begin{document}
+
+  \section*{Hardwarenahe Programmierung\\
+            Musterlösung zu den Übungsaufgaben -- 28.\ November 2019}
+
+  \exercise{Datum-Bibliothek}
+
+  Zerlegen Sie die Datum-Bibliothek aus der Übungsaufgabe 2 vom 7.\,11.\,2019
+  in eine \file{.c}- und eine \file{.h}-Datei. \points{4}
+
+  Hinweis: Schreiben Sie zusätzlich ein Test-Programm.
+
+  \solution
+
+  Die Dateien \gitfile{hp}{20191128}{dates.c} und \gitfile{hp}{20191128}{dates.h} enthalten die Bibliothek,
+  die Datei \gitfile{hp}{20191128}{test-dates.c} ein Programm zum Testen der Bibliothek.
+
+  \exercise{Text-Grafik-Bibliothek}
+
+  Schreiben Sie eine Bibliothek für "`Text-Grafik"' mit folgenden Funktionen:\vspace*{-\medskipamount}
+  \begin{itemize}
+    \item
+      \lstinline|void clear (char c)|\\
+      Bildschirm auf Zeichen \lstinline|c| löschen,\\
+      also komplett mit diesem Zeichen (z.\,B.: Leerzeichen) füllen
+    \item
+      \lstinline|void put_point (int x, int y, char c)|\\
+      Punkt setzen (z.\,B.\ einen Stern (\lstinline{*}) an die Stelle $(x,y)$ "`malen"')
+    \item
+      \lstinline|char get_point (int x, int y)|\\
+      Punkt lesen
+%    \item
+%      \lstinline|void fill (int x, int y, char c, char o)|\\
+%      Fläche in der "`Farbe"' \lstinline|o|,
+%      die den Punkt \lstinline|(x, y)| enthält,
+%      mit der "`Farbe"' \lstinline|c| ausmalen
+    \item
+      \lstinline|void display (void)|\\
+      das Gezeichnete auf dem Bildschirm ausgeben
+  \end{itemize}
+
+  Hinweise:\vspace*{-\medskipamount}
+  \begin{itemize}
+    \item
+      Eine C-Bibliothek besteht aus (mindestens)
+      einer \file{.h}-Datei und einer \file{.c}-Datei.
+    \item
+      Verwenden Sie ein Array als "`Bildschirm"'.
+
+      Vor dem Aufruf der Funktion \lstinline|display()| ist nichts zu sehen;\\
+      alle Grafikoperationen erfolgen auf dem Array.
+    \item
+      Verwenden Sie Präprozessor-Konstante,
+      z.\,B.\ \lstinline{WIDTH} und \lstinline{HEIGHT},\\
+      um Höhe und Breite des "`Bildschirms"' festzulegen:
+      \begin{lstlisting}[gobble=8]
+        #define WIDTH 72
+        #define HEIGHT 24
+      \end{lstlisting}
+    \item
+     Schreiben Sie zusätzlich ein Test-Programm,
+     das alle Funktionen der Bibliothek benutzt,\\
+     um ein hübsches Bild (z.\,B.\ ein stilisiertes Gesicht -- "`Smiley"')
+     auszugeben.
+  \end{itemize}
+  \points{8}
+
+  \solution
+
+  Siehe die Dateien \gitfile{hp}{20191128}{textgraph.c} und \gitfile{hp}{20191128}{textgraph.h} (Bibliothek)
+  sowie \gitfile{hp}{20191128}{test-textgraph.c} (Test-Programm).
+
+  Diese Lösung erfüllt zusätzlich die Aufgabe,
+  bei fehlerhafter Benutzung (Koordinaten außerhalb des Zeichenbereichs)
+  eine sinnvolle Fehlermeldung auszugeben,
+  anstatt unkontrolliert Speicher zu überschreiben und abzustürzen.
+
+  Das Schlüsselwort \lstinline{static}
+  bei der Deklaration der Funktion \lstinline{check_coordinates()}
+  bedeutet, daß diese Funktion nur lokal (d.\,h.\ innerhalb der Bibliothek)
+  verwendet und insbesondere nicht nach außen
+  (d.\,h.\ für die Benutzung durch das Hauptprogramm) exportiert wird.
+  Dies dient dazu, nicht unnötig Bezeichner zu reservieren
+  (Vermeidung von "`Namensraumverschmutzung"').
+
+  Man beachte die Verwendung einfacher Anführungszeichen (Apostrophe)
+  bei der Angabe von \lstinline{char}-Kon"-stanten (\lstinline{'*'})
+  im Gegensatz zur Verwendung doppelter Anführungszeichen
+  bei der Angabe von String-Konstanten
+  (String = Array von \lstinline{char}s, abgeschlossen mit Null-Symbol).
+  Um das einfache Anführungszeichen selbst als \lstinline{char}-Konstante anzugeben,
+  ist ein vorangestellter Backslash erforderlich: \lstinline{'\''} ("`Escape-Sequenz"').
+  Entsprechendes gilt für die Verwendung doppelter Anführungszeichen
+  innerhalb von String-Konstanten:
+  \lstinline{printf ("Your name is: \"%s\"", name);}
+
+  \exercise{LED-Blinkmuster}
+
+  Wir betrachten das folgende Programm für einen ATmega32-Mikro-Controller
+  (Datei: \gitfile{hp}{20191128}{aufgabe-3.c}).
+
+  \begin{minipage}[t]{7cm}
+    \begin{lstlisting}[gobble=6]
+      #include <stdint.h>
+      #include <avr/io.h>
+      #include <avr/interrupt.h>
+
+      uint8_t counter = 1;
+      uint8_t leds = 0;
+
+      ISR (TIMER0_COMP_vect)
+      {
+        if (counter == 0)
+          {
+            leds = (leds + 1) % 8;
+            PORTC = leds << 4;
+          }
+        counter++;
+      }
+    \end{lstlisting}
+  \end{minipage}\hfill\begin{minipage}[t]{8.5cm}
+    \begin{lstlisting}[gobble=6]
+      void init (void)
+      {
+        cli ();
+        TCCR0 = (1 << CS01) | (1 << CS00);
+        TIMSK = 1 << OCIE0;
+        sei ();
+        DDRC = 0x70;
+      }
+
+      int main (void)
+      {
+        init ();
+        while (1)
+          ; /* do nothing */
+        return 0;
+      }
+    \end{lstlisting}
+  \end{minipage}
+
+  An die Bits Nr.\ 4, 5 und 6 des Output-Ports C des Mikro-Controllers sind LEDs angeschlossen.\\
+  Sobald das Programm läuft, blinken diese in charakteristischer Weise:
+  \begin{quote}
+    \newcommand{\tdn}[1]{\raisebox{-2pt}{#1}}
+    \begin{tabular}{|c|c|c|c|}\hline
+      \tdn{Phase} & \tdn{LED oben (rot)} & \tdn{LED Mitte (gelb)} & \tdn{LED unten (grün)} \\[2pt]\hline
+      1 & aus & aus & an  \\\hline
+      2 & aus & an  & aus \\\hline
+      3 & aus & an  & an  \\\hline
+      4 & an  & aus & aus \\\hline
+      5 & an  & aus & an  \\\hline
+      6 & an  & an  & aus \\\hline
+      7 & an  & an  & an  \\\hline
+      8 & aus & aus & aus \\\hline
+    \end{tabular}
+  \end{quote}
+  Jede Phase dauert etwas länger als eine halbe Sekunde.
+  Nach 8 Phasen wiederholt sich das Schema.
+
+  Erklären Sie das Verhalten des Programms anhand des Quelltextes:
+  \vspace{-\medskipamount}
+  \begin{itemize}\itemsep0pt
+    \item[(a)]
+      Wieso macht das Programm überhaupt etwas,
+      wenn doch das Hauptprogramm nach dem Initialisieren lediglich eine Endlosschleife ausführt,
+      in der \emph{nichts} passiert?
+      \points{1}
+    \item[(b)]
+      Wieso wird die Zeile \lstinline|PORTC = leds << 4;| überhaupt aufgerufen,
+      wenn dies doch nur unter der Bedingung \lstinline|counter == 0| passiert,
+      wobei die Variable \lstinline|counter| auf 1 initialisiert,
+      fortwährend erhöht und nirgendwo zurückgesetzt wird?
+      \points{2}
+    \item[(c)]
+      Wie kommt das oben beschriebene Blinkmuster zustande?
+      \points{2}
+    \item[(d)]
+      Wieso dauert eine Phase ungefähr eine halbe Sekunde?
+      \points{2}
+    \item[(e)]
+      Was bedeutet "`\lstinline|ISR (TIMER0_COMP_vect)|"'?
+      \points{1}
+  \end{itemize}
+
+  \goodbreak
+  Hinweis:
+  \vspace{-\medskipamount}
+  \begin{itemize}\itemsep0pt
+    \item
+      Die Funktion \lstinline|init()| sorgt dafür, daß der Timer-Interrupt Nr.\ 0 des Mikro-Controllers
+      etwa 488mal pro Sekunde aufgerufen wird.
+      Außerdem initialisiert sie die benötigten Bits an Port C als Output-Ports.
+      Sie selbst brauchen die Funktion \lstinline|init()| nicht weiter zu erklären.
+  \end{itemize}
+
+  \solution
+
+  \begin{itemize}\itemsep0pt
+    \item[(a)]
+      \textbf{Wieso macht das Programm überhaupt etwas,
+      wenn doch das Hauptprogramm nach dem Initialisieren lediglich eine Endlosschleife ausführt,
+      in der \emph{nichts} passiert?}
+
+      Das Blinken wird durch einen Interrupt-Handler implementiert.
+      Dieser wird nicht durch das Hauptprogramm,
+      sondern durch ein Hardware-Ereignis (hier: Uhr) aufgerufen.
+
+    \item[(b)]
+      \textbf{Wieso wird die Zeile \lstinline|PORTC = leds << 4;| überhaupt aufgerufen,
+      wenn dies doch nur unter der Bedingung \lstinline|counter == 0| passiert,
+      wobei die Variable \lstinline|counter| auf 1 initialisiert,
+      fortwährend erhöht und nirgendwo zurückgesetzt wird?}
+
+      Die vorzeichenlose 8-Bit-Variable \lstinline{counter} kann nur
+      Werte von 0 bis 255 annehmen; bei einem weiteren
+      INkrementieren springt sie wieder auf 0 (Überlauf),
+      und die \lstinline{if}-Bedingung ist erfüllt.
+
+    \item[(c)]
+      \textbf{Wie kommt das oben beschriebene Blinkmuster zustande?}
+
+      In jedem Aufruf des Interrupt-Handlers wird die Variable
+      \lstinline{leds} um 1 erhöht und anschließend modulo 8 genommen.
+      Sie durchläuft daher immer wieder die Zahlen von 0 bis 7.
+
+      Durch die Schiebeoperation \lstinline{leds << 4} werden die 3 Bits 
+      der Variablen \lstinline{leds} an diejenigen Stellen im Byte
+      geschoben, an denen die LEDs an den Mikro-Controller
+      angeschlossen sind (Bits 4, 5 und 6).
+
+      Entsprechend durchläuft das Blinkmuster immer wieder
+      die Binärdarstellungen der Zahlen von 0 bis 7
+      (genauer: von 1 bis 7 und danach 0).
+
+    \item[(d)]
+      \textbf{Wieso dauert eine Phase ungefähr eine halbe Sekunde?}
+
+      Der Interrupt-Handler wird gemäß Hinweis 488mal pro Sekunde aufgerufen.
+      Bei jedem 256sten Aufruf ändert sich das LED-Muster.
+      Eine Phase dauert somit $\frac{256}{488} \approx 0.52$ Sekunden.
+
+    \item[(e)]
+      \textbf{Was bedeutet "`\lstinline|ISR (TIMER0_COMP_vect)|"'?}
+
+      Deklaration eines Interrupt-Handlers für den Timer-Interrupt Nr.\ 0
+  \end{itemize}
+
+\end{document}
diff --git a/20191128/test-dates.c b/20191128/test-dates.c
new file mode 100644
index 0000000000000000000000000000000000000000..5127c11be08532c3d54ba8e85a36abc5400a8566
--- /dev/null
+++ b/20191128/test-dates.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include "dates.h"
+
+void check (char day, char month, int year)
+{
+  date d;
+  if (date_set (&d, day, month, year))
+    {
+      date_print (&d);
+      printf (" --> ");
+      date_next (&d);
+      date_print (&d);
+      printf ("\n");
+    }
+  else
+    printf ("%02d.%02d.%04d: invalid date\n", day, month, year);
+}
+
+int main (void)
+{
+  check (6, 11, 2016);
+  check (29, 11, 2016);
+  check (30, 11, 2016);
+  check (31, 11, 2016);
+  check (29, 12, 2016);
+  check (30, 12, 2016);
+  check (31, 12, 2016);
+  check (28, 2, 2016);
+  check (29, 2, 2016);
+  check (30, 2, 2016);
+  check (28, 2, 2015);
+  check (29, 2, 2015);
+  check (30, 2, 2015);
+  check (31, 12, 2008);
+  check (28, 2, 2000);
+  check (29, 2, 2000);
+  check (30, 2, 2000);
+  check (28, 2, 1900);
+  check (29, 2, 1900);
+  check (30, 2, 1900);
+  return 0;
+}
diff --git a/20191128/test-textgraph.c b/20191128/test-textgraph.c
new file mode 100644
index 0000000000000000000000000000000000000000..4033f61ba55d794afbb5b6c03a0bb55dcc5d3e4e
--- /dev/null
+++ b/20191128/test-textgraph.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include "textgraph.h"
+
+int main (void)
+{
+  clear (' ');
+  put_point (-5, 10, 'X');
+  for (int i = 17; i < 23; i++)
+    put_point (i, 5, '*');
+  put_point (15, 6, '*');
+  put_point (14, 7, '*');
+  put_point (13, 8, '*');
+  put_point (13, 9, '*');
+  put_point (14, 10, '*');
+  put_point (15, 11, '*');
+  for (int i = 17; i < 23; i++)
+    put_point (i, 12, '*');
+  put_point (24, 11, '*');
+  put_point (25, 10, '*');
+  put_point (26, 9, '*');
+  put_point (26, 8, '*');
+  put_point (25, 7, '*');
+  put_point (24, 6, '*');
+  put_point (18, 8, 'O');
+  put_point (21, 8, 'O');
+  put_point (17, 10, '`');
+  for (int i = 18; i < 22; i++)
+    put_point (i, 10, '-');
+  put_point (22, 10, '\'');
+  put_point (13, 42, 'Y');
+  printf ("get_point (%d, %d): '%c'\n", 13, 9, get_point (13, 9));
+  printf ("get_point (%d, %d): '%c'\n", 14, 9, get_point (14, 9));
+  printf ("get_point (%d, %d): '%c'\n", 94, 9, get_point (94, 9));
+  display ();
+  return 0;
+}
diff --git a/20191128/textgraph.c b/20191128/textgraph.c
new file mode 100644
index 0000000000000000000000000000000000000000..a17f9a91173f9fe871967f5aef509798c0efbb64
--- /dev/null
+++ b/20191128/textgraph.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include "textgraph.h"
+
+char buffer[HEIGHT][WIDTH];
+
+void clear (char c)
+{
+  for (int y = 0; y < HEIGHT; y++)
+    for (int x = 0; x < WIDTH; x++)
+      buffer[y][x] = c;
+}
+
+static int check_coordinates (int x, int y, char *function)
+{
+  if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT)
+    return 1;
+  else
+    {
+      fprintf (stderr, "coordinates (%d,%d) out of range in %s\n", x, y, function);
+      return 0;
+    }
+}
+
+void put_point (int x, int y, char c)
+{
+  if (check_coordinates (x, y, "put_point"))
+    buffer[y][x] = c;
+}
+
+char get_point (int x, int y)
+{
+  if (check_coordinates (x, y, "get_point"))
+    return buffer[y][x];
+  else
+    return 0;
+}
+
+void display (void)
+{
+  for (int y = 0; y < HEIGHT; y++)
+    {
+      for (int x = 0; x < WIDTH; x++)
+        printf ("%c", buffer[y][x]);
+      printf ("\n");
+    }
+}
diff --git a/20191128/textgraph.h b/20191128/textgraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..419e0fbd04f3b5d07d42509ab1980513d434eb07
--- /dev/null
+++ b/20191128/textgraph.h
@@ -0,0 +1,12 @@
+#ifndef TEXTGRAPH_H
+#define TEXTGRAPH_H
+
+#define WIDTH 40
+#define HEIGHT 20
+
+extern void clear (char c);
+extern void put_point (int x, int y, char c);
+extern char get_point (int x, int y);
+extern void display (void);
+
+#endif