Datumsroutinen in GFA-Basic
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)
AuflösungsunabhÀngig in Basic
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
Bildschirm auf Diskette
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
Bitbelegung bei Kbshift
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
GESUCHT: TIPS & TRICKS
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