Nur wenige Probleme lassen sich arithmetisch so schwer in den Griff kriegen wie die Verwaltung von Kalenderdaten. Mit den unregelmäßigen Monatslängen und den Schaltjahren arten entsprechende Routinen leicht in seitenlange IF-Abfragen und Additionsreihen aus. Es geht einfacher, wenn man ein paar Tricks kennt. Wir liefern Ihnen Routinen, mit denen Sie eine komplette Datumsverwaltung in Ihr Programm integrieren. Die Beispiele sind in GFA-Basic gehalten, jedoch ist es kein Problem, sie auf andere Programmiersprachen umzusetzen. Das Programm mit allen Routinen finden Sie auf der TOS-Diskette.
Betrachten wir die Vorgehensweise der einzelnen Funktionen. Beginnen wir mit der Prozedur »TAG.NR«. Sie berechnet, am wievielten Tag im Jahr ein bestimmtes Datum zutrifft. Sie rufen die Prozedur mit
@tnr(tag%,monat%,jahr%,tnr%)
auf, wobei Sie in den ersten drei Variablen das jeweilige Datum übergeben. Ob Sie das Jahr vierstellig (1990) oder zweistellig (90) angeben, spielt dabei keine Rolle. In der letzten Variablen tnr% liefert die Prozedur das Ergebnis zurück - das ist ein Wert zwischen 1 und 365 (366 bei Schaltjahren). Letzteres ist übrigens ein Sonderfall, der vorliegt, wenn die Jahreszahl durch 4 teilbar ist, und wird entsprechend abgefragt.
Die Prozedur »TAG.ZAHL« macht im großen und ganzen das gleiche - mit einem entscheidenden Unterschied: Sie gibt die Tageszahl nicht relativ zum Jahresanfang aus, sondern liefert einen Wert zurück, dessen Zählbeginn sich auf den 1. März des Jahres 0 bezieht.
Dieser Wert heißt auch julianisches Datum. Daten vor dem 1. März 0 bekommen ein negatives Vorzeichen -achten Sie darauf, wenn Sie den entsprechenden Wert in Ihrem eigenen Programm auswerten. Beispielsweise funktioniert die Modulo-Funktion(mod) nur bei positiven Zahlen richtig; ich habe deshalb auf ihre Verwendung in diesen Routinen bewußt verzichtet. TAG.ZAHL beachtet übrigens automatisch die Schaltjahre - ein Vorteil der etwas komplizierten Formel, deren ausführliche Erläuterung ich mir hier sparen möchte. Beachten Sie dabei, daß Sie das Datum korrekt vierstellig angeben - zwischen dem 1. April 90 und dem 1. April 1990 liegen 19 Jahrhunderte.
Mit einem absoluten Datumswert in einer einzigen Zahl läßt sich eine Menge anfangen - deshalb benötigen wir TAG.ZAHL auch in allen folgenden Routinen. Wissen Sie eigentlich, an welchem Wochentag Sie geboren wurden? Oder auf welchen Tag Weihnachten im Jahr 2000 fällt? Die Prozedur WELCHER.TAG sagt es Ihnen. Sie übergeben wieder das Datum getrennt nach Tag, Monat und Jahr, und in t$ liefert die Routine den Wochentag zurück. Dabei bedient man sich eines Tricks: Wir nehmen ein Orientierungsdatum, dessen Wochentag bekannt ist. Beispielsweise war der 9.10.1970 ein Freitag. Jetzt müssen wir nur noch bestimmen, wieviel Tage seit diesem Datum positiv oder negativ vergangen sind. Das teilen wir durch 7 (für die sieben Wochentage) und zählen zum Rest 5 dazu (denn der Orientierungstag war ein Freitag). Das Ergebnis ist der gewünschte Wochentag.
Interessant ist auch, zu einem Datum eine gewisse Anzahl Tage zu addieren: Der wievielte genau ist heute in drei Wochen? Oder in 90 Tagen, wenn die Garantie für Ihren neuen Joystick ausläuft?
Diese Aufgabe übernimmt die Prozedur »DAT.RECHNEN«. Ihr übergeben Sie zunächst das Ausgangsdatum sowie den Wert, den Sie hinzuaddieren möchten. Die Prozedur ruft TAG.ZAHL auf, um den absoluten Wert des Datums zu bestimmen, und zählt den Wert a% dazu. Das Subtrahieren des Wertes erreichen Sie durch ein negatives a% (welches Datum war heute vor 14 Tagen?). Nun möchten wir das Ergebnis aber auch wieder als Kalenderdatum vorliegen haben - hierzu dient »RECALC«. Dies ist die Umkehrfunktion von TAG.ZAHL. Das Ergebnis steht dann am Funktionsende in t2%, m2% und j2%.
Mit diesen Routinen haben Sie einige mächtige Werkzeuge, wenn es darum geht, Kalenderdaten rechnerisch zu erfassen. Tiefergehende Anwendungen bauen Sie auf diesem Grundgerüst auf. Die Einsatzgebiete sind dabei nicht nur auf den traditionellen Biorhythmus beschränkt. (Marc Kowalsky/ah)
Um Programme unabhängig von der Bildschirmauflösung zu schreiben, ist es erforderlich, diese zunächst zu ermitteln. Aufschluß über die aktuelle Bildschirmauflösung gibt die Xbios-Funktion »Getrez«. Diese Funktion liefert auf einem ST die Werte 0 bis 2 für die geringe, mittlere und hohe Auflösung. Werte größer 2 sind für weitere Auflösungen, wie z. B. die des TT reserviert.
Aufloesung= Xbios(4)
(ah)
REM
REM *** Auflösung über Xbios 4 (Getrez) ***
REM
aufloesung=XBIOS(4)
REM
REM *** Ermitteln von Kbshift (Sondertastenstaus)
REM
IF PEEK(&HFC001B)=134
kbshift=&HE1B
ELSE
sysbase=LPEEK(&H4F2)
sysbase=sysbase+36
kbshift=LPEEK(sysbase)
ENDIF
Für den Atari gibt es bereits eine ganze Reihe von Programmen, die über Alternate Help den aktuellen Bildschirm speichern. Viele Produkte blockieren jedoch diese Tastenkombination. Um einen Screenshot trotzdem auf eine Diskette oder Festplatte zu bringen, bedienen wir uns einfach einer anderen Tastenkombination. Hierzu eignen sich die Sondertasten Shift, Control, Alternate und Caps Lock besonders gut. Der Atari verfügt über 1 Byte im Speicher, das den aktuellen Sondertastenstatus (»kbshift«) angibt. Offiziell ist die genaue Position dieses Bytes erst seit TOS 1.2 dokumentiert. Dort finden wir es über einen Zeiger im Betriebssystemheader an Adresse $24. Mit
movea.l $4f2,a0 ; _sysbase
adda.l $24,a0 ; Zeiger auf kbshift
movea.l (a0),a0 ; Adresse nach a0
erhalten wir den korrekte Adresse in A0. Dies gilt jedoch nur für TOS-Versionen ab 1.2. Bei allen älteren Versionen liegt »kb_shift« bei Adresse $E1B.
cmpi .b #86,$fc001b ; 86er TOS ?
bne.s new_tos ; nein
move.l #$elb,a0 ; kbshift bei $e1b
Da der Zugriff auf die Adresse $FC001B beim STE und TT mit Bomben bestraft wird (I/O-Bereich), müssen wir zuvor die Identität des Computers klären. Bei allen Geräten der ST-Serie beginnt das Betriebssystem an der Adresse $FC0000, sofern kein RAM-TOS vorliegt. Ist der oben erhaltene Wert für »_sysbase« gleich $FC0000, handelt es sich um einen ST, und eine weitere Unterscheidung bezüglich der TOS-Version ist nötig. Eine Version zur Ermittlung von »Kbshift« in GFA-Basic zeigt Listing 2.
Die Hauptroutine läuft im VBL-Interrupt und prüft ständig den Sondertastenstatus. Jede Sondertaste wird einem Bit in diesem Statusbyte zugeordnet. Den genauen Aufbau zeigt Tabelle 1. Das Programm prüft die Tastenkombination Shift Shift Control, wobei es CapsLock vorher ausmaskiert. Problematisch ist der Aufruf von GEMDOS-Routinen innerhalb eines Interrupts. Um Fehler weitgehend auszuschließen, legen wir »etv_critic« auf das Ende der Routine, um bei Schreib- und Lesefehlern eine Alertbox zu umgehen. Trotzdem ist es ratsam, die Maus für die Zeit des Speicherns links liegen zu lassen. Je nach Computertyp (ST oder TT) speichert »DUMP_IT.PRG« 32000 oder 153600 Bytes. Als Pfad dient das Hauptverzeichnis des Laufwerks, von dem das Programm gestartet wurde. (ah)
REM *** KBSHIFT Adresse ermitteln
sysbase=LPEEK(&H4F2)
kbshift=LPEEK(sysbase+36)
IF sysbase=&HFC0000
IF PEEK(&HFC001B)=134
kbshift=&HE1B
ENDIF
ENDIF
PRINT kbshift
Listing 2. Den Status der Sondertasten in Basic ermitteln
Bitnummer | Taste |
---|---|
0 | Shift [rechts] |
1 | Shift [links] |
2 | Control |
3 | Alternate |
4 | CapsLock |
5 | Maustaste rechts |
6 | Maustaste links |
7 | reserviert [0] |
************************
* DUMP_IT *
************************
* Screen speichern mit *
* Shift+Shift+Control *
************************
* (C)'90 A.Hierstetter *
* für ICP-Verlag *
* T O S - Magazin *
************************
text
movea.l 4(sp),a6 ; Basepageadresse holen
movea.w #$0100+$0400,a5 ; Größe der Basepage + Stackgröße (1k)
adda.l 12(a6),a5 ; + Größe des TEXT-Segments
adda.l 20(a6),a5 ; + Größe des DATA-Segments
adda.l 28(a6),a5 ; + Größe des BSS-Segments
move.l a5,d1 ; = Gesamtlänge des Programms
and.w #$fffe,d1 ; Länge nun gerade
add.l a6,d1 ; + Programmstart
movea.l d1,sp ; Stack endet dort
move.l a5,-(sp) ; Programmlänge
move.l a6,-(sp) ; Adresse der Basepage
move.l #$4a0000,-(sp) ; Funktionsnummer + Dummy
trap #1 ; Mshrink(0,Basepageadr,Prglänge)
lea 12(sp),sp ; Nur noch den Stack korrigieren
move.w #$19,-(sp) ; Aktuelles Laufwerk holen
trap #1 ; Gemdos
addq.l #2,sp ; Stack aufräumen
addi.b #'A',d0
move.b d0,file_name ; In Pfad eintragen
pea install(pc) ; Installation im Supervisor
move.w #38,-(sp) ; SuperExec
trap #14 ; Xbios
addq.l #6,sp ; Stack aufräumen
clr.w -(sp) ; Keine Fehlermeldung
move.l a5,-(sp) ; Länge des Programms
move.w #$31,-(sp) ; Keep Process()
trap #1 ; Zurück zum Desktop
install:
movea.l $04f2,a0 ; Zeiger auf _sysbase
adda.l #$24,a0 ; Varibale auf kbsshift
move.l (a0),kbshift ; Variable sichern
cmpa.l #$fc0024,a0 ; STE oder TT ?
bne.s new_tos ; Ja, dann nicht auf altes TOS prüfen
cmpi.b #$86,$fc001b ; Für alte TOS-Versionen gilt:
bne.s new_tos ; kbshift bei $e1b
move.l #$0e1b,kbshift ;
new_tos:
movea.l $0456,a0 ; VBL-Queue
move.w $0454,d0 ; Anzahl der Einträge in Queue
subq.w #2,d0 ; 2 abziehen, da DBRA und
addq.l #4,a0 ; 1. Eintrag von AES belegt
loop:
tst.l (a0) ; Is was drin ???
beq.s entry ; Nein, dann schlagen wir zu
addq.l #4,a0 ; nächsten Eintrag anschauen
dbra d0,loop ; und zur Schleife
rts ; und zurück
entry:
move.l #vblneu,(a0) ; Neue Routine in Queue eintragen
rts ; und wieder zurück
vblneu:
movea.l kbshift(pc),a0 ; Sondertastenstatus holen
move.b (a0),d0 ; Inhalt nach D0
andi.b #239,d0 ; Caps ausmaskieren
cmpi.b #7,d0 ; Shift+Shift+Control ?
beq.s dump ; Ja, dann Bild speichern
rts ; Sonst weiter in der VBL-Queue
dump:
move.l $0404,error ; Critic_handle merken
move.l end_vbl(pc),$0404 ; und umlenken
clr.w -(sp) ; Lesen / Schreiben
pea file_name(pc) ; Filenamen auf Stack
move.w #$3c,-(sp) ; Fopen()
trap #1 ; Gemdos
addq.l #8,sp ; Stack aufräumen
tst.w d0 ; Fehler ?
bmi.s end_vbl ; Ja, dann sofort raus hier
move.w d0,handle ; Filehandle merken
addi.b #1,file_name+10 ; Buchstaben erhöhen
cmpi.b #91,file_name+10 ; >'Z' ?
bne.s continue ; Nein, dann normal weiter
move.b #65,file_name+10 ; wieder auf 'A' setzen
continue:
move.l #32000,d0
cmpi.b #2,$044c
ble.s do_it
move.l #153600,d0
do_it:
move.l $044e,-(sp) ; Bildschirmadresse auf Stack
move.l d0,-(sp) ; 32000 Bytes werden geschrieben
move.w handle(pc),-(sp) ; Filehandle
move.w #$40,-(sp) ; Fwrite()
trap #1 ; Gemdos
lea 12(sp),sp ; Stack und so weiter
move.w handle(pc),-(sp) ; Nochmal das Filehandle
move.w #$3e,-(sp) ; Fclose
trap #1 ; Gemdos
addq.l #4,sp ; Wieder das gleiche
end_vbl:
move.l error(pc),$0404 ; Critic_Handle zurückstzen
rts ; und weiter in Queue
data
file_name: dc.b 0,':\'
file: dc.b 'PICTUREA.PIC',0
even
bss
kbshift: ds.l 1
error: ds.l 1
handle: ds.w 1
end
Listing 1. Screenshots über Sondertasten
TOS ist ein Magazin zum Mitmachen. Also, Programmierer und Anwender - egal, ob Einsteiger oder Profi - aufgepaßt! Wir suchen Programme und Anwendertips zur Veröffentlichung. Einschränkungen machen wir keine: Das Programm oder der Tip kann kurz oder lang sein. Selbstverständlich gibt es ein angemessenes Honorar für Ihre Veröffentlichung. Ihr Honorar richtet sich nach der Qualität der Einsendung. Pauschalhonorare haben wir nicht, schließlich würdigen wir gute Beiträge nicht nach Länge. Einschicken können Sie auch alles, was mit der Programmierung des ST zusammenhängt. Das muß keine Superroutine sein, eine genial programmierte C-Funktion oder ein Hinweis über Betriebssystemfehler hat gute Chancen, in dieser Rubrik veröffentlicht zu werden. Die Belohnung für Ihren abgedruckten Beitrag beträgt mindestens 50 Mark.
Ihr Programm [auf Diskette] mit ausführlicher Beschreibung richten Sie an:
ICP-Verlag * Redaktion TOS * Stichwort: Tips & Tricks * Wendelsteinstraße 3 * 8011 Vaterstetten