Die Suche ergab 233 Treffer

von bbock
21.02.2023, 20:54
Forum: Hardware
Thema: PCW WiFi Modem
Antworten: 35
Zugriffe: 19088

Re: PCW WiFi Modem

Jungsi hat geschrieben: 21.02.2023, 18:20 ...Leider bin ich noch PCW-Anfänger - daher habe ich keine Ahnung, wie ich QTERM richtig starten kann. Ich scheitere bedauerlicherweise schon daran, das auf eine Diskette zu bringen :-) Die Dateien sind größer als eine 180K-Disk....
Du brauchst nicht alle Dateien aus QTERM4.3f.zip; es genügt die Datei QTERMAMS.COM.
Jungsi hat geschrieben: 21.02.2023, 18:20 In meinem PCW ist ein GOTEK verbaut und funktioniert. CP/M Plus 1.4 habe ich, aber danach ???
CP/M Plus 1.4 ist gut bis 4800 Baud; das reicht auch für den Anfang. Für mehr solltest du mind. CP/M Plus 1.7 verwenden (ich nutze 1.8).
von bbock
21.02.2023, 16:08
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 36785

Re: Fragen und Antworten zum C-Kurs

Hallo Paul,

danke für's Feedback. Das hält mich motiviert weiterzumachen. :)
von bbock
19.02.2023, 13:07
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 10363

8. Kontrollstrukturen

8.1 Anweisungen und Blöcke

Einzelne Anweisungen werden in C immer mit einem Semikolon beendet, z.B.

Code: Alles auswählen

x = 0;
Eine oder mehrere Anweisungen werden mit geschweiften Klammern zu Blöcken zusammengefasst. Der Programmcode von Funktionen ist etwa immer in einem Block zusammengefasst, aber auch Befehlssequenzen von Kontrollanweisungen wie if, else, while oder for.

8.2 if-Anweisungen

Die allgemeine Syntax von if-Anweisungen ist:

Code: Alles auswählen

if (Ausdruck)
    Anweisung1
else
    Anweisung2
Anweisung1 und Anweisung2 können dabei einzelne Statements oder Blöcke sein. Das else ist optional. Beispiele:

Code: Alles auswählen

if (a > b)
    z = a;
else
    z = b;

Code: Alles auswählen

if (r > 10) {
    printf("r darf max. 10 sein.\n");
    r = 10;
}
8.3 else-if Ketten

Code: Alles auswählen

if (Ausdruck1)
    Anweisung1
else if (Ausdruck2)
    Anweisung2
else if (Ausdruck3)
    Anweisung3
else
    Anweisung4
Dies ist eine häufig anzutreffende Konstruktion. Hier wird ein Ausdruck nach dem anderen ausgewertet. Ist ein Ausdruck true, dann wird die zugehörige Anweisung ausgeführt. Trifft keine der Bedingungen zu, d.h. alle Ausdrücke ergeben false, dann wird der else-Zweig ausgeführt.

8.4 switch

Mit switch kann bequem eine von mehreren Alternativen ausgewählt werden, wobei mit Konstanten verglichen wird. Beispiel einer Menüauswahl:

Code: Alles auswählen

char c;
c = getMenuSelection();

switch (c) {
case 'x':
case 'X':
    cleanup();
    exitApp = true;
    break;
case 'd':
case 'D':
    drawPicture();
    break;
default:
    printf("ungültige Option\n");
}
Hier wird die Benutzerauswahl im Menü als Zeichen zurückgeliefert. Bei kleinem oder großem X soll die Anwendung beendet werden, bei kleinem oder großem D wird ein Bild gezeichnet. Andernfalls erscheint eine Fehlermeldung.

Wichtig sind die break-Befehle, denn ohne sie würde der Code einfach zum nächsten case "durchfallen". So passiert bei der Eingabe eines kleinen x zunächst nichts und der Code des nächsten case (mit dem großen X) wird ausgeführt. Dessen Code endet aber mit break, so dass anschließend hinter der geschweiften Klammer zu fortgesetzt wird.

Der default-Fall wird ausgeführt, wenn keine der case-Anweisungen einen Treffer liefert oder wenn im darüberstehenden case nicht mit break abgeschlossen wurde.

8.5 Schleifen - do, while und for

C kennt drei Konstrukte zur wiederholten Ausführung von Anweisungen. Die do-Schleife wird immer mindestens einmal durchlaufen; die Prüfung der Abbruchbedingung erfolgt in der while-Anweisung am Ende. Bei der while-Schleife ist es genau umgekehrt: die Abbruchbedingung wird gleich zu Beginn geprüft. Dadurch kann es sein, dass der Schleifenblock überhaupt nicht durchlaufen wird. Die for-Schleife schließlich ist ideal für Zählvorgänge, bei der eine Schleifenvariable hoch- oder heruntergezählt wird.

Beispiel do-Schleife:

Code: Alles auswählen

do {
    c = getMenuSelection();
    performFunction(c);
} while (c != 'x' && c != 'X');
Die Menüauswahl wird ermittelt und eine dazu passende Funktion via performFunction ausgeführt. Das geschieht so oft, bis ein X eingegeben wurde (unabhängig von der Groß-/Kleinschreibung). performFunction wird auch einmal mit X aufgerufen und muss das berücksichtigen.

Beispiel while-Schleife:

Code: Alles auswählen

c = '.';
i = 0;
while (c != '\0') {
    c = s[i++];
    if (c != '\0')
        putchar(c);
}
Alle Zeichen im Array s werden nacheinander ausgegeben, bis ein Null-Zeichen erreicht wird. Das entspricht der Ausgabe eines Strings, wie er ähnlich auch in der Funktion puts aus der C-Standard-Ein-Ausgabe-Bibliothek zu finden sein dürfte. Unschön ist hier allerdings die doppelte Auswertung der Bedingung c != '\0'.

Beispiel for-Schleife:

Code: Alles auswählen

for (i = 0; i < 10; i++) {
    printf("%d^2 = %d\n", i, i * i);
}
Eine Liste der Zahlen 1 bis 9 und deren Quadratzahlen wird ausgegeben:
0^2 = 0
1^2 = 1
2^2 = 4
...
9^2 = 81

Schleifen können durch die Anweisung break vorzeitig abgebrochen werden. Die Anweisung sorgt dafür, dass die innerste Schleife (oder eine switch-Anweisung) sofort verlassen wird.

Beispiel String-Ausgabe:

Code: Alles auswählen

i = 0;
while (true) {
    c = s[i++];
    if (c == '\0')
        break;
    putchar(c);
}
Mit der Anweisung continue leitet sofort die nächste Wiederholung der Schleife ein ohne den darauffolgenden Code auszuführen.

Im folgenden Beispiel werden nur die positiven Elemente eines Arrays bearbeitet:

Code: Alles auswählen

for (i = 0; i < N; i++) {
    if (a[i] < 0)
        continue; /* negative Elemente überspringen */
    ...           /* positive Elemente bearbeiten */
}
Die Anweisung goto sollte möglichst nicht verwendet werden, da sie leicht zu unübersichtlichem "Spaghetti-Code" führt und den Grundsätzen der strukturierten Programmierung widerspricht. Dennoch gibt es einen Fall, in dem goto nützlich sein kann: das Beenden einer tiefer verschaltelten Schleife. Die break-Anweisung beendet nur die innerste Schleife. Das Verlassen einer z.B. doppelt verschachtelten Schleife kann man mit goto zu einer Sprungmarke bewerkstelligen:

Code: Alles auswählen

for (...) {
    for (...) {
        ...
        if (Katastrophe)
            goto error;
    }
    ...
}
error:
    ...
von bbock
18.02.2023, 15:16
Forum: Programmierung
Thema: Vektorgrafik für die Joyce
Antworten: 61
Zugriffe: 77857

Re: Vektorgrafik für die Joyce

Das Programm vecread habe ich in Version 4.2 mit dem z88dk nach C portiert: Download

Es funktioniert genauso wie die TurboPascal-Version - nur ca. doppelt so schnell!
von bbock
18.02.2023, 14:58
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 36785

Re: Fragen und Antworten zum C-Kurs

Ja, die eckigen und geschweiften Klammern sehen mit den deutschen Tastatureinstellungen auf der Joyce "ungewohnt" aus... :D
Aber wir entwickeln und editieren ja auf dem PC (Stichwort Cross Compiler), und da haben wir dieses Umlauteproblem nicht (dafür ein anderes - dazu später mehr).

Bei mir sieht die Ausgabe von einaus.com so aus:

einaus.png
einaus.png (7.19 KiB) 2284 mal betrachtet
von bbock
17.02.2023, 15:54
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 10363

7. Ein- und Ausgabe

Ein zentrales Thema bei der Verarbeitung von Computerprogrammen ist die Ein- und Ausgabe von Daten. Wir legen ein neues Projektverzeichnis mit dem Namen einaus an, in dem wir folgenden Quellcode als einaus.c speichern:

Code: Alles auswählen

#include <stdio.h>

#define PI 3.141592654

int main(void) {
    float radius, flaeche;
    
    printf("Kreisflaeche berechnen\n\nRadius: ");
    scanf("%f", &radius);
    flaeche = PI * radius * radius;
    printf("\nDie Flaeche betraegt: %f\n", flaeche);
    
    return 0;
}
Wir erzeugen das ausführbare Programm mit

Code: Alles auswählen

zcc +cpm -subtype=pcw80 -lm einaus.c -o einaus.com
Es ist an der Zeit ein paar Worte über das Kompilieren und Binden zu verlieren. zcc ist nicht der Compiler, sondern das Compiler Frontend, das seinerseits einen C-Compiler aufruft. Das z88dk hat gleich zwei zur Auswahl: den klassischen Compiler sccz80 und den moderneren Compiler zsdcc. Über die zcc-Option -compiler=sdcc können wir zsdcc auswählen; mit -compiler=sccz80 wird der klassische Compiler verwendet, der auch der Standard ist, wenn wir die Option -compiler weglassen.

Die Option +cpm wählt die Plattform aus, für die der Code erzeugt werden soll (CP/M). Dazu geben wir den Untertyp pcw80 über die Option -subtype an. pcw40 wäre genausogut möglich; der Unterschied ist nur das Diskettenformat der bei Verwendung der zusätzlichen Option -create-app erzeugten Image-Datei, die automatisch die Endung .dsk bekommt. Beide pcw-Optionen erzeugen Code für die Amstrad PCW-Rechner.

zcc kompiliert nicht nur, sondern kann auch gleich den Linker aufrufen um eine ausführbare COM-Datei zu erstellen, was wir ja bereits fleißig nutzen. Mit -l kann man zusätzliche Bibliotheken (libraries) angeben, die zum Programm dazugebunden werden. Hier wird mit -lm die Standard-Mathematik-Library dazugebunden, die wir für die float-Verarbeitung brauchen.

Dann folgt die zu kompilierende C-Datei, hier einaus.c. An dieser Stelle kann auch eine Liste von mehreren C-Dateien stehen, von denen aber nur eine eine main-Funktion enthalten darf. Über die Option -o können wir schließlich den Namen der Ausgabedatei angeben, hier einaus.com.

Nun zum Programm einaus:

Der #include-Befehl ist eine sog. Präprozessor-Direktive, wie alle Befehle, die am Zeilenanfang ein Hash-Symbol (#) haben. Wichtig: das # muss ganz am Anfang der Zeile stehen! Der Präprozessor verarbeitet alle Quellcode-Dateien, bevor der Compiler zum Zuge kommt. Er liest nur die Zeilen, die mit # führt dann bestimmte Textersetzungsoperationen durch. Bei #include fügt er genau an dieser Stelle den Inhalt der Datei an, die hinter der #include-Direktive aufgeführt ist. Steht der Dateiname in spitzen Klammern wie hier, dann wird die Datei im include-Verzeichnis des Compilers gesucht. Steht er in Gänsefüßchen, dann wird sie im selben Verzeichnis gesucht, in dem sich auch die Datei befindet, die die #include-Direktive enthält.

Die #define-Direktive wird oft für die Definition von Konstanten verwendet, hier für die Kreiszahl Pi. Der Präprozessor sucht in der Folge alle Textstellen, in denen "PI" vorkommt und ersetzt sie durch "3.141592654". Das funktioniert wie beim Suchen / Ersetzen im Editor.

Es folgt die Funktion main, also der Einsprungpunkt in das Programm. In unserem Beispiel werden keine Parameter von der Kommandozeile gebraucht, also steht zwischen den Klammern das Schlüsselwort void, was so viel wie "nichts" bedeutet.

Dann folgt einen geöffnete geschweifte Klammer. Alle Anweisungen bis zur zugehörigen schließenden geschweiften Klammer bilden den Funktionsblock. Jede Anweisung wird in C mit einem Semikolon beendet. Das ist anders als in Pascal, wo das Semikolon zwischen Anweisungen steht.

Zu Beginn des Funktionsblocks werden zwei Variablen vom Typ float deklariert: radius und flaeche. Dann geben wir Text über die Funktion printf aus. printf ist eine Abkürzung für "print formatted", weil sie die Ausgabe von formatiertem Text unterstützt. Der Text besteht aus der Überschrift "Kreisflaeche berechnen", dann folgen zwei Zeilenumbrüche (\n) und die Eingabeaufforderung "Radius: ". Die Ausgabe erscheint auf dem Bildschirm.

Wir wollen eine Gleitkommazahl eingeben; dazu dient die Funktion scanf ("scan formatted"). scanf kann mehrere Parameter haben. Der erste ist immer der sog. Format-String: "%f" steht hier für "float" - wir wollen also einen Gleitkomma-Wert von der Tastatur "scannen". Die folgenden Parameter geben für jede mit dem Prozentzeichen eingeleitete Formatangabe jeweils eine Variable an, in die die eingegebenen Daten gespeichert werden sollen. Anders als bei printf werden die Daten aber nicht aus einer Variablen gelesen, sondern sie sollen in eine Variable geschrieben werden. Dazu muß scanf die Adresse der Variablen kennen. Als zweiten Parameter haben wir daher nicht radius, sondern &radius. Der &-Operator ermittelt die Adresse der Variablen im Speicher des Rechners.

Die flaeche wird mit der bekannten Kreisformel berechnet. Ich möchte noch darauf hinweisen dass der Präprozessor aus dem "flaeche = PI * radius * radius;" bereits ein "flaeche = 3.141592654 * radius * radius;" gemacht hat. Das ist der Code, den der Compiler "sieht".

Die Fläche wird mit printf ausgegeben, wobei auch hier eine Formatangabe "%f" verwendet wird, um eine Gleitkommazahl auszugeben. An Stelle des "%f" wird der Wert der Variablen flaeche eingesetzt, der printf als zweiter Parameter übergeben wurde.

Schließlich geben wir via return eine 0 an das Betriebssystem CP/M zurück und signalisieren damit, dass kein Fehler aufgetreten ist.

Wenn wir das Programm einaus.com starten, dann erscheint die Aufforderung zur Eingabe des Radius. Geben wir z.B. 12.5 ein, dann erscheint das Ergebnis: "Die Flaeche betraegt: 490.873852".
von bbock
17.02.2023, 11:06
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 10363

6. Variablen und Datentypen

C kennt folgende elementare Datentypen:

char: ein einzelnes Zeichen
short: ein kleiner, ganzzahliger Wert
int: ein ganzzahliger Wert
long: ein großer, ganzzahliger Wert
float: ein Gleitkomma-Wert
double: ein Gleitkomma-Wert mit doppelter Genauigkeit

Die ganzzahligen Datentypen können mit Vorzeichen oder (mit dem Schlüsselwort unsigned) ohne Vorzeichen verwendet werden. Die Größen der elementaren Datentypen sind in C - anderes als etwa in Java - maschinenabhängig. Für unsere Joyce gelten folgende Angaben:

char: 1 Byte, -128 bis 127
unsigned char: 1 Byte, 0 bis 255
short: 2 Bytes, -32768 bis 32767
unsigned short: 2 Bytes, 0 bis 65535
int: 2 Bytes, -32768 bis 32767
unsigned int: 2 Bytes, 0 bis 65535
long: 2 Bytes, -32768 bis 32767
unsigned long: 2 Bytes, 0 bis 65535
float: 6 Bytes, 1.000E-38 bis 9.995E+37 (6 Dezimalstellen)
double: 6 Bytes, 1.000E-38 bis 9.995E+37 (6 Dezimalstellen)

Die Angaben können je nach verwendeter math library abweichen. Das z88dk kennt mehrere davon; im Beispielprogramm temperat.c haben wir die Standard-Mathematik-Bibliothek mit -lm eingebunden. Sie verwendet ein 6-Byte-Format mit 40 Bit Mantisse und 8 Bit Exponent. Man sieht insbesondere, dass sich die Datentypen short, int und long nicht unterscheiden, genausowenig wie float und double.

Neben den elementaren Datentypen gibt es noch:
  • void (die große Leere, also gar nichts)
  • Aufzählungstypen (enumerations, C-Schlüsselwort enum)
  • Datenfelder (arrays)
  • Strukturen (C-Schlüsselwort struct)
  • Unions (C-Schlüsselwort union)
  • Zeigertypen (pointer)
  • Funktionstypen
Es gibt in C keinen Datentyp "String", dennoch gibt es natürlich strings und auch eine ganze library mit Funktionen dafür. Allerdings sind C-Strings einfach nur Datenfelder von Zeichen, bei denen das letzte Zeichen das Nullzeichen '\0' ist. Wie man damit umgeht, folgt in einem späteren Kapitel.
von bbock
16.02.2023, 20:44
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 10363

5. Die Struktur von C-Programmen

C-Programme bestehen aus Dateien mit den Endungen .c (C-Quellcode) und .h (Header-Dateien). Die Header-Dateien enthalten die Schnittstellen um Funktionen und weitere Definitionen aus anderen C-Dateien verfügbar zu machen. Dies geschieht per #include-Anweisung. Die Definition der Programme, also die eigentliche Logik, befindet sich aber in den C-Dateien selbst.

Alle C-Programme bestehen aus Funktionen. Prozeduren gibt es nicht; stattdessen gibt es Funktionen, die keinen Wert zurückliefern. Diese haben dann den Typ void. Eine besondere Funktion hat einen festgelegten Namen und stellt den Einsprungpunkt für ein C-Programm dar: die Funktion main. Sie muss in jedem C-Programm vorhanden sein und hat folgende Signatur:

Code: Alles auswählen

int main(int argc, char *argv[])
Die Parameter argc und argv sind für den Zugriff auf die Kommandozeilenparameter des Programms zuständig; dazu später mehr. Werden keine Kommandozeilenparameter benötigt, dann kann man die main-Funktion auch so schreiben:

Code: Alles auswählen

int main(void)
Weitere Funktionen können in derselben oder in einer weiteren C-Datei geschrieben werden. Alle Funktionen haben den generellen Aufbau

Code: Alles auswählen

Rückgabetyp Funktionsname(Parameterliste) {
  ...
}
Innerhalb der geschweiften Klammern stehen die Anweisungen, die die Funktion ausmachen. Die folgende Funktion wandelt z.B. eine Temperatur von Grad Fahrenheit in Grad Celsius um:

Code: Alles auswählen

float fahrenheitToCelsius(float fahrenheit) {
  return (fahrenheit - 32.0) / 1.8;
}
Hier wird als Parameter eine Zahl vom Typ float, also eine Fließkommazahl, übergeben. Auch der Rückgabewert ist vom Typ float; er wird berechnet und mit dem Befehl return zurückgegeben.

Ein Beispielprogramm, dass die Funktion verwendet könnte wie folgt aussehen. Wir speichern den Programmtext in einem neuen Projektverzeichnis namens fahrenheit unter dem Dateinamen temperat.c:

Code: Alles auswählen

#include <stdio.h>

float fahrenheitToCelsius(float fahrenheit) {
  return (fahrenheit - 32.0) / 1.8;
}

int main(void) {
    float f, c;

    f = 72.0;
    c = fahrenheitToCelsius(f);
    
    printf("%f Grad Fahrenheit sind %f Grad Celsius\n", f, c);
    
    return 0;
}
Dann erzeugen wir das ausführbare Programm mit

Code: Alles auswählen

zcc +cpm -subtype=pcw80 -lm temperat.c -o temperat.com
Anschließend kopieren wir temperat.com in das ramdisk-Verzeichnis der CP/M Box und starten es. Die Ausgabe lautet
72.000000 Grad Fahrenheit sind 22.222222 Grad Celsius.
von bbock
15.02.2023, 18:38
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 36785

Re: Fragen und Antworten zum C-Kurs

Und ein Punkt in der Mitte?
von bbock
15.02.2023, 17:56
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 36785

Re: Fragen und Antworten zum C-Kurs

Immer langsam mit den jungen Pferden. Die ganzen Themen kommen ja noch; wir sind erst ganz am Anfang.

Nur so viel vorab:

Die BIN-Dateien sind binäre Dateien, die der Compiler erzeugt. Die werden dann mit den Libraries zur fertigen COM-Datei gebunden. Die BIN-Dateien sind also nur Zwischendateien; man braucht sie später nicht mehr. Solltet ihr ein Versionsverwaltungsprogramm wie GIT verwenden, dann käme *.BIN auf die ignore-Liste. Es können übrigens bei Bedarf durch zcc-Schalter auch weitere Zwischendateien erzeugt werden, z.B. ASM- oder LST-Dateien. Die können z.B. für's Debugging interessant sein (ist aber nur für Fortgeschrittene).

Die DSK-Dateien sind Disketten-Images, die direkt von CP/M-Box via File > Drive A bzw. File > Drive B geladen werden können. Oder man konvertiert sie in HFE-Dateien und lädt sie mit dem Gotek-Laufwerk. Ich finde es am einfachsten, die COM-Dateien ins CP/M Box ramdisk-Verzeichnis zu kopieren und über Laufwerk M: zu starten.

Was die Dateinamen angeht: zcc und die zugehörigen Programme sind ja PC-Programme, die (u.a.) unter Windows laufen. Sie können daher mit langen Dateinamen umgehen. Alles, was wir Richtung CP/M kopieren, also insbesondere die COM-Dateien, müssen aber den CP/M-Regeln folgen (8.3).

Zum #include: Dateien, die in spitze Klammern eingeschlossen werden, sucht der Compiler in seinem include-Verzeichnis. Die mit den Gänsefüßchen sucht er im selben Verzeichnis, in dem sich auch die Datei mit dem #include-Befehl befindet.

Das wird aber alles noch ausführlicher erklärt.