Dieser Artikel soll anhand eines Assembler-Beispielprogramms auf die sehr interessante Programmierung des in CMOS-Technik aufgebauten Single-Chip-Tastaturprozessors “HD 6301 VI” im ATARI ST eingehen. Der im folgenden nur noch “IKBD” (Intelligent KeyBoarD) genannte Prozessor wird neben anderen Herstellern auch von HITACHI Semiconductors produziert, die mir freundlicherweise das “HD 6301 VI - USER’S MANUAL” zur Verfügung stellten und deshalb nicht unerwähnt bleiben sollten, genausowenig wie mein guter Freund Thomas Schneider, von dem ich vor langer Zeit die Idee zum Programm in GFA-BASIC bekam. Die Interrupt-Programmierung des IKBD läßt sich aber nur in Assembler (bzw. C mit Inline-Assembler) bewerkstelligen, weshalb ich auch auf diese immer beliebter werdende Sprache auf dem ATARI ST zurückgreife.
Das “Betriebssystem” des IKBD erlaubt einige interessante Kommandos, die, wie wir noch sehen werden, in einfacher Form vom ST aus aktiviert werden können. Übrigens befindet sich in allen Rechnern der ATARI ST-Serie der gleiche IKBD mit unverändertem Betriebssystem; eine Update-Version ist meines Erachtens auch nicht nötig (abgesehen von dem an wenigen Stellen sehr unsauberen Programmierstil).
Vorneweg sei aber gesagt, daß ich in diesem Artikel nicht auf das disassemblierte ROM-Listing (4 kByte) des IKBD eingehen möchte, da sicher nur wenigen ST-Usern auch der 8-Bit-Befehlssatz des 6301 bekannt ist, eine derartige Ausführung also sinnlos und für die meisten Leser uninteressant wäre. Ich stelle es aber jedem interessierten Leser frei, das von mir kommentierte Listing als ASCII-File (ca. 2500 Zeilen) gegen Einsenden einer formatierten Leerdiskette zu erhalten, auf die ich dann das Listing kopieren kann. Meine Adresse finden Sie im Kopf des Hauptprogramms.
Vielmehr möchte ich als Abschluß des Beitrages ein vollkommen in Assembler geschriebenes Programm vorstellen, welches ähnlich dem Control-Accessory das Setzen der Uhrzeit nach jedem Reset erspart, aber durch die Anwesenheit im AUTO-Ordner keinen Speicherplatz reserviert hält.
Wie schon in der “ST-Computer” 2/89 auf den Seiten 65ff von Alex Esser berichtet, besitzt der ATARI ST mit dem IKBD eine resetfeste Software-Uhrzeit, die über den integrierten TIMER sekundenweise hochgezählt wird und auch über das aktuelle Datum Auskunft geben kann. In dem vorgestellten Programm wird die aktuelle Uhrzeit nur einmal nach jedem Kaltstart (Einschalten des Rechners) eingegeben, nach jedem weiteren Warmstart (Reset) wird das Programm aus dem AUTO-Ordner geladen und die aktuelle Zeit des IKBD in die GEMDOS-Uhr kopiert.
Achtung: Wie jeder stolze Mega ST-Besitzer bestimmt schon längst bemerkt hat, ist das Programm bei eingebauter Echtzeit-Uhr natürlich sinnlos. Aber da der 1040er letztlich Computer des Jahres 1988 geworden ist, müßte der Mammutteil der STs ohne Hardware-Uhr ausgerüstet sein.
Viele User haben noch das umständliche Control-Accessory auf ihrer Boot-Diskette, was nach dem Lesen dieses Artikels zumindest aus der Sicht einer resetfesten Uhrzeit Schnee von gestern werden müßte. Das Programm kann natürlich jederzeit aufgerufen werden, es gibt dann sekundenweise laufend die aktuelle Uhrzeit plus Datum aus dem IKBD genauso wieder, wie es auch nach einem Reset erscheint und eine Korrektur ermöglicht.
Diese Korrektur aber wird knallhart überwacht : Es können nur real existente Daten vom 01.01.1980 bis zum 31.12.2079 eingegeben werden, ebenso natürlich nur dann ein 29. Februar, wenn auch ein Schaltjahr vorhanden ist. Die Beschränkung auf 2079 stammt nicht von mir, sondern von Digital Research, die für das GEMDOS verantwortlich sind. Außerdem wäre ich, ähnlich den meisten anderen Lesern, zu diesem Zeitpunkt 113 Jahre alt und würde mich kaum mehr mit Computern beschäftigen.
Die Korrektur kann nach jeder Einheit (Tag, Monat,...) durch Druck auf [RETURN] beendet werden, dann übernimmt das Programm die unveränderten Werte. Dies erspart weniger peniblen Menschen das genaue Stellen der Sekunden.
Beendet werden kann das Programm entweder durch das Betätigen der [J]-Taste oder viel einfacher durch [RETURN]. Jede andere Taste ermöglicht eine nochmalige Korrektur der Daten.
Für diejenigen unter den Lesern, die aber noch nicht gewußt haben, daß der ATARI ST einen eigenen Tastaturprozessor besitzt, folgt vorerst eine kleine Erläuterung zur Existenzberechtigung dieses “Single-Chip-Prozessors” aus der Familie des 8-Bit 6800. Die “Freaks” können diese Abschnitte getrost überfliegen.
Damit Sie mit einem Computer wie dem ATARI überhaupt kommunizieren können, benötigen Sie eine Peripherieschnittstelle. Hier gibt es verschiedene Möglichkeiten; am ATARI sind es die RS-232 (serielle Datenübertragung), die Centronics- (8-Bit-parallel) und die DMA-Schnittstelle (8-Bit-parallel, 10 MBit/s) für Harddisk, Laserdrucker oder eine Applikation mit VME-Bus (z.B. Netzwerke mehrerer Rechner bzw. Ein-/Ausgabegeräten).
Nicht zu vergessen sind natürlich noch die Floppy (seriell, 250 kBit/s), der ROM-Port (Cartridge) und die MIDI-Schnittstelle (“Musical Instrument Digital Interface”, seriell) zum gleichzeitigen Betrieb mehrerer Synthesizer, die dem ST unter anderem bei Musik-Profis eine so große Popularität bescherte.
Aber niemand kann sich einen Personalcomputer ohne TASTATUR vorstellen. Diese für die Programmentwicklung und Textverarbeitung wichtigste Schnittstelle wird bei größeren Rechnern i.allg. durch einen eigenen Prozessor verwaltet.
So auch bei unserem ST. Ein eigener Prozessor deshalb, weil eben die Abfrage der vielen Tasten plus angeschlossener Maus und/oder Joystick selbst in einer Matrixanordnung dem Hauptrechner sehr viel Zeit wegnehmen würde. Und Zeit ist Geld -> Geld hat aber fast keiner von uns -> Ein eigener Prozessor muß her!
So also geschehen auch im ATARI. Wenn Sie nun eine der auf dem ST befindlichen 95 Tasten betätigen, merkt das als allererstes der IKBD. Dieser sendet nun den Tastaturcode über seine interne serielle Schnittstelle an die ACIA (s.u.), diese löst daraufhin bei dem “Interruptverwalter”, bei uns ein “MFP 68901”, einen Interrupt aus, der nach einer Prioritätenliste entscheidet, ob denn die CPU im Moment gestört werden darf. Das ist wichtig, da sonst z.B. während eines Floppy-Zugriffes dessen getimter Ablauf gestört werden könnte. Sind alle Hindernisse beseitigt, wird bei der CPU der endgültige Interrupt ausgelöst. Jetzt unterbricht diese also das momentane Programm und holt sich das Zeichen über ein spezielles Interrupt-Programm (welches auch den Tastaturmatrix-Scancode in den ASCII-Code verwandelt und den bekannten “Tastaturklick” erzeugt) vom Datenregister der Tastatur-ACIA in den Tastaturpuffer im RAM. Hier kann der User jetzt den ASCII-Code der gedrückten Taste über die bestehenden GEMDOS-Routinen des TOS holen. Übrigens sendet der IKBD nach jeder Mausbewegung ein ganzes Paket an Koordinaten und Daten auf diese Art und Weise indirekt an die CPU. Soweit also die Daseinsberechtigung des IKBD.
Jetzt habe ich hoffentlich auch noch die letzten 1040er-, 520er- und 260er-User neugierig gemacht, deshalb geht es jetzt ans Eingemachte.
Für die direkte Programmierung des IKBD ist die interne, asynchron (d.h. ohne Synchronisierungssignal) arbeitende serielle Schnittstelle der Tastatur-ACIA (6850) im ST zuständig. Diese besitzt zwei Register, das Controlregister bei $fffc00 und das Datenregister bei $fffc02. Wer hierzu Genaueres wissen möchte, sei an das ST-Profibuch von Sybex verwiesen; eine genauere Erläuterung würde hier viel zu weit führen.
Viel bequemer sind doch die Möglichkeiten, die uns das XBIOS bietet. Die dort vorhandenen Funktionen XBIOS 22 (SETTIME) und XBIOS 23 (GETTIME) nehmen mir um ein Haar die ganze Arbeit ab. Sie haben aber bekanntlich den GEMDOS-Haken, daß sie nur gerade Sekunden verarbeiten. Diese Verschwendung des Sekundentaktes umgehe ich einfach durch die eigene Programmierung dieser Routinen.
Der IKBD stellt uns durch die Funktionen $1b (Uhrzeit stellen) und $1c (Uhrzeit holen) alles Nötige zur Verfügung. Natürlich ist auch hier eine illegale Methode möglich : Die Funktionen $20 (RAM beschreiben) und $21 (RAM lesen) ermöglichen dasselbe, da das Datum im internen RAM des IKBD immer in Speicherzelle $82..$84 und die Zeit in $85..$87 zwischengespeichert wird. Die beiden legalen Funktionen machen letztendlich auch gar nichts anderes, als die genannten Speicherstellen im IKBD-RAM zu modifizieren bzw. auszulesen.
Übrigens empfiehlt es sich nicht, mit der Funktion $20 zu spielen, da die gesamten 128 Bytes des IKBD-RAMs ($0080..$00ff) so ziemlich komplett für Variablen und den Stack verbraucht werden. Das Betriebssystem befindet sich im ROM bei $f000..$ff6d (bzw. $ffed), die Interrupt-Vektoren bei $ffee..$ffff, interne Register (Timer, Sende- und Empfangsdaten,...) bei $0000..$001f. Die übrigen Speicherbereiche sind im aktiven Modus 7 nicht benutzbar und bringen bei einem Leseversuch den Wert $ff. Für die übrigen Modi (0..6) wiederum ist das Betriebssystem des IKBD nicht ausgelegt.
Die Funktion XBIOS 34 (KBDVBASE) gibt uns nach ihrem Aufruf einen Pointer (Basisadresse) auf die Startadressen der Tastatur-Interruptvektoren zurück. Das Betriebssystem des ATARI unterscheidet nämlich zwischen einem einfachen Tastendruck oder einer Mausbewegung oder Joystickmeldung oder...
Für jede Quelle, die an den IKBD angeschlossen ist, gibt es in der Tabelle der Vektoren eine eigene Interrupt-Routinenadresse (s. Tabelle 1).
Vielleicht ist noch zu erwähnen, daß der ATARI ST zwei ACIAs besitzt, eine für MIDI, die andere für den IKBD. Beide sind hardwaremäßig absolut identisch, erfüllen auch denselben Zweck:
Umformung der anliegenden 8-Bit-parallelen Daten in serielle asynchrone Übertragung und umgekehrt. Lediglich die serielle Übertragungsgeschwindigkeit beträgt genormte 31250 Baud (Bit/s) bei MIDI und 7812.5 Baud beim IKBD.
Midisys und ikbdsys sind die beiden Routinen, die direkt nach einem Ereignis der MIDI-ACIA bzw. der IKBD-ACIA aufgerufen werden. Uns sollen aber nur die Wege der für den IKBD zuständigen ACIA interessieren, da MIDI den Rahmen dieses Artikels sprengen würde.
Basisadresse | + 0: | enthält Adresse midivec | für Midi-Routine |
---|---|---|---|
+ 4: | vkbderr | Tastatur-Error | |
+ 8: | vmiderr | MIDI-Error | |
+ 12: | statvec | Status-Routine | |
+ 16: | mousevec | Maus-Routinen | |
+ 20: | clockvec | Uhrzeit-Routine | |
+ 24: | joyvec | Joystick-Routinen | |
+ 28: | midisys | MIDI-Systemvektor | |
+ 32: | ikbdsys | Tastatur-Systemvektor |
Tabelle 1: Für jede Quelle, die an den IKBD angeschlossen ist, gibt es in der Tabelle der Vektoren eine eigene Interrupt-Routinenadresse.
Da der IKBD jedes Vorkommnis in seinem Arbeitsbereich sofort meldet, würde ein unüberschaubarer Datensalat entstehen. Keine Maschine und erst recht kein Mensch könnte erkennen, ob jetzt gerade die Maus dran war oder einer der beiden Joysticks eine Bewegung abgab, eine Taste gedrückt oder vielleicht die aktuelle Uhrzeit gesendet wurde. Deshalb ist das Betriebssystem des IKBD so programmiert, daß immer vor einer Meldung ein sog. Header gesendet wird.
Die Interrupt-Routine in ikbdsys verwaltet dann auch die gesendeten Pakete (Kette von gesendeten Datenbytes plus Header) und verzweigt in die entsprechenden, oben aufgeführten Unterroutinen. Eine solche Unterroutine darf aber nicht länger als 1 Millisekunde dauern und muß mit dem Assembler-Mnemonic RTS (ReTurn from Subroutine) abgeschlossen sein.
In unserem Falle soll ja eine eigene Interrupt-Routine geschrieben werden, deshalb muß der Uhrzeitvektor in der obigen Tabelle auf unsere eigene Routine “umgebogen” werden. Dieser Vektor zeigt bekanntlich auf die verschwenderische 2-Sekundentaktroutine der Funktion XBIOS 23 (GETTIME).
Also, equal goes it loose: Die Adresse, auf die nach einer Uhrzeitanforderung verzweigt werden soll, steht ja bekanntlich in clockvec, also 20 Bytes über der Basisadresse, die uns der Aufruf von XBIOS 34 (KBDVBASE) zurückgegeben hat. Wenn wir jetzt diesen Vektor auf unsere eigene Routine umbiegen, würde sie bei einer Uhrzeitanforderung auch ausgeführt werden...
Der aufmerksame Leser hat sich längst gefragt: Wie mache ich denn so eine Anforderung?
Ganz einfach mit der Funktion XBIOS 25 (IKBDWS). Diese verlangt die Adresse des Strings und auch die Anzahl der Bytes-1, die gesendet werden sollen. “Bytes-1” deshalb, weil, wie der eingeweihte Assembler-Programmierer weiß, eine “dbf”-Schleife verwendet wird. Diese dekrementiert ein CPU-internes Register solange, bis dessen Inhalt kleiner als Null ist. Erst dann wird die Schleife verlassen. Wenn also ein Byte gesendet werden soll, muß eine Null übergeben werden.
Ja, aber was soll ich denn wann senden? Tja, die Auflistung der erlaubten und dokumentierten Befehle an den IKBD findet man z.B. imo.g. ST-Profibuch oder den ähnlichen Systemfachbüchern zum ATARI ST. Ich möchte mich neben den schon erwähnten Befehlen $20 und $21 auf die Befehle $1b (Uhrzeit stellen) und $1c (Uhrzeit holen) beschränken. Der Befehl $1b, Uhrzeit stellen, ist sehr einfach aufgebaut. Die Zeit wird immer als gepackte BCD-Zahl gesendet. Eine gepackte BCD-Zahl ist eine im Hexadezimalsystem dargestellte Dezimalzahl, z.B. für (dez 29) kommt dann $29 (= dez 41). Klar? Wenn nicht, dann nochmal lesen.
Gesendet wird zuerst der Header $1b, dann ein gepacktes BCD-Byte als Jahreszahl (z.B. $89 für 1989), ein Byte Monat (z.B. $01 für Januar), ein Byte Tag (z.B. $31 für den 31.), ein Byte Stunde (z.B. $22 für 22 Uhr), ein Byte Minute (z.B. $39 für 39.Minute) und ein Byte Sekunde (z.B. $51 für 51.Sekunde). Der ganze String sähe also wie folgt aus :
setdata dc.b $1b,$89,$01,$31,$22,$39,$51
Nibbles (Halbbytes), die keine Dezimalzahlen (0..9) enthalten, also z.B. $8a für die Jahreszahl, werden vom IKBD ignoriert. In diesem Fall wird demnach nur die 8 gesetzt, der andere Wert bleibt erhalten. Nach jedem Kaltstart des IKBD enthält die Uhr nur Nullen; also wird die Jahreszahl $80 eingetragen. Übrigens startet die IKBD-Uhr leider erst dann, wenn sie einmal gestellt wurde!
Das ganze Programm zum Senden der obigen Uhrzeit sähe dann so aus (die Zeilen 210..212 im Listing bewirken genau dasselbe):
settime pea setdata(pc) Stringadr. auf Stack
move.w #6,-(a7) Anzahl Bytes-1
move.w #25,-(a7) XBIOS-Funktion 25..
trap #14 ..ausführen
addq.l #8,a7 Stack korrigieren
So, jetzt wollen wir nur noch die Uhrzeit lesen; sonst wäre ja alles umsonst gewesen. Dazu müssen wir die Clock-Interrupt-Routine schreiben, also die Routine, die nach einer Uhrzeitanforderung über den indirekten Interrupt des IKBD angesprungen wird. Sie könnte so aussehen (Zeilen 405..409 im Listing):
clock moveq #5,d0 (5+1 )-Bytes sollen
gelesen werden
lea leseuhr(pc),a1 a1 zeigt auf reser-
vierten Speicherplatz
repeat move.b(a0)+,(a1)+ Inhalt von a0 -> Inhalt von a1 kopieren
dbf d0,repeat Schleife ausführen.,
rts ..und mit “RTS” abschließen
section bss
save_old ds.l 1 Speicher für alten Vektor (s.u.)
leseuhr ds.b 6 6 Bytes im Speiche (BSS) reservieren,
Clock-Header wird nicht mitkopiert!
Die Interrupt-Routine wäre fertig, jetzt muß nur noch der zuständige Vektor “umgebogen” werden, um bei einem Interrupt auch auf die obige Routine zu springen (ab Zeile 16 im Listing):
vektor move.w #34,-(a7) ACIA-Vektorfeld.,
trap #14 ..holen
addq.l #2,a7 Stack korrigieren
move.l d0,a0 Basisadresse nach a0
lea save_old(pc),a1 Speicher für alten Vektor
move.l 20(a0),(a1) alten Vektor im Speicher sichern
lea clock(pc),a2 obige Interrupt-Routine..
move.l a2,20(a0) ..wird jetzt installiert
Das wär’s... Nach jedem Senden von $1c, halt Moment, das geht etwa so (Zeilen 363..367 im Listing):
gettime pea getdata Stringadr. auf Stack
clr.w -(a7) Anzahl zu sendender
Bytes-1 = 0
move #25,-(a7) XBIOS25..
trap #14 ..ausführen
addq.l #8,a7 Stack korrigieren
section data
getdata dc.b $1c Das soll gesendet werden
Jetzt wird nach jedem Senden von $1c die aktuelle Uhrzeit in den Speicher ab leseuhr geschrieben; logischerweise im gleichen gepackten BCD-Format wie beim Stellen der Uhrzeit.
Aber leider stört uns ja noch eines : Wir wissen nicht, wann der Interrupt auftreten wird. Eine zu früh erfolgte Abfrage von leseuhr bringt entweder ein total falsches Ergebnis oder die vorherige Uhrzeit, je nachdem, was im reservierten Speicher stand. Eine zu späte Abfrage (mit riesigem Zeit-Sicherheitsabstand) führt uns auch nicht weiter, denn das kostet unnötig verbrauchte Rechenzeit. Aber wie für (fast) alles im Soft- und Hardwarebereich gibt es auch hier einen kleinen Trick: Man schreibt z.B. an die Stelle des Tags in “leseuhr” einen Wert, der niemals als Tag auftreten kann, z.B. ein $ff. Eine ständige Abfrage dieses $ff nach dem Anfordern der neuen Zeit (s. gettime) wird zwangsweise so lange durchgeführt, bis ein anderer Wert vom Interrupt eingetragen ist. Hierbei ist es gleich, welches Byte von leseuhr abgefragt wird, da die neue Uhrzeit immer in einem Interrupt eingetragen und somit immer komplett kopiert wird. Erst danach fährt die CPU mit dem normalen Programm fort. Somit haben wir genau SO lange gewartet, wie es nötig war.
Das nachfolgende Programm, komplett auf dem DevpacST-Assembler V2.0 von HiSoft (Markt & Technik) geschrieben, zeigt eine mögliche Realisierung des oben beschriebenen Zusammenhangs; eine einführende Beschreibung wurde schon ganz am Anfang des Artikels gemacht. Eine Anpassung an die Syntax anderer Assembler dürfte keine Probleme bereiten, sofern diese den vollen MOTOROLA-Befehlssatz unterstützen. Wenn nicht die Längen “.B” oder “.L” angegeben wurden, handelt es sich um “.W” (Wortlänge). Nur noch wenige Assembler verlangen auch die (Standard-) Angabe der Wortlänge. Die ausführliche Dokumentierung dürfte das Programm auch dem Einsteiger verständlich machen, obwohl es so kurz wie möglich geschrieben wurde und darunter bestimmt die Modularität leiden mußte. Das Programm läuft schon seit einiger Zeit fehlerfrei auf verschiedenen Rechnern; da nur legale Adressen und Aufrufe benutzt werden, gibt es keine Komplikationen zwischen altem TOS und Blitter-TOS. Sollten die folgenden Betriebssysteme für den ST immer noch nicht die IKBD-Uhr unterstützen, so läuft das Programm mit Sicherheit auch auf diesen Versionen.
***************************************************
* Dieses Programm aktiviert die Tastaturprozessor-*
* uhr nach einem Kaltstart, um dann z.Eingabe der *
* aktuellen Uhrzeit aufzufordern. Das Programm *
* erkennt selbständig, ob d. Tastaturuhrzeit noch *
* gestellt werden muss, oder sie schon aktiviert *
* ist. Im letzten Fall wird die laufende Uhr auch *
* angezeigt, stellen ist aber auch hier möglich. *
* (c) Sieghard Schäfer, bis 22.Oktober 1988 *
* (auf DevpacST-Assembler) *
***************************************************
move.l 4(a7),a0 Basepage
move.l $c(a0),d0 Text
add.l $1c(a0),d0 BSS
add.l #$100,d0 Basepage
move.l d0,-(a7) Länge
move.l a0,-(a7) Adresse
clr.w -(a7) Dummy
move.w #$4a,-(a7) SETBLOCK
trap #1
move.w #34,-(a7) KBDVBASE
trap #14 ACIA-Vektorfeld..
move.l d0,a0 ..Basis-Adr. in a0
lea basis(pc),a1
move.l a0,(a1) Basis sichern
lea altv(pc),a1 alten Clock-Vektor.,
move.l 20(a0),(a1) ..sichern
lea keyin(pc),a1 eig. Clock-Vektor.,
move.l a1,20(a0) ..übernehmen
*********************************************
move.w #4,-(a7) GETREZ
trap #14 Auflösung testen
lea 16(a7),a7 kompl. Stack korrig.
lea color(pc),a0 ursprüngl. Auflösung.,
move.w d0,(a0) ..nach 'color'
btst #1,d0 Monochrom ?
bne.s mono
*********************************************
moveq #1,d0 nein, dann Medium.,
bsr res ..einstellen
moveq #3,d1 Farbe aus Palette 3..
bsr get_pal ..holen,..
lea farbe(pc),a0
move.w d0,(a0) ..in 'farbe' sichern
clr.w d1 Wert aus Palette 0..
bsr get_pal ..holen und..
eor.w #$777,d0 ..EXOR in.,
moveq #3,d1 ..Palette 3..
bsr set_pal ..schreiben
*********************************************
mono bsr clock $1c (Uhrabfrage senden)
lea flag(pc),a0 Flag (Zeit gestellt?)
move.w #$1000,d0 Warteschleife
time tst.b 2(a6) Interrupt bearbeitet?
bne.s lauft ja, Bildschirm aufbauen
dbf d0,time nein, weiter warten
move.w #-1,(a0) Flag setzen
moveq #2,d0 Uhrzeit m. Wert aus..
in_time move.w -8(a6),(a6)+ ..'save' setzen + ins..
dbf d0,in_time ..Sendereg. kopieren
subq.l #6,a6 a6 wieder korrigieren
bsr prozess Datum & Uhr > Prozessor
bra.s na_ja Bildschirm aufbauen
lauft clr.w (a0) Flag löschen
na_ja lea init(pc),a0 VT52-Initialis.
bsr text ..ausführen
bsr star Sterne oben zeichnen
lea text1(pc),a0 Text1..
bsr text ..schreiben
moveq #30,d7 31-mal..
moveq #-1,d0 ..Zeichen $ff..
bsr print ..schreiben
lea text2(pc),a0 Text2..
bsr text ..schreiben
bsr ausgabe komplette Zeitausgabe
lea text4(pc),a0 Text4..
bsr text ..schreiben
bsr star Sterne unten zeichnen
lea flag(pc),a0
tst.w (a0) läuft Uhrzeit schon ?
bne.s weiter nein, dann 'weiter'
bra direkt sonst laufend anzeigen
*********************************************
nochmal lea err_txt(pc),a0
bsr text 'errortxt' schreiben
lea ding(pc),a0
moveq #-1,d5 Fehlerflag setzen
clr.w d5 unteres Wort löschen
bra.s tune 'Ding'-Ton ausgeben
weiter lea cursor(pc),a0 Cursor a.Anfang
clr.l d5 Fehlerflag ganz löschen
tune bsr text
*********************************************
lea syntax(pc),a5 Syntax-Check-POINTER
lea store(pc),a4 Zw.-speicher-POINTER..
moveq #-1,d0 \ ..zuerst.,
move.l d0,(a4) | ..mit 'f'..
move.w d0,4(a4) / ..auffüllen
keystrt moveq #2,d6 je 3 Datenbytes
key clr.w d4 Eingabebyte löschen
moveq #1,d7 2 Nibble-Hälften
keypaar bsr tast auf Taste warten
cmp.b #$d,d0 [RETURN]?
bne.s no_ret
tst.w d7 ja, dann aber.,
beq.s keypaar ..nur nach vollem Byte
lea store(pc),a0
tst.l d5 Fehler gewesen ?
bmi.s no_set ja, neue Zeit ignorier.
bne ende raus während Stellen!
lea syntax+6(pc),a5 Syntax-POINTER und.,
lea store+3(pc),a4 ..Store-POINTER auf..
lea u_point(pc),a0 ..'ZEIT '-Stellen
bsr text Cursor auf ZEIT
move.w #-1,d5 zweiten Durchgang.,
bra.s keystrt ..für ZEIT ausführen
no_set moveq #2,d0
copy move.w -8(a0),(a0)+alte Daten übernehmen.,
dbf d0,copy
bra direkt ..und laufend anzeigen
no_ret cmp.b #$30,d0 Taste <0?
bcs.s keypaar dann ignorieren!
cmp.b #$39,d0 Taste >9?
bhi.s keypaar dann auch ignorieren!
move.w d0,d1 nach d1 kopieren,.,
and.b #$f,d0 ..nur unteres Nibble!
tst.w d7 schon 2.Durchgang?
beq.s take_it ja -> Sprung 'take_it'
lsl.w #4,d0 sonst oberes Nibble
take_it or.b d0,d4 ob./unt. Nibble in d4
sub.b #$20,d1 Taste-32 ->Dig.Ziffer..
move.w d1,d0 ..nach d0 kopieren.,
bsr out ..und ausgeben
dbf d7,keypaar Immer 2 Tasten holen
cmp.b #$12,l(a5) Monatseingabe?
bne.s nomonth
cmp.b #2,d4 ja, dann Februar?
bne.s other
cmp.b #$29,-1(a4) ja, >29.Februar?
bhi nochmal ja, Fehler !
other cmp.b #8,d4 sonst : >=August?
bcc.s august nein, dann.,
btst #0,d4 ..ab Jan. gerad. Monat?
bne.s nomonth ungerade, alles ok!
bra.s day_30 gerade
august btst #0,d4 ..ab Aug. gerad. Monat?
beq.s nomonth ungerade, alles ok
day_30 cmp.b #$31,-1(a4) der 31.im 30Tage-Monat?
beq nochmal nochmal!!!
nomonth cmp.b #$99,1(a5) Jahreseingabe?
bne.s no_year
cmp.b #2,-1(a4) ja, Februar?
bne.s no_year
bsr bcd_hex ja, in hex umwandeln
divu #4,d0 Schaltjahr berechnen
swap d0
tst.w d0 Schaltjahr?
bne.s test_28
move.b #$29,d1 ja, 29.2 erlaubt!
bra.s testfeb
test_28 move.b #$28,d1 nein, dann nur 28.2.!
testfeb cmp.b -2(a4),d1 Festlegung beachtet?
bcs nochmal nein, Fehler!!!
no_year cmp.b (a5),d4 unt.Grenze d. Eingabe.,
bcs error ..darunter? -> Fehler!
cmp.b 1(a5),d4 über oberer Grenze ?
bhi error ja, Fehler!!!
move.b d4,(a4)+ in STORE abspeichern,.,
add.l #2,a5 ..SYNTAX-POINTER +2
lea esc_c(pc),a0
bsr text Ein Zeichen nach rechts
dbf d6,key nächste Tastenfolge
tst.w d5 Schon mal hier gewesen?
bne.s ende
lea space(pc),a0
bsr text nein, 'space' einfügen
move.w #-1,d5 Flag setzen
bra keystrt noch mal von vorne!
**********************************************
prozess bsr tausch Jahr & Tag vertauschen
pea (a0)
pea $190006 IKBDWS
trap #14 Neue Daten an Prozessor
addq.l #8,a7
rts
**********************************************
ende bsr.s prozess Daten an Tastaturproz.
direkt lea ok_text(pc),a0
bsr text 'ok_text' schreiben
prt_uhr bsr clock Uhrzeit abfragen
wait tst.b 2(a6)
beq.s wait Uhrzeit empfangen?
bsr ausgabe ja, kompl.Stringausgabe
move.w #$b,-(a7) CONSTAT
trap #1
addq.l #2,a7
tst.w d0 Tastaturpuffer leer?
beq.s prt_uhr
bsr tast nein, dann Taste holen
cmp.b #$d,d0 [RETURN]?
beq.s fin ja, -> fertig
or.b #$20,d0 sonst vielleicht..
cmp.b #'j',d0 ..'j' oder 'J'?
beq.s fin ja, -> fertig
lea ok_cls(pc),a0
bsr text sonst Eingabe..
bra weiter ..wiederholen!
fin bsr tausch Tag & Jahr vertauschen
clr.l d7 Datum-Register löschen
addq.l #1,a0 soll auf Store zeigen
move.l a0,a6 nach a6 kopieren
move.b (a6)+,d4 Jahr holen
moveq #$80,d1 Jahres-Offset in d1
sbcd.b d1,d4 Jahres-Offset abziehen
bsr.s bcd_hex d4<bcd> -> d0<hex>
or.w d0,d7 ..abspeichern,..
lsl.w #4,d7 ..nach links schieben
bsr.s read_bh Monat
or.w d0,d7
lsl.w #5,d7
bsr.s read_bh Tag
or.w d0,d7
move.w d7,-(a7)
move.w #$2b,-(a7) SET DATE
trap #1 Datum an GEMDOS
clr.l d7 Zeit-Register löschen
bsr.s read_bh Stunde
or.w d0,d7
lsl.w #6,d7
bsr.s read_bh Minute
or.w d0,d7
lsl.w #5,d7
bsr.s read_bh Sekunde
lsr.b #1,d0 geteilt durch 2 (...)
or.w d0,d7
move.w d7,-(a7)
move.w #$2d,-(a7) SET TIME
trap #1 Zeit an GEMDOS
addq.l #8,a7 Stack korrigieren
move.l basis(pc),a0
move.l altv(pc),20(a0) alt. Clockvektor zurück
move.w color(pc),d0
btst #2,d0 vorher HIGH-Resolution?
bne.s not_col
bsr res nein, dann..
move.w farbe(pc),d0
moveq #3,d1 ..alt.Wert v.Palette3..
bsr set_pal ..zurückschreiben
not_col lea cls(pc),a0
bsr.s text Bildschirm-Cls
clr.w -(a7) TERM
trap #1 SCHLUSS
*********************************************
error lea err_txt(pc),a0
bsr.s text 'errortxt' schreiben
lea links(pc),a0
bsr.s text zwei Zeichen nach links
or.l #$ffff0000,d5 Flag für ERROR setzen
bra key Eingabe wiederholen
* - - - - - - - - - - - - - - - - - - - - - - - -
read_bh move.b (a6)+,d4 Nächstes Zeichen lesen
bcd_hex clr.l d0
move.b d4,d0 Umrechnung v. BCD ins.,
move.b d4,d1
lsr.b #4,d0 ..Hexadezimal-Format
mulu #10,d0 Benötigt wird das.,
and.b #$f,d1 ..BCD in d4,hex kommt.,
add.b d1,d0 ..kommt dann in d0
rts
*********************************************
text move.l a0,-(a7)
move.w #9,-(a7) PRINT LINE
trap #1 Textstring ausgeben
addq.l #6,a7
rts
*********************************************
tausch lea code+1(pc),a0 Hier wird das..
move.b 3(a0),d0 ..USA-Format des 6301..
move.b 1(a0),3(a0) ..dem deutschen..
move.b d0,1(a0) ..GEMDOS angepasst
rts
*********************************************
star moveq #42,d7
moveq #'*',d0 Sterne schreiben
print move.l d0,-(a7) d0 retten
bsr.s out Zeichen ausgeben
move.l (a7)+,d0 d0 zurück
dbf d7,print Druckt (d7+1)Zeichen d0
rts
********************************************
outcomp add.w #$10,d0 Offset f.Computerzahlen
out move.w d0,-(a7)
pea $30005 BCONOUT
trap #13 Druckt Zeichen d0
addq.l #6,a7
rts
*********************************************
outbyte clr.w d0
move.b (a6)+,d0 Byte holen und..
move.w d0,d7 ..kopieren
lsr.b #4,d0 High-Nibble
bsr.s outcomp
and.w #$f,d7 Low-Nibble
move.w d7,d0
bra.s outcomp Byte schreiben
*********************************************
tast move.w #8,-(a7) CONIN WITHOUT ECHO
trap #1 Tasten-Wert in d0
addq.l #2,a7
rts
*********************************************
clock lea store(pc),a6
clr.b 2(a6) Spezial-Dummy setzen
pea ask_uhr(pc) $1c
clr.w -(a7)
move.w #25,-(a7) IKBDWS
trap #14 Uhrzeit holen
addq.l #8,a7
rts
*********************************************
ausgabe bsr.s tausch Tag & Jahr vertauschen
lea cursor1(pc),a0
bsr.s text Cursor richten
bsr.s outbyte Tag ausgeben
moveq #’.',d0
bsr.s out . ausgeben
bsr.s outbyte Monat ausgeben
moveq #'.',d0
bsr.s out . ausgeben
bsr.s outbyte Jahr ausgeben
moveq #2,d7
moveq #' ',d0
bsr.s print 3 * Space
bsr.s outbyte Stunde ausgeben
moveq #':',d0
bsr.s out : ausgeben
bsr.s outbyte Minute ausgeben
moveq #':',d0
bsr.s out : ausgeben
bsr.s outbyte Sekunde ausgeben
rts
*********************************************
res move.w d0,-(a7)
moveq #-1,d0
move.l d0,-(a7)
move.l d0,-(a7)
move.w #5,-(a7) SETSCREEN
trap #14 für Bildschirmauflösung
lea 12(a7),a7
rts
*********************************************
get_pal moveq #-1,d0 (abfragen der..)
set_pal move.w d0,-(a7) (setzen der..)
move.w d1,-(a7) ..d1-Palette, d0-Farbe
move.w #7,-(a7) SETCOLOR
trap #14
addq.l #6,a7
rts
*********************************************
keyin moveq #5,d0 Das ist die neue
lea store(pc),a1 Routine, die vom
repeat move.b (a0)+,(a1)+ Tast.-proz. Daten-
dbf d0,repeat pakete empfängt und in
rts d.Puffer (a1)+ schreibt
*********************************************
syntax dc.w $0131,$0112,$0099,$0023,$0059,$0059
init dc.b 27,'E',27,'f',27,'Y',41,50,0
text1 dc.b 27,'Y',42,50,'*',27,'Y',42,92,'*'
dc.b 27,'Y',43,50,'*',27,'Y',43,56,'Bitte '
dc.b 'Datum und Uhrzeit stellen',27,'Y',43
dc.b 92,'*',27,'Y',44,50,'*',27,'Y',44,56,0
text2 dc.b 27,'Y',44,92,'*',27,'Y',45,50,'*'
dc.b 27,'Y',45,57,'6301->GEMDOS-Uhr '
dc.b 189,' S.Schäfer',27,'Y',45,92,'*',27
dc.b 'Y',46,50,'*',27,'Y',46,56,'---> ',0
text4 dc.b ' <—-',27,'Y',46,92,'*',27,'Y',47,50
dc.b '*',27,'Y',47,92,'*',27,'Y',48,50,0
err_txt dc.b 27,'j',27,'f',27,'Y',47,67
dc.b 27, 'p ERROR ! ' ,27, 'q',27, 'k',0
ding dc.b 7
cursor dc.b 27,'e'
cursor1 dc.b 27,'Y',46,62,0
space dc.b 27,'C'
esc_c dc.b 27,'C',0
links dc.b 7,27,'e',27,'D',27,'D',0
ok_text dc.b 27,'f',27,'Y',47,67,27,'pOK (j/n)?'
dc.b 27,'q',0
ok_cls dc.b 27,'e',27,'Y',47,67,' ',0
u_point dc.b 27,'Y',46,73,0
cls dc.b 27,'E',0
ask_uhr dc.b $1c
even
save dc.w $2210,$8812,$0000
code dc.w $001b
store dsbss.w 3
basis dsbss.l 1
altv dsbss.l 1
flag dsbss.w 1
color dsbss.w 1
farbe dsbss.w 1