Schnittstellen-Dschungel: Neue Rechner - Neue Schnittstellen Teil 1

Seit einiger Zeit gibt es die neuen Atari-Rechner, nämlich den TT und den Mega STE; diese zeichnen sich gegenüber den „alten“ STs durch eine wesentlich erweiterte Hardware aus. So hat es sich beispielsweise gezeigt, daß die eine serielle Schnittstelle der STs oft nicht ausreichte. Manch einer wollte gerne eine SCSI-Festplatte/Streamer/Wechselplatte anschließen, anderen reichten die Grafik- und Sound-Qualitäten nicht aus.

Ataris Antwort darauf waren der TT sowie der neue Mega STE. Beide zeichnen sich nicht nur durch das gleiche „gelungene“ Gehäuse-Design aus, sondern sie befriedigen auch die Wünsche von Atari-Benutzern mit höheren Ansprüchen. Leider scheitert die Nutzung der vorhandenen neuen Hardware an der nur dem illustren Kreis eingetragener Entwickler zugänglichen neuen Dokumentation. Dem soll diese Serie zumindest teilweise Abhilfe schaffen.

Dabei wollen wir jedoch nicht auf die eher für Spiele-Programmierer interessanten Grafik- und Sound-Fähigkeiten eingehen, denn das würde den Rahmen dieser Reihe sprengen. Stattdessen werden wir uns mehr auf die neue Hardware sowie deren Programmierung konzentrieren. So wird „ganz nebenbei“ auch ein GEM-Programm entstehen, das die theoretisch beschriebene Programmierung praktisch anschaulich macht und viele bei der GEM-Programmierung auftretende Probleme und Stolpersteine beschreibt und (hoffentlich) auch aus dem Weg räumt. Es handelt sich dabei nicht nur um ein nutzloses Beispielprogramm, sondern um etwas in dieser Form auf Atari-Computern völlig Neues: für jede der bis zu vier seriellen Schnittstellen wird ein eigenes Terminal in einem Fenster verwaltet - und das simultan! Es findet also sozusagen Multitasking innerhalb eines GEM-Programmes in mehreren Fenstern statt, ohne dazu Accessories zu benötigen. Das Programm läuft übrigens auflösungsunabhängig auf jedem Rechner der Atari-Serie, wobei auf einem normalen ST natürlich nur ein Terminalfenster zur Verfügung gestellt wird (es gibt schließlich nur eine serielle Schnittstelle). Auf einem TT (ggf. sogar mit Großbildschirm) dagegen kann das Programm seine volle Leistungsfähigkeit beweisen.

Zunächst beschäftigen wir uns in dieser Serie mit der neuen Hardware und deren Programmierung, anschließend wird das oben erwähnte Programm erläutert und auf die verwendeten Programmiertricks eingegangen. Unter anderem zeigen wir, wie man Icons auf einem eigenen Desktop mit Fenstern und allem, was dazu gehört, verwaltet. Und das ist nicht nur für Besitzer eines TT/STE interessant, sondern auch für viele GEM-Programmierer, die bisher über eigene Menüleisten und Dialoge nicht hinausgekommen sind.

   
$FFFFFA81 Parallel-Port
$FFFFFA83 Flankenregister
$FFFFFA85 Datenrichtung
$FFFFFA87 Interrupt Enable A
$FFFFFA89 Interrupt Enable B
$FFFFFA8B Interrupt Pending A
$FFFFFA8D Interrupt Pending B
$FFFFFA8F Interrupt In-Service A
$FFFFFA91 Interrupt In-Service B
$FFFFFA93 Interrupt Mask A
$FFFFFA95 Interrupt Mask B
$FFFFFA97 Vektor-Register
$FFFFFA99 Timer A Control
$FFFFFA9B Timer B Control
$FFFFFA9D Timer C und D Control
$FFFFFA9F Timer A Data
$FFFFFAA1 Timer B Data
$FFFFFAA3 Timer C Data
$FFFFFAA5 Timer D Data
$FFFFFAA7 Sync Character
$FFFFFAA9 USART Control
$FFFFFAAB Receiver Status
$FFFFFAAD Transmitter Status
$FFFFFAAF USART Data

Tabelle 1: Register-Adressen des MFP2

Ans Eingemachte

Wie schon erwähnt, befassen wir uns zunächst mit den zusätzlichen Schnittstellen des TT und Mega STE. Beide Rechner haben nicht nur ein ähnliches Design, sondern auch ein ähnliches Innenleben. Sowohl TT als auch Mega STE besitzen eine eingebaute SCSI-Schnittstelle, an der die interne Festplatte angeschlossen ist, sowie vier bzw. drei serielle Schnittstellen. Atari ist jedoch bei diesen beiden Rechnern dazu übergegangen, anstelle der 25poligen Sub-D-Buchsen die im PC-Bereich üblichen 9poligen Buchsen zu verwenden. Die Belegung dieser Buchsen ist aus Abbildung 1 ersichtlich.

Im TT ist zusätzlich zu dem im normalen ST schon vorhandenen Multifunktionsbaustein (MFP) ein zweiter MFP eingebaut. Diesen besitzt der Mega STE nicht; daraus erklärt sich der Unterschied in der Zahl der seriellen Schnittstellen. Die beiden übrigen Schnittstellen werden von dem leistungsfähigen Serial Communications Controller (SCC) Z8530 von Zilog verwaltet.

Der erste MFP (ab hier MFP1 genannt) entspricht in Programmierung und Benutzung exakt dem MFP in den alten STs, so daß hier keine weitere Dokumentation notwendig ist. Hier seien deshalb nur die Adressen der speicherabgebildeten Register des neuen MFP sowie der entsprechenden Interrupt-Vektoren genannt (siehe Tabellen 1 und 2). Für weitere Informationen bezüglich des MFP 68901 können Sie [1], [2] und [3] heranziehen, die unter anderem auch diesen Chip ausführlich behandeln.

Abb. 1: Die Pin-Belegung des seriellen Ports (von außen)

MFP, zum zweiten

Der zweite (neue) MFP (ab hier MFP2 genannt) kümmert sich, wie bereits erwähnt, um eine der seriellen Schnittstellen des TT; bei der zu diesem Baustein gehörigen Schnittstelle sind laut [4] leider nur RxD, TxD und GND beschältet. Er ist somit nur mit einem Software-Handshake sinnvoll nutzbar.

Timer D des MFP2 ist wie gewohnt für die Baud-Rate zuständig, und Timer A fällt eine besondere Aufgabe zu. Wie ja bekannt ist, besitzt der 68030-Prozessor einen eingebauten Cache, der u.a. die Ausführung von Schleifen erheblich beschleunigt. Leider hatte Atari in den alten TOS-Versionen einige Warteroutinen durch Herunterzählen eines Prozessor-Registers in einer Schleife implementiert, was schon bei Verwendung eines 68020-Prozessors oder eines Hardware-Caches in einem alten ST zu Problemen führte. Diese Warteschleifen wurden dann nämlich viel zu schnell ausgeführt, was ihrem Sinn natürlich widersprach. Hier kommt derTimer A des MFP2 ins Spiel: Die alten Warteschleifen wurden im TT durch den genannten Timer realisiert. Dadurch ist nun die Wartezeit unabhängig von der Geschwindigkeit des Prozessors.

Der aufmerksame Leser wird sich jetzt vielleicht fragen, was aus diesen Schleifen auf dem Mega STE geworden ist, denn dieser besitzt ja ebenfalls einen Cache und wird mit 16 MHz betrieben, hat jedoch keinen zweiten MFP. Die Lösung ist jedoch verblüffend einfach: Atari benutzt hierzu den Timer A des MFP1 (!!!). Mit anderen Worten: es steht kein Timer mehr zur vollen freien Verfügung (zumindest nicht auf dem Mega STE). Da die erwähnten Warteschleifen jedoch nur benutzt werden, wenn Betriebssystemfunktionen aufgerufen werden, ist das Problem nicht ganz so gravierend, wie es auf den ersten Blick erscheint. Zum einen lassen sich die meisten Timing-Probleme durch Benutzung des Vertical-Blank-Interrupts lösen, zum anderen ist der Timer A solange brauchbar, wie man nicht zwischen Setzen und Ablaufen des Timers eine Betriebssystemfunktion aufruft. Atari empfiehlt jedoch, diesen Timer grundsätzlich nicht zu benutzen.

Timer B und C von MFP2 scheinen jedoch für eigene Benutzung frei zu sein (zumindest bis Atari etwas anderes verlautbart).

Auf die Interrupt-Vektoren des MFP2 kann leider nicht über die XBIOS-Funktion Mfpint() zugegriffen werden, gleiches gilt auch für die Funktion Xbtimer(). Die Vektoren selbst können zwar noch mit der Funktion Setexc() gesetzt werden, die Timer jedoch müssen durch direkten Zugriff auf die Hardware programmiert werden.

Der Schnittsteilen-Multiplexer

Die Benutzung der seriellen Schnittstellen wird allerdings vom Betriebssystem unterstützt; dazu wird eine neue XBIOS-Funktion namens Bconmap() ab TOS-Version 2.00 angeboten. Das zugehörige Binding ist in Turbo-C 2.03 bereits vorhanden, bei anderen C-Compilern muß in der Header-Datei „osbind.h“ nur folgende Zeile ergänzt werden:

#define Bconmap(a) XBIOS(44,a)

Wie kann nun eine einzige Funktion vier (oder mehr?) Schnittstellen unterstützen? Die Lösung ist verblüffend einfach: Es werden die alten BIOS/XBIOS-Funktionen Bconin(), Bconout(), Bconstat(), Bcostat() und Rsconf() mitbenutzt. Und zwar beziehen sich diese Funktionen immer auf die sogenannte „aktuelle“ serielle Schnittstelle AUX:. Diese hat bei den ersten vier genannten Funktionen weiterhin den Index 1, so daß sich alle Programme sowie Betriebssystemfunktionen wie gewohnt verhalten und auf die aktuelle serielle Schnittstelle zugreifen. Um die aktuelle Schnittstelle zu ändern, wird Bconmap() aufgerufen und der Index der Schnittstelle übergeben, die von nun an die „aktuelle“ Schnittstelle sein soll. Dabei wird der Index der bisherigen aktuellen Schnittstelle zurückgegeben. Soll also zum Beispiel die Baud-Rate des MFP2 geändert werden, könnte das entsprechende Code-Fragment etwa folgendermaßen aussehen:

WORD old;
old=Bconmap(8);
Rsconf(...);
Bconmap(old);
Ebene Belegung
15 107
14 106
13 Timer A
12 RS232 Empfangspuffer voll
11 RS232 Empfangsfehler
10 RS232 Sendepuffer leer
9 RS232 Sendefehler
8 Timer B
7 105
6 104
5 Timer C
4 Timer D (Baud-Rate-Generator)
3 I03
2 I02
1 I01
0 IO1

Tabelle 2: Interrupt-Vektoren des MFP2 (Vektornummern 80-95)

Soll nur die aktuelle serielle Schnittstelle ermittelt werden, ist Bconmap() mit dem Parameter -1 aufzurufen. Um auf verschiedenen seriellen Schnittstellen „gleichzeitig“ auszugeben, muß jedoch nicht ständig mit Bconmap() hin- und hergeschaltet werden. Die Schnittstellen haben hierzu eigene „direkte“ Indizes bekommen, so daß sich folgende erweiterte Tabelle ergibt (siehe Tabelle 3).

Index Gerät Beschreibung
0 PRT: Centronics-Schnittstelle
1 AUX: aktuelle Schnittstelle (siehe Text)
2 CON: Konsole (Tastatur und Bildschirm)
3 MIDI Midi-Schnittstelle
4 IKBD Intelligente Tastatur
5 SCREEN Bildschirm

Index | Gerät | TT | Mega STE 6 | AUX1: | Modem 1 | Modem 1 7 | AUX2: | Modem 2 | Modem 2 8 | AUX3: | Serial 1 | Serial 2/LAN 9 | AUX4: | Serial 2/LAN |

Tabelle 3: (Neue) Gerätenummern für BIOS/XBIOS-Funktionen

Nun ist es nicht aus der TOS-Version ersichtlich, wieviele Schnittstellen tatsächlich installiert sind. Auch hier hilft die Bconmap(/-Funktion weiter. Wird sie nämlich mit Parameter -2 aufgerufen, liefert sie einen Zeiger auf eine Struktur namens BCONMAP zurück, die folgendermaßen aufgebaut ist:

struct {
    MAPTAB *maptab;
    WORD maptabsize;
} BCONMAP;

struct
{
    WORD (*Bconstat)();
    LONG (*Bconin)();
    WORD (*Bcostat)();
    VOID (*Bconout)();
    LONG (*Rsconf)();
    IOREC *iorec;
} MAPTAB;

In maptabsize steht die Anzahl der verfügbaren seriellen Schnittstellen; maptab zeigt auf die oben beschriebene Struktur vom Typ MAPTAB der aktuellen Schnittstelle. Darüber wird im XBIOS auf die aktuelle Schnittstelle zugegriffen; eine Umschaltung erfolgt, indem ein interner Zeiger auf eine andere MAPTAB-Struktur gesetzt wird.

Die Online-Hilfe des Turbo-C-Systems liefert übrigens außer den Namen ansonsten nur falsche Angaben zu diesem Thema! Ist diese Funktion (z.B. wegen älterer TOS-Version) nicht vorhanden, so bekommt man nicht die Funktionsnummer 44 zurück, sondern die Adresse auf ein RTS im Bereich des Betriebssystems, das der XBIOS-Handler für nicht vorhandene Funktionen ausführt. Erst bei Funktionen, die die größte Funktionsnummer überschreiten (je nach TOS-Version unterschiedlich), wird die Funktionsnummer zurückgeliefert. Da jedoch in zukünftigen Versionen die höchste Funktionsnummer nahezu beliebig groß werden kann, sollte immer direkt die TOS-Version abgeprüft werden.

Fehler-Nr. Bedeutung
0 OK, kein Fehler
-1 Timeout
-10 Fehler beim Schreiben
-11 Fehler beim Lesen
-15 falsche Gerätenummer
sonst: negativer Fehlercode von SCSI-Gerät

Tabelle 4: Fehlercodes von DMAread und DMAwrite

SCSI für (fast) alle...

Sowohl TT als Mega STE besitzen als erste Atari-Computer der ST-Serie ein eingebautes SCSI-Interface mit daran angeschlossener Festplatte. Obwohl beide Rechner sich in dieser Beziehung gleichen, wurde das „Problem“ SCSI unterschiedlich gelöst. Im Mega STE wurde ähnlich wie in [5] ein Host-Adapter intern an den DMA-Port angeschlossen, der den ACSI-Bus nach SCSI konvertiert. Auf dem Mega STE können demzufolge über den DMA-Bus außer der Festplatte noch sieben weitere Geräte angeschlossen werden. Auf dem TT dagegen ist der DMA-Port vollkommen frei, der SCSI-Port ist davon völlig unabhängig.

Im Gegensatz zu dem SCSI-Bus des Mega STE funktioniert der SCSI-Port des TT nicht im DMA-Betrieb, sondern die CPU übernimmt das komplette SCSI-Protokoll per Software. Das stellt jedoch in der Praxis keinen Nachteil dar, da die CPU durch ihren Cache und die hohe Taktrate ebenso schnell ist wie ein DMA-SCSI-Bus; aus dem selben Grund ist übrigens im TT auch kein Blitter enthalten.

Der Benutzer merkt von dieser Software-Realisierung jedoch nichts, denn der Zugriff auf die SCSI-Schnittstelle geschieht über die XBIOS-Funktionen DMAread() und DMAwrite(), die folgendermaßen definiert sind:

WORD DMAread(LONG sector, WORD
    count, VOID *buffer, WORD devno); 
WORD DMAwrite(LONG sector, WORD
    count, VOID *buffer, WORD devno);

Dabei haben die einzelnen Parameter folgende Bedeutung:

sector: Die Nummer des physikalischen Sektors einer Partition (nicht zu verwechseln mit dem logischen Sektor einer Partition!).

count: Anzahl der zu lesenden bzw. zu schreibenden Sektoren

buffer: Adresse im RAM, von der bzw. zu der die Daten transportiert werden

devno: Die Nummer des angesprochenen Gerätes. Auf dem Mega STE sind die Werte 0-7 gültig, während auf dem TT sich der Bereich von 0-15 erstreckt; hierbei sind die Werte 0-7 den Geräten 0-7 am DMA-Port zugeordnet, während die Werte 8-15 den Geräten 0-7 am SCSI-Bus zugeordnet sind.

Als Rückgabewert erhält man eine Null, falls kein Fehler aufgetreten ist, andernfalls eine Fehlernummer (siehe Tabelle 4).

Die nützlichen Beispiele 1 und 2 demonstrieren, wie man den sonst nicht ohne Assembler-Kenntnisse zugänglichen Root-Sektor einer Festplatte liest bzw. schreibt. Vor Benutzung sollte jedoch sichergestellt sein, daß mindestens die TOS-Version 2.00 vorliegt!

Auch hier sind die zugehörigen Bindings bereits in Turbo-C 2.03 vorhanden.

Für andere Compiler müssen folgende Zeilen in „osbind.h“ ergänzt werden:

#define DMAread(a,b,c,d)
        XBIOS(42,a,b,c,d)
#define DMAwrite(a,b,c,d)
        XBIOS(43,a,b,c,d)

Übrigens bootet der TT die Geräte in der Reihenfolge 8-15 und erst dann 0-7. Damit wird sichergestellt, daß zunächst von der internen SCSI-Platte gebootet wird. Wen die 30sekündige Pause beim Booten stört, der kann diese Warteschleife durch den Druck einer beliebigen Taste abbrechen, sobald die Festplatte bereit ist (das bekannte „Piep-Piep-Prrrrt“); das funktioniert natürlich auf beiden Rechnern. Atari erwähnt dies leider mit keinem Wort, deswegen hier dieser kleine Tip.

Umgezogen...

Noch ein Wort zur Vorsicht: Im TT ist nicht mehr die in den Mega ST/STEs eingebaute Uhr vorhanden, sondern ein anderer Baustein an anderer Adresse. Aber es ist ja ohnehin bekannt, daß nicht direkt auf die Uhren-Register zugegriffen werden soll, sondern nur über die dafür vorgesehenen TOS-Funktionen.

Zum Schluß noch ein Hinweis zum Listing. Es handelt sich dabei um den ersten Teil des eingangs erwähnten Terminal-Programmes (das mit den unglaublichen Fähigkeiten...) und ist allein noch nicht lauffähig. Das Programm wurde mit Turbo C 2.03 entwickelt, sollte aber mit kleinen Änderungen auch mit anderen C-Compilern zu übersetzen sein.

In den nächsten Folgen beschäftigen wir uns dann ausgiebig mit den vielfältigen Möglichkeiten des SCC, der Netzwerke auf dem TT/Mega STE erst möglich macht.

Oliver Scholz & Uwe Hax

Literatur:

[1] Brückmann, Englisch, Gerits: Atari ST Intern, Data Becker Verlag

[2] Jankowski, Rabich, Reschke: Atari ST Profibuch, Sybex Verlag

[3] Motorola 68901 Multifunctional Peripheral Technical Manual

[4] Atari Explorer Januar/Februar ' 91

[5] Reiner Wiechert: Per SCSI zum ST, Teil II: Die Hardware, ST-Computer 2/90, Seite 138 ff.

VOID save_root(char *filename) {
{
    WORD handle;
    BYTE buffer[512];

    if (DMAread(0L,1,buffer,0)==0) 
    {
        handle=Fcreate(filename,0); 
        if (handle>=0)
        {
            Fwrite(handle,512L,buffer); 
            Fclose(handle);
        }
    }
}

Beispiel 1: Lesen des Root-Sektors einer Festplatte

VOID restore_root(char *filename)
{
    WORD handle;
    BYTE buffer[512];

    handle=Fopen(filename,0); 
    if (handle>=0)
    {
        if (Fread(handle,512L,buffer)==512L) 
            if (DMAwrite(0L,1,buffer,0)!=0) 
                Cconws("Fataler Fehler!\r\n"); 
        Fclose(handle);
    }
}

Beispiel 2: Schreiben des Root-Sektors einer Festplatte


/*
 * TT44TT.C
 * Hauptmodul Ereignisverwaltung und Dispatcher
 * Copyright (c) 1991 by MAXON
 * Autoren Oliver Scholz & Uwe Hax 
 */

#include <aes.h>
#include <portab.h>
#include <vdi.h>
#include <stdlib.h>
#include <tos.h>
#include <stdio.h>
#include <string.h>

#include "tt44tt.h"
#include "termdefs.h"
#include "proto.h"
#include "tt44tt.rsh"

#define GLOBAL 
#include "variable.h"

/*
 * Hauptprogramm 
 */

VOID main(VOID)
{
    WORD x,y,w,h;
    WORD i;
    WORD phys_handle;
    CHAR pathbuf[128];
    BCONMAP *bc;

    /* Programmstart und Initialisierung */ 
    if ((gl_apid=appl_init())<0)
    {
        form_alert(1,APP_NOT_STARTED); 
        exit(-1);
    }

    wind_get(DESKTOP,WF_WORKXYWH,&x, &y, &w, &h); 
    phys_handle=graf_handle(&wchar,&hchar,&wbox,&hbox); 
    open_vwork (phys_handle); 
    vqt_fontinfo (vdi_handle, &dummy, &dummy, distances,&dummy,effects); 
    vswr_mode (vdi_handle,MD_REPLACE) ,

    strcpy(inf_path,"A:"); 
    inf_path[0]=(CHAR)(Dgetdrv()+('A')); 
    Dgetpath(pathbuf,0); 
    strcat(mf_path,pathbuf); 
    strcat(inf_path, "\\*.INF"); 
    strcpy(inf_name, "TT44TT.INF"),

    get_tos_version(); 
    get_addresses();

    if (tos_version>=0x200)
    {
        aux_offset=6 ;
        bc= (BCONMAP *) Bconmap (-2); 
        num_aux=bc->maptabsize; 
        if (num_aux=3)
        { /* Mega STE */ 
            iconlist[2]=SCC1; 
            iconlist[3]=MFP2;
            strcpy(window[2].title,window[3].title);
        }
        /* LAN-Port aktiv: eine weniger ... */ 
        if (!(Giaccess(0,14) & 0x80)) 
            num_aux—;
    }
    else
    {
        num_aux=1; 
        aux_offset=1;
    }

    /* nicht benötigte Icons ausblenden */ 
    for (i=num_aux; i<4; i++)
        newdesk[iconlist[i]].ob_flags |= HIDETREE;

    init_ports(port),

    for(i=0; i<num_aux; i++)
    {
        write_port(i); 
        wind_info(i); 
        init_terminal(i),
    }

    wind_update(BEG_UPDATE); 
    hide_mouse();

    /* eigenes Desktop installieren */ 
    newdesk[ROOT].ob_width=x+w; 
    newdesk[ROOT].ob_height=h+y; 
    wind_set(DESKTOP,WF_NEWDESK, newdesk, ROOT); 
    objc_draw(newdesk,ROOT,MAX_DEPTH,x, y, w, h),

    /* Menuzeile anzeigen */ 
    ienable(FALSE);
    menu_icheck(menu,ZOOM,zoomflag); 
    menu_bar(menu,TRUE);

    loadinf("TT44TT.INF",1);

    graf_mouse(ARROW,0L); 
    show_mouse(); 
    wind_update(END_DPDATE);

    events();   /* Hauptschleife */

    /* Programm beenden, alle Fenster schliePen */
    wind_update(BEG_UPDATE);
    hide_mouse();
    for (i=0; i<4, i++)
        if (window[i] handle>=0)
        {
            wind_close(window[i].handle); 
            wind_delete(window[i].handle);
        }
    menu_bar(menu,FALSE); 
    rsrc_free(); 
    show_mouse(); 
    wind_update(END_UPDATE);

    v_clsvwk(vdi_handle); 
    appl_exit(); 
    exit(0);
}
/*
 * Workstation öffnen 
 */

VOID open_vwork(WORD phys_handle)
{
    WORD i;
    static WORD work_in[12]; 
    static WORD work_out[57];

    for (i=0; i<10, i++) 
        work_in[i]=1; 
    work_in[10]=2; 
    vdi_handle=phys_handle;
    v_opnvwk (work_in, &vdi_handle, work_out);
}

/*
 * Resource-Koordinaten umrechnen und
 * Adressen der Baume holen
 */

VOID get_addresses(VOID)
{
    WORD i;

    for (i=0; i<NUM_OBS, i++) 
        rsrc_obfix(rs_object,i) ;

    menu=get_traddr(MENU); 
    newdesk=get_traddr(NEWDESK); 
    port_dial=get_traddr(PORTS); 
    info_box=get_traddr(ABOUTBOX);
}

/*
 * Adresse eines Baumes ermitteln 
 */

OBJECT *get_traddr(WORD tree_index)
{
    WORD i,j;

    for (i=0,j=0; i<=tree_index; i++) 
        while (rs_object[j++].ob_next!=-1);

    return(&rs_object[--j]);
}

/*
 * Hauptschleife Events behandeln 
 */

VOID events(VOID)
{
    WORD exit_flag=FALSE;
    WORD events;
    WORD mesg_buff[8];
    WORD mouse,ymouse,mbutton, clicks;
    WORD iconidx,i,j;
    WORD key;

    do
    {
        events=evnt_multi(MU_MESAG | MU_BUTTON | 
                MU_KEYBD | MU_TIMER,2,1,1, 
                dummy, dummy, dummy, dummy, dummy, 
                dummy, dummy, dummy, dummy, dummy, 
                mesg_buff,
                100,0, /* Timerkonstante 100ms */ 
                &xmouse,&ymouse,&mbutton,
                &dummy,&key,&clicks);

        wind_update(BEG_UPDATE);

        if (events & MU_TIMER)
            for (i=0; i<num_aux; i++) 
                if (Bconstat(i+aux_offset))
                {
                    hide_mouse(); 
                    do_output(i); 
                    show_mouse();
                }

        if (events & MU_KEYBD)
        {
            hide_mouse(); 
            key &= 0xff;
            if (key && (top_window >= 0))
            {
                Bconout(aux_offset+top_window,key); 
                if (key == CR)
                    Bconout(aux_offset+top_window, LF);
            }
            show_mouse();
        }

        if (events & MU_BUTTON)
        {
            hide_mouse();
            if (wind_find(xmouse,ymouse)==DESKTOP)
            {
                /* Icon-Behandlung */
                iconidx=objc_find(newdesk,ROOT,MAX_DEPTH, xmouse,ymouse);
                curr_icon=-1; 
                curr_device=top_window;

                for (i=0; i<4; i++)
                {
                    if (newdesk[iconlist[1]].ob_state & SELECTED)
                    {
                        newdesk[iconlist[i]] ob_state &= ~SELECTED; 
                        draw_icon(iconlist[i]);
                    }
                    if (iconidx==iconlist[i]) 
                        curr_icon=i;

                }

                if (curr_icon != -1)
                    if (clicks==1) /* einfacher Click */
                    {
                        newdesk[iconidx].ob_state |=SELECTED; 
                        draw_icon(iconidx); 
                        curr_device=curr_icon;
                    }
                    else /* Doppelclick */
                    {
                        j=curr_icon; 
                        curr_icon=-1; 
                        open_window(j);
                    }

                if (curr_device==-1) 
                    ienable(FALSE); 
                else
                    ienable(TRUE);

                evnt_button(1,1,0,&dummy,&dummy,&dummy,&dummy);
            }
            show_mouse();
        }

        if (events & MU_MESAG)
        {
            hide_mouse(); 
            switch (mesg_buff[0])
            {
                case WM_REDRAW:
                    wm_redraw(mesg_buff[3],mesg_buff[4], 
                              mesg_buff[5],mesg_buff[6], 
                              mesg_buff[7]);
                    break;

                case WM_MOVED:
                    wm_moved(mesg_buff); 
                    break;

                case WM_SIZED:
                    wm_sized(mesg_buff); 
                    break;

                case WM_NEWTOP: 
                case WM_TOPPED:
                    wm_topped(mesg_buff[3]); 
                    break;

                case WM_CLOSED:
                    wm_closed(mesg_buff[3]); 
                    break;

                case WM_FULLED:
                    wm_fulled(mesg_buff); 
                    break;

                case WM_ARROWED:
                    wm_arrowed(mesg_buff); 
                    break;

                case WM_VSLID:
                    wm_vslid(mesg_buff); 
                    break;

                case WM_HSLID:
                    wm_hslid(mesg_buff); 
                    break;

                case MN_SELECTED:
                    exit_flag=mn_selected(mesg_buff); 
                    break;
            }
            show_mouse();
        }
        wind_update (END_UPDATE);
    }
    while (!exit_flag);
}

/*
 * ein Icon zeichnen, dabei Rechteckliste
 * des Desktops beachten 
 */

VOID draw_icon(WORD iconidx)
{
    GRECT t1, t2;

    objc_offset(newdesk,iconidx,&t2.g_x,&t2.g_y); 
    t2.g_w=newdesk[iconidx].ob_width; 
    t2.g_h=newdesk[iconidx].ob_height;

    wind_get(DESKTOP,WF_FIRSTXYWH,&t1.g_x,&t1.g_y,
                &t1.g_w,&t1.g_h); 
    while (t1.g_w && t1.g_h)
    {
        if (rc_intersect(&t2,&t1))
            objc_draw(newdesk,iconidx,MAX_DEPTH, t1.g_x,t1.g_y,t1.g_w,t1.g_h); 
        wind_get(DESKTOP, WF_NEXTXYWH, &t1.g_x, &t1.g_y, &t1.g_w, &t1.g_h);
    }
}

/*
 * die abschaltbaren Menüeintrage ein/ausschalten 
 */

VOID ienable(WORD flag)
{
    menu_ienable(menu,PORT,flag); 
    menu_ienable(menu,OPEN,flag); 
    menu_ienable(menu,CLOSE,flag);
}

/*
 * angewählten Menueintrag auswerten 
 */

WORD mn_selected(WORD *mesg_buff)
{
    WORD temp,button;
    CHAR path[128],*ptr;

    switch(mesg_buff[4])
    {
        case LOADINF: /* Inf-Datei laden */
            fileselect(inf_path,inf_name,&button, "INF Datei laden");

            if (button)
            {
                strcpy(path,inf_path); 
                ptr=strrchr(path,'\\'); 
                if (ptr)
                    *(ptr+1)=EOS; 
                strcat(path,inf_name); 
                loadinf(path, 0) ;
            }
            break;

        case SAVEINF: /* Inf-Datei speichern */
            fileselect(inf_path, inf_name, &button, "INF Datei sichern");
            if (button)
            {
                strcpy (path, inf_path); 
                ptr=strrchr(path,'\\'); 
                if (ptr)
                    *(ptr+1)=EOS; 
                strcat(path,inf_name); 
                saveinf(path);
            }
            break; 

        case ZOOM: /* Zoomboxen */
            zoomflag = (!zoomflag); 
            menu_icheck(menu,ZOOM,zoomflag); 
            break;

        case OPEN: /* Fenster öffnen */
            if (curr_icon!=-1)
            {
                temp=curr_icon; 
                curr_icon=-1; 
                open_window(temp);
                newdesk[iconlist[temp]].ob_state &= ~SELECTED; 
                draw_icon(iconlist[temp]);
            }
            break;

        case CLOSE: /* Fenster schließen */
            if (curr_icon!=-1)
            {
                temp=curr_icon; 
                curr_icon=-1;
                if (window[temp].handle!=-1)
                    wm_closed(window[temp].handle); 
                newdesk[iconlist[temp]].ob_state &= ~SELECTED; 
                draw_icon(iconlist[temp]);
            }
            else
            {
                if (window[top_window].handle!=-1)
                    wm_closed(window[top_window].handle);
            }
            break;

        case QUIT /* Program beenden */ 
            return(TRUE);

        case ABOUT: /* About-Dialog anzeigen */
            show_info(); 
            break;

        case PORT: /* Port konfigurieren */
            read_port(curr_device); 
            if (conf_port(&port[curr_device]))
            {
                write_port(curr_devioe); 
                wind_info(curr_device);
            }
            if (curr_icon!=-1)
            {
                newdesk[iconlist[curr_icon]].ob_state &= ~SELECTED; 
                draw_icon(iconlist[curr_icon]); 
                curr_icon=-1; 
                curr_device=top_window;
            }
            break;
    }
    menu_tnormal(menu, mesg_buff[3],TRUE); 
    return(FALSE);
}

/*
 * Programminfo ausgeben 
 */

VOID show_info(VOID)
{
    WORD cx,cy,cw,ch;

    form_center(info_box,&cx,&cy,&cw,&ch); 
    form_dial(FMD_START, cx, cy, cw, ch, cx, cy, cw, ch);

    objc_draw(info_box,ROOT,MAX_DEPTH,cx, cy, cw, ch);

    show_mouse(); 
    form_do(info_box,0); 
    hide_mouse();

    info_box[ABOUTOK].ob_state &= ~SELECTED; 

    form_dial(FMD_FINISH,cx,cy,cw,ch,cx,cy,cw,ch);
}

/*
 * je nach TOS-Version andere Fileselectbox
 * aufrufen 
 */

WORD fileselect (CHAR *inpath, CHAR *insel,
                 WORD *exbutton, CHAR *label)
{
    WORD retcode;
    show_mouse();

    if (tos_version > 0x104)
        retcode=fsel_exinput(inpath, insel, exbutton, label );
    else
        retcode=fsel_input(inpath,insel,exbutton);

    hide_mouse(); 
    return(retcode);
}

/*
 * Tos-Version ermitteln 
 */

VOID get_tos_version(VOID)
{
    VOID *_ssp;
    SYSHDR **hdr,

    _ssp=(VOID*) Super(0L); 
    hdr=(SYSHDR **)_sysbase; 
    tos_version=(*hdr)->os_version;
    Super (_ssp);
}

/*
 * INF-Datei schreiben 
 */

VOID saveinf(CHAR *pfad)
{
    WORD handle,error,i;

    error=FALSE;

    for (i=0; i<4; i++)
        if (window[i].handle >= 0)
            wind_get(window[i].handle,WF_CURRXYWH,
                     &window[i].x,&window[i].y,
                     &window[i).w,&window[i].h);

    if ((handle=Fcreate(pfad,0)) >= 0 )
    {
        if (Fwrite(handle,sizeof(CONF_RS)*4,&port) == (sizeof(CONF_RS)* 4)) 
            if (Fwrite(handle,sizeof(WINDOW)*4,&window) == (sizeof(WINDOW)*4))
            {
                if (Fwrite(handle,sizeof(WORD),&zoomflag) !=(sizeof(WORD))) 
                    error=TRUE;
            }
            else
                error=TRUE;
        else
            error=TRUE;
        Fclose(handle);
    }
    else
        error=TRUE;

    if (error)
        form_alert(1, INF_WRERR);
}

/*
 * INF Datei lesen 
 */

VOID loadinf (CHAR *pfad,WORD init)
{
    WORD handle, error,i;
    CONF_RS lport[4];
    WINDOW lwindow[4];

    error=FALSE;

    if ((handle=Fopen(pfad,0)) >= 0 )
    {
        if (Fread(handle,sizeof(CONF_RS)*4, &lport)== (sizeof(CONF_RS)*4)) 
            if (Fread(handle,sizeof(WINDOW)*4,&lwindow) ==(sizeof(WINDOW)*4))
            {
                if (Fread(handle,sizeof(WORD),&zoomflag) !=(sizeof(WORD))) 
                    error=TRUE,
            }
            else
                error=TRUE;
        else
            error=TRUE;

        Fclose(handle);
    }
    else
        error=TRUE;

    if (error && !init)
        form_alert(1,INF_RDERR);

    if (!error)
    {
        for (i=0;i<num_aux;i++)
        {
            if (window[i].handle != -1)
                wm_closed(window[i].handle); 
            port[i]=lport[i]; 
            write_port(i);

            window[i]=lwindow[i]; 
            window[i].fulled = FALSE; 
            wind_info(i);

            if (window[i].handle != -1)
            {
                window[i].handle = -1; 
                open_window(i),
            }
        }
        menu_icheck(menu,ZOOM,zoomflag);
    }
}

/*
 * Bconmap für alle!
 */

LONG _bconmap(WORD devno)
{
    LONG old;

    if (tos_version>=0x200) 
        old=Bconmap(devno); 
    else
        old=1L; /* AUX */ 

    return(old);
}


Aus: ST-Computer 09 / 1991, Seite 117

Links

Copyright-Bestimmungen: siehe Über diese Seite