← TOS 12 / 1990

Tips und Tricks fĂŒr Programmierer

Programmieren

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