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