DSKINF: Wie man mehr aus seinem DESKTOP.INF macht

Das hier vorgestellte Programm sorgt dafür, daß die mit dem Kontrollfeld-Accessory gemachten Voreinstellungen der DESKTOP.INF-Datei auch ohne das Kontrollfeld-Accessory wirksam werden. Dabei werden die wenig benutzten AES-Funktionen shel_get, shel_put und form_error erklärt und weitere Anwendungsmöglichkeiten erläutert.

Mit dem jedem ST beiliegenden Kontrollfeld-Accessory kann man bekanntlich allerlei tolle Dinge einstellen: Der Tastaturklick und der “Gong” lassen sich an- und abschalten, die Ansprechschwelle und Wiederholrate der Tastaturwiederholung, die Geschwindigkeit der Mausdoppelklicks und die Standardfarbpalette lassen sich wählen. Außerdem ist eine Voreinstellung der RS232-Schnittstelle und des Druckers möglich. Für letzteres war bei älteren Versionen ein eigenes “Emulator-Accessory” notwendig, inzwischen hat diese Aufgabe auch das Kontrollfeld übernommen.

All diese Einstellungen lassen sich unter dem Menüpunkt “Arbeit sichern” des Desktops sogar abspeichem. Dabei erzeugt das Desktop bekanntlich die DESKTOP.INF-Datei, in der neben der Konfiguration des Desktops (Fensterpositionen, “Voreinstellungen” usw.) auch die mit dem Kontrollfeld eingestellten Parameter stehen.

Doch wie so vieles bei unserem Lieblingsrechner hat auch dieses einen Haken. Die Kontrollfeldparameter werden nämlich nur vom Kontrollfeld-Accessory selbst wirksam gemacht (nicht etwa vom Desktop oder vom Betriebssystem), d.h. nur wenn das Kontrollfeld-Accessory geladen ist, kommt man in den Genuß all der mühsam ausprobierten “optimalen” Voreinstellungen.

Doch nicht jeder hat Lust, jedesmal das Kontrollfeld-Accessory mitzuladen, da man es sonst im allgemeinen nicht braucht. Das kleine Accessory, das ich Ihnen hier vorstellen möchte, übernimmt nun diese Aufgabe, d.h. es macht beim Booten all die oben genannten Einstellungen wirksam, ohne daß das Kontrollfeld-Accessory geladen wird. Da es seine Informationen aus dem DESKTOP.INF bezieht, habe ich es auf den kryptischen Namen DSKINF getauft.

Eigentlich ist es eine ziemliche Verschwendung, für solch ein “Problem” ein Accessory zu opfern, daher ist es günstiger, DSKINF in ein anderes Accessory, quasi als Zugabe, einzubauen.

Falls Sie bisher das Kontrollfeld-Accessory nicht benutzt haben, könnte es sein, daß in Ihrem DESKTOP.INF einige Parameter “merkwürdig” eingestellt sind, die sich bisher nicht bemerkbar gemacht haben. Daher kann es sein, daß Ihre Maus sich etwas ungewöhnlich benimmt oder die Farben leicht anders sind.

Dann sollten Sie zuerst mit dem Kontrollfeld-Accessory für eine geeignete Einstellung sorgen.

Wo ist das DESKTOP.INF?

Unser Programm muß also irgendwie an die im DESKTOP .INF gespeicherten Parameter herankommen. Als erstes würde es sich vielleicht anbieten, das DESKTOP.INF direkt von Diskette oder Harddisk zu laden. Doch ist es nicht so einfach, zuverlässig das Bootlaufwerk herauszubekommen.

Das “Problem” löst sich in Wohlgefallen auf, wenn man sich an die in der Literatur recht stiefmütterlich behandelten AES-Funktionen shel_get und shel_put erinnert (in [2] sind sie korrekt erklärt). Diese lesen nämlich einen Puffer des AES aus (bzw. beschreiben ihn), in dem sich normalerweise das DESKTOP.INF befindet. Das AES lädt das DESKTOP.INF nämlich beim Systemstart automatisch in einen internen Puffer, und auch das Desktop und das Kontrollfeld-Accessory benutzen diese AES-Funktionen, um darauf zuzugreifen. “Arbeit sichern” speichert dann das DESKTOP.INF aus dem Puffer auf das externe Speichermedium.

Jedesmal, wenn man auf dem Desktop ein Programm startet, speichert das Desktop “seine” Parameter (wie etwa die Fensterkonfiguration) mit shel_put dort ab. Sobald man wieder zum Desktop “zurückkehrt”, liest das Desktop das DESKTOP.INF mit shel_get, um sich an die alten Fensterpositionen usw. “zu erinnern”.

#a123456
  1. Betrieb : 0=Vollduplex, 1=Halbduplex
  2. Baudrate : 0=9600, 1=4800, 2=1200, 3=300, 4=19200, 5=3600, 6=2400, 7=2000, 8=1800, 9=600, ‘:’=200, ‘;’=150, ‘<‘=134, ‘=‘=110, ‘>’=75, ‘?’=50
  3. Parität 0=keine, 1=ungerade, 2=gerade
  4. Datenbits : 0=8, 1=7, 2=6, 3=5
  5. Handshake: 0=keins, 1=XON/XOFF, 2=RTS/CTS, 3=beides
  6. Bit8 : 0=gesetzt, 1=gelöscht

#b123456

  1. Typ: 0=Matrix, 1=Typenrad
  2. Farbe: 0=Schwarzweiß, 1=Farbe
  3. Auflösung: 0=1280, 1=960 Punkte
  4. Qualität: 0=Test, 1=Maximum
  5. Port: 0=Centronics, 1=RS232
  6. Papierart: 0=Endlospapier, 1=Einzelblatt

#c777...123AABB

777... (ersten 48 Ziffern): Farbpalette (s. Text)

  1. Doppelklick-Geschwindigkeit (0-4, wie im Kontrollfeld)
  2. Tastenklick: 0=aus, 1=an
  3. Glocke: 0=aus, 1=an AA. Tastaturwiederholzeit (zweistellige Hex-Zahl)
    BB. Tastaturwiederholrate (zweistellige Hex-Zahl)

Die Kontrollfeld-Parameter im DESKTOP.INF

Aufbau des DESKTOP.INF

Das DESKTOP.INF ist bekanntlich eine Textdatei, in der jede Zeile mit einem ’#’-Zeichen beginnt, gefolgt von einem Buchstaben, der angibt, welcher Art die Informationen dieser Zeile sind. Falls Sie mit dem DESKTOP.INF noch nicht so vertraut sind, sollten Sie es sich zunächst durch einen Doppelklick oder “zeige Info” anzeigen lassen.

Hier interessieren uns nur ‘#a’ ‘#b’ und ’#c’, die für die Voreinstellung der seriellen Schnittstelle, die Druckeranpassung und das Kontrollfeld zuständig sind. Die Bedeutungen der nachfolgenden Parameter sind kurz in der Abbildung zusammengestellt.

Bei älteren Versionen des Kontrollfeld-Accessories sind bei ‘Baud-Rate’ nur die ersten vier Werte einstellbar.

Dem ‘#c’ folgen zunächst 16 Zifferngruppen aus je 3 Ziffern. Jede Gruppe legt dabei die Farbe einer der maximal 16 Farben der Farbpalette fest. Dabei steht jede der drei Ziffern einer Gruppe für die Farbintensität der Grundfarben Rot, Grün und Blau (zwischen 0 und 7). Im Monochrommodus ist nur die erste Dreiergruppe von Bedeutung. Dabei steht ‘777’ für die normale und ’000’ für die invertierte Darstellung.

Das DESKTOP.INF endet eigentlich mit einem CTRL-Z (IBM läßt grüßen), doch fragt DSKINF sicherheitshalber auch noch auf das übliche Nullbyte ab. Wer mehr über den Aufbau des DESKTOP.INF wissen möchte, sei auf [1] oder [2] verwiesen, da eine komplette Behandlung hierein wenig zu weit führen würde.

Arbeitsweise von DSKINF

Die Funktion anal_dskinf läßt nun von get_dskinf das DESKTOP.INF mittels shel_get in einen vorher reservierten Puffer lesen. Der erste Parameter bei shel_get gibt die Adresse des vorher bereitgestellten Puffers an, der zweite die Zahl der zu übertragenden Zeichen. Die tatsächliche Länge des DESKTOP.INF kann nicht im voraus ermittelt werden, doch ist der Puffer nach ATARIs neueren Dokumentationen 1024 Byte lang, also holen wir uns einfach diese 1024 Byte.

Die nachfolgende Schleife sucht nun die für uns relevanten Einträge im DESKTOP. INF heraus.

Die drei Routinen set_rs232, set_prt und set_cntrl werten nun die nachfolgenden Parameter aus. Zu set_rs232 und set_prt gibt es nicht viel anzumerken. Bei set_rs232 sollten Sie beachten, daß die Bits 7,4,3 und 0 des USART-Control-Registers (‘ucr’ im Listing) im DESKTOP.INF nicht voreingestellt werden können, und daher von DSKINF nicht verändert werden sollten. Außerdem sind die Parameter “Voll-/Halbduplex” und “8. Bit an/aus” keine Einstellmöglichkeiten der seriellen Schnittstelle selbst, sondern werden vom “VT52-Emulator” bei der Kommunikation über die Schnittstelle berücksichtigt. Daher können sie von DSKINF nicht ausgewertet werden.

Bei set_prt gibt es keine Besonderheiten, da alle Möglichkeiten, die die Druckereinstellung mit der XBIOS-Funktion Setprt bietet, mit dem DESKTOP.INF auch voreingestellt werden können.

Bei set_cntrl sollten Sie sich aber die Zusammenstellung der Farbpalette etwas genauer ansehen. Die dort angegebenen 16 Farben bilden nämlich nicht in dieser Reihenfolge die Farbpalette des Shifters (Video-Chips), sondern werden vom Kontrollfeld-Accessory scheinbar nach einem merkwürdigen Schema in einer anderen Reihenfolge zusammengestellt (abhängig von der Auflösung).

Das Feld col_ix in der Funktion _set_cntrl gibt über die “Vertauschung” bei niedriger und mittlerer Auflösung Auskunft. Es wird mit der Position einer Farbe innerhalb der ‘#c’-Zeile indiziert (2. Index) und liefert die zugehörige Position innerhalb der Farbpalette des Shifters. Bei der mittleren Auflösung z.B. wird die zweite Farbe im DESKTOP.INF zur vierten in der Palette des Shifters. Das Rätsel läßt sich erklären, wenn man annimmt, daß das Kontrollfeld-Accessory die Farben nicht mit der XBIOS-Funktion Setpalette oder Setcolor, sondern mit der VDI-Funktion vs_color setzt (dann ohne “Vertauschung”). Ein Blick ins VDI-Listing [3] zeigt nämlich, daß vs_color die “VDI-Farben” (“color index”) zur Palette des Shifters genau so zuordnet, wie das Kontrollfeld dies macht.

DSKINF könnte nun in der gleichen Weise vorgehen. Doch liegen die Farben im DESKTOP.INF schon in einem der Shifter-Palette angepaßten Format vor, d.h. die Intensitäten der Grundfarben sind schon in Werten 0...7 angegeben, das VDI benötigt jedoch eine geräteunabhängige 1000er-Skalierung, bei der die Farbintensitäten jeweils in Promille der Gesamtintensität (=1000 Promille) angegeben werden. DSKINF müßte also die Farbwerte aus dem DESKTOP.INF auf die VDI-Skalierung umrechnen, außerdem müßte eine “VDI work station” eröffnet werden, um vs_color aufzurufen.

Daher wurde der einfachere Weg gewählt, indem DSKINF die Farbpalette über das XBIOS setzt und dafür die Numerierung der Farben selbst umrechnet. Dies könnte also bei anderen Bildschirmgerätetreibern des VDI zu Schwierigkeiten führen.

In einer Kleinigkeit verhält sich DSKINF übrigens anders als das Kontrollfeld-Accessory. Während vs_color nur die tatsächlich möglichen Farben neu setzt (also 1, 4 oder 16 je nach Auflösung), verändert DSKINF immer die ganze Palette. Da die Auflösung aber im allgemeinen nicht verändert wird, ohne daß neu gebootet wird, macht dies nicht viel aus.

Tastaturklick und “Glocke” können nur mit der Systemvariablen conterm beeinflußt werden, daher muß kurzfristig in den Supervisor-Modus umgeschaltet werden, um die sonst “geschützte” Systemvariable ändern zu können.

Am Ende von set_cntrl findet sich noch der XBIOS-Aufruf Vsync, mit dem auf den nächsten Bildwechsel des Monitors gewartet wird. Dies ist erforderlich, da die bei Setpalette angegebene Farbpalette erst beim nächsten Bildwechsel gesetzt wird. Da Setpalette sich nur den Zeiger auf die Palette merkt und das Feld col nur lokal definiert, also nach dem Verlassen von set_cntrl nicht mehr vorhanden ist, würde das BIOS die Palette eventuell gar nicht mehr vorfinden, wenn es sie setzen will. Vsync sorgt also dafür, daß col beim Verlassen von set_cntrl garantiert nicht mehr benötigt wird.

Werfen wir zu guter Letzt noch einen Blick auf das Hauptprogramm (main). Zu Beginn wird das aktuelle DESKTOP.INF gerettet. Da es zu diesem Zeitpunkt kaum schon verändert worden sein kann, handelt es sich sicherlich um das Original-DESKTOP.INF, wie es auch auf der Disk vorliegt.

Beim Anklicken des Accessories im Desk-Menü wird nun dieses gerettete DESKTOP. INF als neues DESKTOP.INF gesetzt. Dazu wird die AES-Funktion shel_put verwendet, die analog zu shel_get arbeitet, nur daß hier die spezifizierte Anzahl Bytes in den DESKTOP.INF-Puffer übertragen wird. Bei größeren Werten als 1024 werden globale Variablenbereiche des AES überschrieben, daher Vorsicht.

Dies kann dazu benutzt werden, das Desktop “aufzuräumen”. Verändern Sie z.B. den Aufbau des Desktops, indem Sie Laufwerkssymbole verschieben, Voreinstellungen verändern oder Fenster verändern. Starten Sie nun ein GEM-Programm, das den Aufruf von Accessories zuläßt, und wählen Sie von dort aus DSKINF im Deskmenü an. Nach der Rückkehr aus dem GEM-Programm finden Sie das Desktop in seiner Anfangskonfiguration wieder.

Vom Desktop aus bewirkt der Aufruf von DSKINF allerdings nichts, da das Desktop bei jedem Programmstart das DESKTOP.INF selbst mit shel_put überschreibt und nur mit shel_get liest, wenn das Desktop nach Beendigung des anderen Programms wieder gestartet wird.

Hier findet sich gleich noch eine Anwendung der ebenfalls wenig benutzten AES-Funktion form_error. Diese Funktion bekommt einen PC-DOS-Fehlercode übergeben und gibt daraufhin eine (allerdings nicht immer ganz passende) Alert-Box aus. Mehr Informationen zu form_error finden Sie in [4] und [2],

Es erweist sich manchmal als sehr praktisch, wenn man auch bei programmeigenen Fehlern mit GEMDOS-Fehlercodes arbeitet und die Fehlercodes von GEMDOS-Aufrufen (insbesondere Dateioperationen) “aufhebt”, um sie später an eine Routine wie alrt_tos weiterzugeben. alrt_tos rechnet die GEMDOS-Fehlercodes in PC-DOS-Codes um und ruft damit form_error auf. Somit spart man sich so manche eigene Alert-Box.

Anregungen

Hier noch einige Anregungen für Sie, falls Sie Lust haben, selbst ein wenig mit shel_get und shel_put herumzuspielen. Das Programm kann z.B. dahingehend verändert werden, daß bei jedem Anklicken anal_dskinf aufgerufen wird. Dies ist besonders bei Programmen praktisch, die den Bildschirm invertieren oder die Farbpalette umschalten und “vergessen”, dies rückgängig zu machen.

Eine andere Anwendung von shel_get könnte darin bestehen, sich Informationen über die auf dem Desktop angemeldeten Laufwerke zu verschaffen. Gerade für Programme mit eigenen Laufwerkssymbolen bestünde somit die Möglichkeit, deren Position und Beschriftung aus dem DESKTOP.INF zu übernehmen.

Auch wäre ein Programm denkbar, das zwischen verschiedenen Grundeinstellungen des Desktops auswählen läßt. Oder wie wäre es mit einem Programm, das Laufwerkssymbole selbsttätig anmelden kann? Ihrer Phantasie sind kaum Grenzen gesetzt.

Literatur:

[1] O.T.Dietz: “Dem Desktop auf der Spur”, ST-Computer 10/86

[2] Jankowski/Reschke/Rabich: “ATARI ST Profibuch”, 5.Auflage, Sybex 1988

[3] Abraham/Englisch/Günther/Szczepanowski: “ATARI ST GEM”, Data Becker 1987

[4] A.Esser: “Auf der Schwelle zum Licht”, ST-Computer 2/88


/* Übernahme d.i.DESKTOP.INF gespeich. Parameter für RS232-Schnittstelle, Drucker und "Kontrollfeld"

   23.8.1987, 28.2.1989 
   (c) A. Esser

   entwickelt mit MEGAMAX C/LASER C
   direkt lauffähig unter LASER C und TURBO C
*/

#ifdef __TURBOC__

#include <tos.h> /* für TURBO C */
#include <aes.h>

#define appl_init() gl_apid=appl_init()
int gl_apid;

int rd_hex(char *p); /* Prototypen */
void set_rs232(char *p);
void set_prt(char *p);
void set_cntrl(char *p);
char *get_dskinf(void);
int anal_dskinf(void);
int do_work(char *p);
void alrt_tos(int err);

#else

#include <osbind.h> /* für LASER C */
#include <gemdefs.h>

extern int gl_apid;

#endif

#define NIL     0L
#define TRUE    1
#define FALSE   0

#define E_OK    0 /* GEMDOS-Rückgabewert f.OK */
#define ENSMEM  -39 /* GEMDOS-Fehler: kein Speicher mehr */ 
#define conterm ((char *)0x484) /*Systemvariable 'conterm' */

#define MAXINF  1024 /* maximal erlaubte Länge des DESKTOP.INF */ 
#define CTRL_Z  26   /* Endekennung */
#define ACCNAME "  DESKTOP.INF" /* Menu-Eintrag */

/* 2-stelliges Hex-ASCII nach int konvertieren */ 
int rd_hex(p)
register char *p;   /* Zeiger auf 2 Hex-ASCII-Ziffern */
{   register int num; 
    register int dig;

    if ((dig = *p++ - '0') > 9)
        dig -= 7; 
    num = dig << 4; 
    if ((dig = *p++ - '0') > 9)
        dig -= 7; 
    return (num | dig);
}

/* RS232-Parameter n.DESKTOP.INF-Einst.setzen */ 
void set_rs232(p)
register char *p;   /* Zeiger in DESKTOP.INF */
{   int baud,ctrl,ucr;

/* für Baud-Raten 9600,4800,1200,300,19200,3600,2400,2000, 
                  1800,600,200,150,134,110,75,50*/ 
    static int brate[] = { 1,2,7,9,0,3,4,5,6,8,10,11,12,13,14,15 };

    /* f.Parität: keine, ungerade, gerade,(keine)*/ 
    static int parity[] = { 0,4,6,0 };

    ucr = (int)((unsigned long) Rsconf(-1,-1,-1,-1,-1,-1) >> 24) & 0x99; 
    p++;        /* Voll-/Halb-Duplex ignorieren*/
    baud = brate[*p++ & 15];        /* Baud-Rate */
    ucr |= parity[*p++ & 3];        /* Parity */
    ucr |= (*p++ & 3) << 5;         /* Data-Bits */
    ctrl = *p++ & 3;                /* Handshake */
                                    /* Bit8-An/Aus ignorieren */ 
    Rsconf(baud,Ctrl,ucr,-1,-1,-1); /*alles setzen*/
}

/* Drucker-Konfig. n. DESKTOP.INF-Einst.setzen */ 
void set_prt(p)
char *p;                /* Zeiger in DESKTOP.INF */
{   register int i; 
    register int konf;

    for (konf=0, i=0; i<6; i++)     /*Konfig.-Bits */ 
        konf |= (*p++ & 1) << i;    /*zusammenrechnen*/ 
    Setprt(konf);            /* Konfiguration setzen */
}

/* Kontrollfeld-Parameter nach DESKTOP.INF-Einstellung setzen */ 
void set_cntrl(p)
register char *p;   /* Zeiger in DESKTOP.INF */
{   register int no,i;
    int col[16];    /* Farbpalette */
    void *ssp;      /* Zwischenspeicher für Supervisor Stack Pointer */
    int res,ix;
    register int c;
    static char col_ix[2][16]
        = { {0,15,1,2,4,6,3,5,7,8,9,10,12,14,11,13}, /* für niedrige Auflösung */
            {0,3,1,2,4,5,6,7,8,9,10,11,12,13,14,15}}; /* für mittlere Auflösung */
    res = Getrez();         /* Bildschirm-Auflösung */
    for (no=0; no<16; no++) /* Farben der Farbpalette holen */
    {   for (c=0, i=2; i>=0; i--)   /* RGB-Anteile zusammenrechnen */ 
            c |= (int)(*p++ & 7) << (i * 4); 
        ix = (res == 0 || res == 1)?col_ix[res][no]: no;
        col[ix] = c;
    }
    Setpalette(col);                /* Farbpalette setzen */ 
    evnt_dclick(*p++ & 7, TRUE);    /* Maus-Klick-Speed setzen */

    ssp = (void *)Super(NIL);       /*Supervisor-Mode*/ 
    /* Tastatur-Klick und Glock an-/ausschalten*/ 
    *conterm = (*conterm & ~5) | (*p++ & 1) | ((*p++& 1) << 2);
    Super(ssp);                     /* zurück in den User-Mode */ 
    Kbrate(rd_hex(p),rd_hex(p+2));  /* Tastatur-Repeat */
    Vsync(); /* warten bis Farbpalette gesetzt*/
}

/* Kopie des DESKTOP.INF holen */ 
char *get_dskinf()
{   char *p;
    
    if ((p = (char *)Malloc((long)MAXINF)) != NIL)
        shel_get(p,MAXINF);         /* DESKTOP.INF vom AES abholen */
    return p;
}

/* DESKTOP.INF nach '#'-Einträgen durchsuchen */ 
int anal_dskinf()
{   char *infb;         /* Puffer für DESKTOP.INF */
    register char *p;

    if ((p = infb = get_dskinf()) == NIL)
        return ENSMEM;  /* kein Speicher mehr */ 
    shel_get(p,MAXINF); /* DESKTOP.INF vom AES abholen */
    p[MAXINF-1] = 0;    /* sicherheitshalber... */
    while (*p && *p != CTRL_Z)
    {   if (*p++ == '#') /* Eintrag gefunden */ 
            switch (*p++) /* Typ untersuchen */
            {   case 'a':
                    set_rs232(p);   /* RS232-Konfiguration */ 
                    break; 
                case 'b':
                    set_prt(p);     /* Drucker-Konfiguration */ 
                    break; 
                case 'c':
                    set_cntrl(p);   /* Kontroll-Feld */ 
                    break;
            }
    }
    Mfree(infb);    /* Puffer freigeben */ 
    return E_OK;    /* alles ok */
}

/* Alert-Box für GEMDOS-Fehler 'err' ausgeben */ 
void alrt_tos(err) 
int err;
{   if (err < -32)
        form_error(-31-err); /* auf PC-DOS-Fehlercode umrechnen */
}


/* Aktion bei Aufruf des Accessories */ 
int do_work(dskinf) 
char *dskinf;
{
    if (dskinf != NIL)
        shel_put(dskinf,MAXINF); /* Original-Info zurückholen */ 
    return anal_dskinf(); /*Parameter übernehmen*/
}


/* Haupt-Programm */ 
void main()
{   int err;        /* GEMDOS-Fehlermeldung */
    int msg[8];     /* Puffer für AES message */
    int menu_id;    /* Menu-Kennung */
    char *org_inf;  /* Original-DESKTOP . INF */

    appl_init();    /* beim AES anmelden */ 
    menu_id = menu_register(gl_apid,ACCNAME);   /* ins Menu eintragen */ 
    if ((org_inf = get_dskinf()) == NIL)        /* Original-DESKTOP.INF merken */ 
        err = ENSMEM; /*nicht genug Speicher*/
    else
        err = anal_dskinf(); /* Parameter übernehmen */

    while(1)
    {   if (err != E_OK)
        {   alrt_tos(err); /* Fehler als GEM-Alert ausgeben */ 
            err = E_OK;
        }
        evnt_mesag(msg); /* warten bis Accessory angeklickt */ 

        if (msg[0] == AC_OPEN) /* nur AC_OPEN berücksichtigen */ 
            if (msg[4] == menu_id) /* für eigenes Accessory */
                err = do_work(org_inf);
    }
}

DSKINF-Programm


Alex Esser
Aus: ST-Computer 04 / 1989, Seite 88

Links

Copyright-Bestimmungen: siehe Über diese Seite