← 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