Für ordentliche Eingabemasken ist INPUT auf dem C64 kaum zu gebrauchen. In diesem Beitrag bauen wir deshalb eine eigene Eingaberoutine, die genau das tut, was wir wollen – und nichts anderes.
Warum nicht einfach INPUT?
INPUT lässt dem Nutzer zu viele Freiheiten:
- Mit den Cursortasten kann er das Eingabefeld verlassen, absichtlich oder aus Versehen, und dabei den Bildschirmaufbau zerstören.
- Wir können nicht festlegen, welche Zeichen erlaubt sind. Zahlen, Satzzeichen, Leerzeichen – alles geht durch.
- Wir können die Länge nicht begrenzen. Man kann die Eingabe hinterher prüfen und eine Fehlermeldung ausgeben, aber während der Eingabe hat man keine Kontrolle.
In einem Programm mit Eingabemaske darf der Nutzer nicht mehr tun können, als vorgesehen ist. Da das C64-BASIC außer INPUT nichts mitbringt, schreiben wir uns die Routine selbst.
Die Anforderungen
Als Beispiel soll der Nutzer seinen Namen eingeben. Die Routine muss Folgendes leisten:
- Erlaubt sind nur die Buchstaben A bis Z. Zahlen, Sonderzeichen und Leerzeichen werden ignoriert.
- Der Name darf höchstens 10 Zeichen lang sein. Die Länge wird im Eingabefeld angezeigt.
- Tippfehler lassen sich mit der DEL-Taste korrigieren – aber nicht über den Anfang des Feldes hinaus.
- Der Nutzer kann das Eingabefeld auf keinem Weg verlassen.
- RETURN beendet die Eingabe, aber nur, wenn mindestens ein Zeichen eingegeben wurde.
- Der Name wird in einer Variablen gespeichert und am Ende mit „HALLO NAME!” ausgegeben.
Das fertige Programm
Vorweg das komplette Listing. Es ist erstaunlich kurz:
10 POKE 53280,0:POKE 53281,0
20 PRINT CHR$(147):PRINT CHR$(153)
30 PRINT ”GIB DEINEN NAMEN EIN: [ ]”;
40 PRINT”{11 x left}”;
50 GET A$:IF A$=”" THEN 50
51 IF ASC(A$)=13 THEN 210
52 IF ASC(A$)=20 THEN 305
55 IF ASC(A$)<65 OR ASC(A$)>90 THEN 50
58 IF LEN(N$)>9 THEN 50
60 N$=N$+A$
62 PRINT A$;
70 GOTO 50
200 REM ++++ EINGABE BEENDEN ++++
210 IF LEN(N$)=0 THEN 50
220 PRINT:PRINT
230 PRINT ”HALLO ”;N$;”!”
240 END
300 REM ++++ EINGABE LOESCHEN ++++
305 IF LEN(N$)=0 THEN 50
306 N$=MID$(N$,1,LEN(N$)-1)
310 PRINT ”{left} {left}”;
390 GOTO 50
Hinweis: {left} steht für die Taste Cursor links innerhalb eines Strings. {11 x left} bedeutet, dass die Taste elfmal hintereinander gedrückt wird. Zwischen den eckigen Klammern in Zeile 30 stehen genau 10 Leerzeichen.
Jetzt Schritt für Schritt.
Schritt 1: Bildschirm und Eingabemaske
10 POKE 53280,0:POKE 53281,0 20 PRINT CHR$(147):PRINT CHR$(153)
Zeile 10 schaltet Rahmen (53280) und Hintergrund (53281) auf Schwarz. In Zeile 20 löscht CHR$(147) den Bildschirm – das entspricht der Tastenkombination SHIFT + CLR/HOME. CHR$(153) stellt die Schriftfarbe auf Hellgrün. Die PETSCII-Codes der Farben findet man in jeder PETSCII-Tabelle; Hellgrün ist $99 bzw. dezimal 153.
30 PRINT ”GIB DEINEN NAMEN EIN: [ ]”;
40 PRINT”{11 x left}”;
Zeile 30 zeichnet die Maske. Die zehn Leerzeichen zwischen den Klammern zeigen dem Nutzer, wie viel Platz er hat.
Nach dem PRINT steht der Cursor hinter der schließenden Klammer. Zeile 40 setzt ihn deshalb mit elfmal Cursor links zurück auf das erste Feld: einmal über die Klammer, zehnmal über die Leerzeichen. (Im Video musste ich hier zweimal nachbessern, einmal bei den Leerzeichen und einmal bei den Cursor-links-Zeichen. Beim Nachzählen lohnt es sich, die Klammer nicht zu vergessen.)
Wichtig ist bei beiden Zeilen das Semikolon am Ende. Ohne Semikolon setzt PRINT nach der Ausgabe den Cursor an den Anfang der nächsten Zeile. Mit Semikolon bleibt er dort stehen, wo die Ausgabe endet.
Schritt 2: Tasten abfragen mit GET
Statt INPUT verwenden wir GET. GET wartet nicht auf eine ganze Zeichenkette mit RETURN, sondern liest genau ein Zeichen aus dem Tastaturpuffer. Wurde keine Taste gedrückt, bleibt die Variable leer und das Programm läuft sofort weiter.
50 GET A$:IF A$=”" THEN 50
Deshalb die Schleife: Solange A$ leer ist, springt Zeile 50 auf sich selbst zurück. Erst wenn eine Taste gedrückt wurde, geht es weiter.
Ein Nebeneffekt, den wir gern mitnehmen: GET zeigt nichts auf dem Bildschirm an und führt keine Steuerzeichen aus. Ein Druck auf eine Cursortaste bewegt also nicht den Cursor, sondern liefert nur ein Zeichen in A$. Was davon auf dem Bildschirm landet, entscheiden allein wir.
60 N$=N$+A$ 62 PRINT A$; 70 GOTO 50
Ist eine Taste gedrückt worden, hängen wir das Zeichen an die Variable N$ (für Name) an und geben es aus – wieder mit Semikolon, damit der Cursor in der Zeile bleibt. Dann geht es zurück zu GET und zur nächsten Taste.
Damit können wir schon tippen. Allerdings noch alles, auch DEL, und das zerstört prompt die Maske. Also schränken wir ein.
Schritt 3: Nur Buchstaben zulassen
Man könnte jeden erlaubten Buchstaben einzeln prüfen. Das wäre mühsam und lang. Einfacher geht es mit ASC: Die Funktion liefert den PETSCII-Code eines Zeichens. Da die Buchstaben A bis Z in der Tabelle direkt hintereinander liegen (A = 65, Z = 90), genügt eine Bereichsprüfung:
55 IF ASC(A$)<65 OR ASC(A$)>90 THEN 50
Liegt der Code außerhalb dieses Bereichs, springen wir zurück zu GET. Die Taste wird weder angezeigt noch gespeichert – für den Nutzer passiert einfach nichts.
Die Reihenfolge ist dabei wichtig: ASC darf erst aufgerufen werden, wenn A$ nicht leer ist. ASC(”") bricht auf dem C64 mit ?ILLEGAL QUANTITY ERROR ab. Durch die Warteschleife in Zeile 50 ist das sichergestellt.
Schritt 4: Ausnahmen für RETURN und DEL
Jetzt sind auch RETURN und DEL gesperrt, denn auch sie liegen außerhalb von 65 bis 90. Beide brauchen wir aber. Deshalb prüfen wir sie vor der Bereichsprüfung und springen in eigene Unterprogramme:
51 IF ASC(A$)=13 THEN 210 52 IF ASC(A$)=20 THEN 305
13 ist der PETSCII-Code von RETURN, 20 der von DEL.
Eingabe beenden
200 REM ++++ EINGABE BEENDEN ++++ 210 IF LEN(N$)=0 THEN 50 220 PRINT:PRINT 230 PRINT ”HALLO ”;N$;”!” 240 END
LEN liefert die Länge einer Zeichenkette. Ist N$ noch leer, ignorieren wir RETURN einfach und warten auf die nächste Taste. So kommt der Nutzer nicht aus der Maske, bevor er mindestens einen Buchstaben eingegeben hat. Andernfalls gilt die Eingabe als vollständig und wir begrüßen ihn mit seinem Namen.
Zeichen löschen
Der erste Gedanke wäre, DEL einfach auszuführen, also PRINT A$ auch für das Löschzeichen. Das funktioniert, hat aber einen unschönen Nebeneffekt: DEL schiebt auf dem C64 alles, was rechts vom Cursor in der Zeile steht, eine Stelle nach links. Die schließende Klammer der Maske wandert also bei jedem Löschen mit.
Die Lösung ist, das Löschen nachzubauen:
310 PRINT ”{left} {left}”;
Ein Schritt nach links, ein Leerzeichen über das alte Zeichen schreiben, wieder ein Schritt nach links. Das Ergebnis sieht aus wie DEL, aber der Rest der Zeile bleibt, wo er ist.
Schritt 5: Die Grenzen des Feldes
Noch kann der Nutzer rechts über das Feld hinaustippen und links über den Anfang hinauslöschen. Für beide Fälle brauchen wir die Cursorposition. Die könnte man auslesen – müssen wir aber nicht, denn wir haben sie schon: Die Länge von N$ entspricht genau der Zahl der Zeichen im Feld und damit der Position des Cursors.
Rechte Grenze
58 IF LEN(N$)>9 THEN 50
Hat N$ schon 10 Zeichen, ist das Feld voll und jede weitere Taste wird ignoriert. „>9” bedeutet bei ganzen Zahlen dasselbe wie „>=10”. Die Prüfung steht hinter der Buchstabenprüfung, aber vor Zeile 60 – RETURN und DEL funktionieren also auch bei vollem Feld.
Linke Grenze
Die Prüfung für die linke Grenze muss in das Lösch-Unterprogramm. Im Video hatte ich sie zuerst in die Hauptschleife geschrieben, wo sie nie zum Zug kam: Bei DEL springt Zeile 52 schon vorher nach 305.
305 IF LEN(N$)=0 THEN 50
Ist N$ leer, steht der Cursor am Feldanfang, und DEL wird ignoriert.
Schritt 6: Den Namen korrekt mitführen
Ein Fehler blieb noch: Gibt man „THOMAS” ein, löscht alles und tippt „TOMMY”, steht am Ende „HALLO THOMASTOMMY!”. Auf dem Bildschirm wurde gelöscht, in N$ aber nicht.
Beim Löschen muss also auch das letzte Zeichen aus N$ entfernt werden:
306 N$=MID$(N$,1,LEN(N$)-1)
MID$ liefert ab Position 1 alle Zeichen außer dem letzten. Dasselbe ginge auch mit LEFT$(N$,LEN(N$)-1).
Hier zeigt sich, warum Zeile 305 vor 306 stehen muss: Bei leerem N$ wäre LEN(N$)-1 gleich -1, und MID$ bricht mit ?ILLEGAL QUANTITY ERROR ab – genau das ist im Video passiert, bevor die Prüfung an der richtigen Stelle stand.
Das Ergebnis
Alle Anforderungen sind erfüllt:
- Nur Buchstaben von A bis Z werden angenommen.
- Nach 10 Zeichen ist Schluss.
- DEL löscht, ohne die Maske zu verschieben, und hört am Feldanfang auf.
- Der Nutzer kann das Feld weder nach links noch nach rechts verlassen, und da die Cursortasten ignoriert werden, auch nicht nach oben oder unten.
- RETURN funktioniert nur mit mindestens einem Zeichen.
- N$ enthält immer genau das, was im Feld steht.
Die Routine lässt sich leicht anpassen. Ein paar Ideen:
- Andere Feldlänge: Leerzeichen in Zeile 30, Anzahl der Cursor-links-Zeichen in Zeile 40 und die 9 in Zeile 58 ändern.
- Weitere Zeichen erlauben: Ziffern haben die Codes 48 bis 57, das Leerzeichen hat 32. Dafür eine eigene Ausnahme vor Zeile 55 einfügen, die direkt nach Zeile 58 springt.
- Als Unterprogramm: Ersetzt man END in Zeile 240 durch RETURN und ruft die Routine mit GOSUB auf, lässt sie sich für mehrere Felder in einem Programm verwenden. N$ muss dann vor jedem Aufruf mit N$=”" geleert werden.
Damit ist die Aufgabe gelöst. Wer sich diesen Beitrag lieber als Video anschaut kann das au meinem Youtube-Kanal machen.