Tips und Tricks für Programmierer

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



Aus: TOS 12 / 1990, Seite 82

Links

Copyright-Bestimmungen: siehe Über diese Seite