Simple Screen Protection: Der TT am Rande der Finsternis

Fall 1: Da sitzt man nun vor dem Rechner und betrachtet ein Stück Quellcode, denkt nach, weshalb es nicht so funktioniert wie gedacht - da verdunkelt sich der Bildschirm und reißt einen aus seinen Gedanken.

Fall 2: Endlich ist der TT geliefert worden. Nach einigen Klimmzügen im XCONTROL.ACC ist das Monitorbild endlich invertiert, weil die (nur) 60 Hz Bildfrequenz doch auf die Augen gehen. Die auf dem alten ST als sehr komfortabel empfundenen AUTO-Ordner-Programme werden installiert. Plötzlich erscheint das Bild Rot in Rot. Eine Mausbewegung, schon ist der Spuk vorbei. Ein Virus? Nein, ein Bildschirmschoner war hier am Werk.

Diese beiden Fälle zeigen, daß ein Bildschirmschoner, selbst wenn er funktioniert, ein zweischneidiges Schwert sein kann - nützlich und nervend zugleich. Abhilfe bringt hier ein Schoner, dem jederzeit mitgeteilt werden kann, daß er jetzt bitte die Verdunkelung herunterzulassen hat, und sich ansonsten still zu verhalten habe.

Die Taktiken

Es gibt verschiedene Arten von Verdunkelungstatiken: Videosignal abschalten, an der Synchronisation herumfummeln oder die doppelte Invertierung.

Dieser Aufzählung möchte ich noch ein weiteres Mitglied hinzufügen: Die (von mir so getaufte) ‚Dunkel‘-Palette. Die Dunkel-Palette hat für sämtliche Farben den Mischwert Null.

Diese Taktik hat einen kleinen Nachteil: Wenn ein Programm während der Dunkelschaltung die Palette ändert und die aktuelle rettet, um sie zu gegebener Zeit wieder zu restaurieren, schickt es damit den Rechner bis zum nächsten RESET in die Dunkelheit.

Das hier zum Teil verwendete GFA-BASIC hat diese Eigenschaft, wenn es aus dem Direkt- in den Editor-Modus umschaltet. Die zu diesem Zeitpunkt aktive Palette wird gerettet und beim nachfolgenden Verlassen des Interpreters wieder gesetzt. Dieses Problem kommt im zweiten Abschnitt zum Tragen.

SSPA

Wenn das Simple-Screen-Protection-Accessory (SSPA) als Accessory gestartet wurde und die Prüfung der maximal möglichen Farben den Wert 4096 bescheinigt, wird versucht, das Accessory in die Liste einzutragen. Ist der Rechner kein TT/STE (4096 mögliche Farben) oder die Liste voll, dann beschränkt sich das Programm darauf, zu warten, bis der Rechner keinen Saft mehr hat. Die Argumente (für Low-und Highword, da Intelformat, die Kompatibilität läßt grüßen) von -1 sind vorzeichenlos zu interpretieren und werden zu einer unsigned long zusammengesetzt. (Diese hat den Wert 4,294,967,295 (=232-1), oder anders ausgedrückt: der EVNT_TIMER()-Aufruf kehrt erst nach 49 Tagen, 17 Stunden, 2 Minuten und 47.295 Sekunden zum Hauptprogramm zurück.)

Nach der Installation wartet das Accessory nur auf seinen Aufruf, rettet die aktuelle Palette. (Es geht zwar theoretisch eleganter mit den XBIOS-Funktionen 84 und 85, allerdings hagelte es bei mir dann 4 Bomben.) Daher rettet das Programm die tatsächlich vorhandenen Farben mittels der VDI-Funktion vq_color und setzt die Farben der ,Dunkel‘-Palette mit vs_color.

Da die Funktionen aber jeweils in einer 16Bit-Variablen getrennt die Rot/Grün/Blau-Anteile im Array RGB erwarten und Accessories möglichst wenig Speicher belegen sollten, wird auf einen kleinen Trick zurückgegriffen: Die Werte für die Farbanteile bewegen sich im Bereich zwischen 0 und 1000. Beim Wert 1024 ist nur das 10. Bit gesetzt, es werden also für jeden Farbanteil gerade 9 Bits gebraucht (max. Wert: 1023). Daher passen alle drei Farbanteile bequem in eine 32Bit-Variable. Damit werden 2 Felder x 4 Bytes/Element x 256 Elemente = 2048 Bytes für beide Paletten benötigt. Bei der einfacheren 1:1-Speicherung mit 2 Bytes/Element in einer 256 x 3-Matrix wären es 2 x 2 x 256 x 3 = 3072 Bytes. Allein nur durch eine geschicktere Speichermethode wurde ein Kilobyte eingespart - und das bei einer doch recht kleinen Datenmenge.

Zum Speichern der Farbanteile wird jeder Farbanteil an seine entsprechende Stelle bitweise nach links verschoben: Der Rotanteil belegt die unteren Bits 0..9, Grün liegt bei den Bits 10..19, Blau landet bei den Bits 20..29. (Bleiben noch die Bits 30 und 3 übrig). Hierbei muß allerdings beachtet werden, daß in ein Langwort gespeichert wird, daher ist die explizite Typumwandlung vor der Verschiebung nötig.

Beim Auslesen der Palette taucht ein anderes Problem auf: Die nicht gewünschten Anteile müssen ausmaskiert werden. Dazu wird eine Zahl benutzt, die im gewünschten Bereich sämtliche Bits gesetzt hat, deren sonstige Bits aber gelöscht sind. Damit noch der Bereich oberhalb des gewünschten ausmaskiert werden kann, muß das Bit oberhalb des maximal auszumaskierenden Bits gesetzt sein, hier ist es das Bit 30. Nach dem Ausmaskieren wird der Farbanteil wieder entsprechend nach rechts verschoben, um den Originalwert zu erhalten.

/***************************************** 
 Simple Screen Protection Accessory (SSPA)

 (C) MAXON COMPUTER GmbH 1992

 Author: T.W. Müller

 BORLAND TURBO C V2.03 
*****************************************/

/* Einige Header-Dateien */

#include <aes.h>
#include <vdi.h>

/* Einige Definitionen, um das Programm lesbarer zu machen */

#define MAX_COLORS work_out[39]
#define TT_STE_COLORS 4096 
#define ANZ_COLORS work_out[13] 
#define ShiftGreen 10 
#define ShiftBlue 20 
#define MaskRed 0x400003FF 
#define MaskGreen 0x400FFC00 
#define MaskBlue 0x7FF00000

/* Die Farben - um Konflikte mit den 
   GEM-Konstanten zu vermeiden, mit 
   Underscores versehen. */

#define _Red_ RGB[0]
#define _Green_ RGB[1]
#define _Blue_ RGB[2]

/* so sieht also die Ewigkeit aus, naja ... */ 
#define forever for(;;)

#define isACC (!_app)
#define Darkness (message[0] == AC_OPEN) 

extern int _app;

    /*
    Handles für AES und VDI,
    ID-Nummern f. Prog. und ACC.
    */

int     phys_handle, 
        handle, 
        appl_id, 
        menu_id,
        dummy; /* wasted storage ... */
        /* die Paletten, max. 256 Farben */ 
long    DarkPal[256],CurPal[256]; 
int	    work_out[57];

/* Die Prototypen */
void WaitForMessage ( void );
void WaitForGodot ( void );
void GetPalette(int C1, int Cm, long C[256]); 
void SetPalette(int C1, int Cm, long C[256]);

void main( void )
{
    int i;
    int work_in[11];

    /* Applikation anmelden */ 
    appl_id = appl_init ( ); 
    if ( appl_id ! = -1 )
    {
        for ( i = 0; i < 10; work_in[i++] = 1); 
        work_in[10] = 2;
        /* beim AES anmelden */
        phys_handle = graf_handle( &dummy, &dummy, &dummy, &dummy);
        handle = phys_handle;
        /* Beim VDI anmelden -> AES-Handle,
            <- VDI-Handle */ 
        v_opnvwk( work_in, &handle, work_out ); 
        if ( handle != 0 )
            if (isACC && (MAX_COLORS == TT_STE_COLORS)) 
            {
                for (i = 0; i < 256; DarkPal[i++] = 0L); 
                menu_id = menu_register( appl_id, "  Screen Protector" ) ;
            /*
                Aus der nächsten Seile kehrt das 
                Accessory nie wieder zurück ...
            */
                WaitForMessage();
            }; /* isACC . . . */
        /*
            sobald APPL_INIT() keinen Fehler meldet, 
            das Accessory aber nicht installiert 
            wurde, landet es hier ...
        */
        WaitForGodot();
    }; /* appl id */ 
    exit( 0 );
} /* main () */

void WaitForMessage( void )
{
    int message[8],
        CurX,CurY,CurKeys,
        OldX,OldY,OldKeys;

    forever /* Auf immer und ewig ... */
    {
        evnt_mesag(message); 
        if (Darkness)
        {
            message[0]=0; /* sicher ist sicher */
            /*
                Vergleichswerte ermitteln ...
            */
            vq_mouse(handle,&OldKeys,&OldX,&OldY); 
            vq_key_s(handle,&OldKeys);
            /*
                Farben retten und 'Dunkel'-Palette setzen
            */
            GetPalette(0,ANZ_COLORS-1,CurPal);
            SetPalette(0,ANZ_COLORS-1,DarkPal);
            /*
                Warten...
            */
            do
            {
                evnt_timer(250,0);
                vq_mouse(handle,&CurKeys,&CurX,&CurY); 
                vq_key_s(handle,&CurKeys);
            }while ((OldKeys == CurKeys) && (OldX == CurX) && (OldY == CurY));
            /*
                alte Farben wiederherstellen
            */
            SetPalette(0,ANZ_COLORS-1,CurPal);
        }; /* Darkness */
    }; /* forever */
}

void WaitForGodot( void )
{
    forever evnt_timer(-1,-1);
}

void GetPalette(int min, int max, long c[256])
{
    int i, RGB[3];
    for (i = min; i <= max ; i++)
    {
        vq_color(handle, i, 0, RGB);
        c[i] = ((long) _Red_);
        c[i] += ((long) _Green_) << ShiftGreen;
        c[i] += ((long) _Blue_) << ShiftBlue;
    }
}

void SetPalette(int min, int max, long c[256]) 
{
    int i, RGB[3];
    for (i = min; i <= max ; i++)
    {
        _Red_   = (c[i] & MaskRed);
        _Green_ = (c[i] & MaskGreen) >> ShiftGreen; 
        _Blue_  = (c[i] & MaskBlue)  >> ShiftBlue; 
        vs_color(handle, i, RGB);
    }
}

Listing 1: Das Simple-Screen-Protection-Accessory

Nachdem die Paletten ausgetauscht wurden, wartet das Programm wieder - auf die Betätigung einer Sondertaste (Control, Alternate, Shift) oder auf eine Bewegung des Mauszeigers. Danach setzt es die aktuelle Palette - um nun wieder auf seinen Aufruf zu warten. Damit der Rechner bzw. das TOS/GEM während der Verdunkelung Weiterarbeiten kann, ist der EVNT_TIMER()-Aufruf in der Schleife zuständig. Er legt eine Pause von einer viertel Sekunde zwischen zwei Abfragen ein, in der dann andere Programme abgearbeitet werden können. Auch hier sind wieder Low- und High-Word als Argumente angegeben.

Listing 1 zeigt das komplette Programm, übersetzt wurde es mit der abgedruckten Projekt-Datei. Die verwendete Stack-Größe beträgt 256 Bytes - das reicht vollkommen, da die Arrays nur per Adresse übergeben werden. Der vom Programm belegte Speicher beträgt 138 Bytes (ermittelt mit DISKUS / Segmentdaten).

Der zweite Abschnitt

Das SSPA ist auch von anderen Programmen her steuerbar! Hierzu wird die sog. ,Accessory-Pipe‘ benutzt. Die Vorgehensweise ist denkbar einfach:

Such das Accessory unter seinem Dateinamen, merk Dir die Applikation-ID, bau den gewünschten Message-Puffer zusammen (16 Bytes bzw. 8 Wörter) und schick die Adresse des neuen Message-Puffers an das Accessory.

Die mitgelieferte Einbindung für das GFA-BASIC zeigt, wie einfach der recht mächtige Mechanismus der Accessory-Pipe zu bedienen ist (Listing 2). Die eigentlichen Steuerungszeilen sind mit einem ,(*)' markiert.

Ausblick

Auch für andere Zwecke ist es sehr nützlich: ein Accessory, um eine Hardcopy zu speichern (was im TT-Modus vom TOS/GEM unterdrückt wird.), indem einfach an das Accessory eine Nachricht gesandt wird. Die Möglichkeiten sind ja nahezu unerschöpflich ...

' ********************************************** 
' GPA BASIC-Einbindung zum Dunkelschalten via 
' ACC-Pipe (Achtung! Fehler im GFA-Handbuch !!)
' Information aus dem Sybex Profibuch
'
' (C) MAXON Computer 1992
'
' Author: T.W. Müller 
' GFA BASIC 3.6 (TT-Version)
' **********************************************
'
' Zu Starten mittels 'RUN' aus dem Direkt-Modus
'
' Kleine Spielerei: ein Countdown . . .
CLS
PRINT
PRINT
PRINT SPC(20);"SSPA Fernsteuerung"
PRINT
PRINT "Nach dem Countdown ";
PRINT "wird der Bildschirm verdunkelt ..."
PRINT
PRINT "... und nach 1 Sekunde";
PRINT " wiederhergestellt !"
PRINT
PRINT "Countdown:"
FOR i&=9 DOWNTO 0
    PRINT ,i&;CHR$(13);
    IF i&
        PAUSE 50 
    ENDIF 
NEXT i&
DIM message&(7) ! Message-Puffer 
message&(0)=40  ! AC_OPEN - Message
ap_id&=APPL_FIND("PROTECT ") ! ACC finden ... (*)
IF ap_id&>0     ! gefunden und dunkel wird's (*)
    '
    ' Es wird dunkel.
    '
    ~APPL_WRITE(ap_id&,16,V:message&(0)) ! (*)
    '
    ' Die Print-Befehle erfolgen in der 
    ' Verdunkelung
    '
    ~EVNT_TIMER(500)
    PRINT
    PRINT
    PRINT ,"Bitte warten ..."
    '
    ' Etwas warten ...
    '
    ~EVNT_TIMER(500)
    ' Shift links simulieren ...
    ~BIOS(11,8)
    ' ... warten, bis SSPA geprüft hat ...
    ~EVNT_TIMER(2000)
    ' ... und alle Spuren verwischen !
    ~BIOS(11,0)
ENDIF ! (*)
END

Listing 2: So geht’s mit der Pipe

;
; Projektdatei zu PROTECT.C
;
c:\protect.acc      ; C: ist Boot-Laufwerk 
.C[-G]              ; Compiler-Optionen
.L[-V]              ; Linker-Optionen
=
tcstart.o

protect.c

tcstdlib.lib 
tcgemlib.lib

Listing 3: Projektdatei


Thomas Müller
Aus: ST-Computer 11 / 1992, Seite 92

Links

Copyright-Bestimmungen: siehe Über diese Seite