Schnittstellen-Dschungel: Neue Rechner - Neue Schnittstellen, Teil 3

Wie im letzten Teil versprochen, dreht es sich dieses Mal ausschließlich um den Serial Communications Controller SCC Z8530 von Zilog. Dieser Baustein ist in den Mega STEs und TTs für zwei der seriellen Schnittstellen bzw. den LAN-Port zuständig.

Wir können hier allerdings nicht auf jedes einzelne Bit dieses Bausteins eingehen, da dies ein ganzes Buch füllen würde (und auch tut!), stattdessen wollen wir hier nur Anregungen geben und die Möglichkeiten dieses Bausteins aufzeigen. Wenn Sie jedoch schon einmal den Z80-SIO programmiert haben, werden Sie sich vermutlich auf dem Z8530 gleich wie zu Hause fühlen, denn die Funktionen des SIO sind im SCC alle enthalten. Wer sich weitergehend mit diesem Baustein beschäftigen möchte, wird an der Anschaffung des Handbuches [1] allerdings nicht vorbeikommen.

Die eierlegende Wollmilchsau

Der SCC bietet all das, was man bisher in Atari-Rechnern vermißt hat, zumindest was die seriellen Schnittstellen angeht (siehe Abb.en 1 und 2). Er enthält zwei unabhängige Kommunikationskanäle, die per Software an einen weiten Einsatzbereich anpaßbar sind. Der SCC ist nicht nur für die bekannten asynchronen Datenformate geeignet, wie sie bisher im ST unterstützt wurden. Eine seiner Stärken ist die Handhabung von synchronen Protokollen. So unterstützt er synchrone byte-orientierte Protokolle wie IBM Bisync sowie synchrone bit-orientierte Protokolle wie HDLC und IBM SDLC. Er ist sogar in der Lage, Diskettenlaufwerke und Magnetbänder zu steuern. Vielleicht findet sich ja jemand, der ein Diskettenlaufwerk mit entsprechendem Interface an eine serielle Schnittstelle des TT anschließt und somit in der Lage ist, beliebige Diskettenformate zu schreiben und zu lesen.

Er kann außerdem verschieden kodierte Signale (NRZ, NRZI, FM1, FM0 und Manchester-Code) entschlüsseln, die unter anderem dazu benutzt werden, gleichstromfreie Daten zu übermitteln.

Weiterhin kann der Baustein selbständig CRC-Prüfsummen generieren und überprüfen, was für ein schnelles Netzwerk unabdingbar ist. Dazu besitzt der SCC 14 Schreib- und sieben Leseregister pro Kanal, die vom Programmierer benutzt werden können, um den SCC an nahezu beliebige Aufgabenstellungen anzupassen.

Die Register

Zehn Schreibregister werden pro Kanal für die Einstellung der verschiedenen Betriebsarten benutzt, zwei für die Sync-Zeichen-Generation und zwei für die Baud-Rate. Zusätzlich gibt es zwei Schreibregister, die beide Kanäle gemeinsam haben. Eins ist das Vector-Register und eines das Master-Interrupt-Control-and-Reset-Register. Vier Leseregister zeigen Status-Informationen an, in zweien läßt sich die Baud-Rate auslesen und über ein Register kann ein Byte aus dem Empfangspuffer gelesen werden. Zusätzlich gibt es zwei Leseregister, die beide Kanäle wieder gemeinsam haben; eins für die Interrupt-Pending-Bits und eins für den Interrupt-Vektor.

Tabelle 1 gibt einen Überblick über die internen Register des SCC.

Abb. 1: Blockschaltbild des SCC

Betriebsarten

Da wir auf die bekannte asynchrone Betriebsart hier nicht näher eingehen wollen, gehen wir gleich zu den synchronen Betriebsarten über.

Synchrone Betriebsarten verschwenden nicht wie asynchrone Bits für Start- und Stop-Bits; alle Bits werden als Daten-Bits benutzt. Da die Start- und Stop-Bits fehlen, muß auf andere Weise festgestellt werden, wo der Byte-Strom beginnt. Hierzu stellt die CPU den Hunt-Modus ein; in diesem Modus werden die ankommenden Bits in das Lese-Register geschoben und dieses ständig mit dem Sync-Zeichen verglichen. Wurde das Sync-Zeichen erkannt, werden die folgenen Bytes empfangen und im Puffer abgelegt. Der Mono-Sync-Modus benutzt hierzu ein 8-Bit-Sync-Zeichen, während der der Bisync-Modus ein 16-Bit-Sync-Zeichen benutzt.

Im externen Sync-Modus wird dagegen auf ein Signal an einem Eingang gewartet, damit der Empfang beginnen kann.

Der SDLC-Modus (Synchronious Data Link Control) benutzt Synchronisations-Zeichen ähnlich wie im Mono- und Bisync-Modus, ist jedoch ein bit-orientiertes Protokoll. Dazu benutzt der SCC einen Frame mit einem Aufbau wie in Abb. 3.

Register Funktionen
WR0 Kommando-Register, Pointer-Bits, CRC-Initialisierung, Resets für verschiedene Modi
WR1 Interrupt-Bedingungen
WR2 Interrupt-Vektor (für beide Kanäle gemeinsam)
WR3 Empfänger-Kontroll-Parameter
WR4 Sender/Empfänger-Parameter
WR5 Sende/Kontroll-Parameter
WR6 Sync-Zeichen (1. Byte) oder SDLC-Adreßfeld
WR7 Sync-Zeichen (2. Byte) oder SDLC-Flag
WR8 Sende-Puffer
WR9 Master Interrupt Control und Reset (für beide Kanäle gemeinsam)
WR10 verschiedene Sender/Empfänger-Kontrollbits, NRZI, NRZ, FM, CRC-Reset
WR11 Takt-Eingang wählen
WR12 Baudrate, unteres Byte
WR13 Baudrate, oberes Byte
WR14 verschiedene Kontrollbits für Baudraten-Generator, etc.
WR15 externe Interrupt-Kontrolle
RR0 Sender/Empfänger-Puffer-Status, externer Status
RR1 besonderer Empfänger-Status
RR2 Interrupt-Vektor
RR3 Interrupt-Pending-Bits
RR8 Empfangs-Puffer
RR10 verschiedene Status-Parameter
RR12 Baudrate, unteres Byte
RR13 Baudrate, oberes Byte
RR15 externe Interrupt-Kontroll-Informationen

Tabelle 1: Die internen Register des SCC

Adresse SCC-Kanal Funktion
FFFF8C81 A Control-Register
FFFF8C83 A Data-Register
FFFF8C85 B Control-Register
FFFF8C87 B Data-Register

Tabelle 2: Register-Belegung des SCC

Abb. 2: Pinbelegung des SCC

Das Datenfeld kann eine beliebige Länge haben (inklusive 0), sollte jedoch nicht zu lang gewählt werden, damit eine Nachricht nicht durch einen Übertragungsfehler verfälscht wird. Die beiden Flags geben einen eindeutigen Start- und Endpunkt des Frames an. Zwei aufeinanderfolgende Frames brauchen nur durch ein Flag getrennt zu werden. Das Flag kann eindeutig erkannt werden, da der SCC nach fünf aufeinanderfolgenden Einsen automatisch eine Null einfügt und diese im Empfängerteil auch automatisch wieder entfernt, ohne daß der Programmierer irgendetwas davon bemerkt.

Der SDLC-Ring-Modus entspricht fast genau dem SDLC-Modus, nur daß hier die Ring-Topologie mit einer Monitor-Station unterstützt wird. Die Monitor-Station schickt hierzu ein Token (EOP - End of Poll, Bit-Muster 11111110) auf den Ring. Alle Stationen, die an den Ring angeschlossen sind, geben normalerweise die empfangenen Bits an der Sendeseite wieder aus (dies macht der SCC automatisch). Sobald sie ein EOP erkennen, wird das letzte empfangene 1-Bit zu einer 0 geändert, bevor es auf der Sende-Seite den Baustein wieder verläßt. Dadurch wird aus dem EOP ein Start-Flag. Nun kann die Station einen Frame generieren, und wenn sie fertig ist, generiert sie ein neues Token (EOP) und schickt es an die Nachfolgestation, die bei Bedarf wiederum ihren Frame anhängen kann.

Die gesamte Flag/EOP-Erkennung wird vom SCC selbständig erledigt und nimmt somit der CPU die Arbeit ab, alle Bits einzeln zu kontrollieren. Stattdessen wird einfach bei erkanntem EOP ein Interrupt generiert (der natürlich auch ignoriert werden kann).

Wie die komplexen synchronen Modi zu programmieren sind, würde den Rahmen dieses Artikels und auch dieser Serie(!) sprengen, deshalb verweisen wir hier wieder auf das Handbuch zum SCC [1].

Im folgenden wollen wir die wichtigsten internen Register des SCC beschreiben. Alle Angaben erfolgen wie immer ohne Gewähr und unter Ausschluß des Rechtsweges.

Inside Atari

Der SCC wird im Atari über nur vier Register angesprochen, zwei pro Kanal (siehe Tabelle 2). Wie können nun über so wenige Register so viele Funktionen ausgelöst werden?

Der Zugriff geschieht in zwei Schritten, ähnlich dem im Atari-Soundchip, wenn auch etwas komplizierter. Dazu ist zunächst eine genauere Kenntnis des WRO-Registers notwendig.

In dieses Register können Befehle eingetragen werden, die den gesamten Baustein betreffen, wie zum Beispiel ein Software-Reset. Die unteren drei Bits (die sogenannten Pointer-Bits) dienen dazu, ein bestimmtes Register im SCC zu adressieren. Dazu wird in WRO die Nummer des Registers geschrieben, auf das im nächsten Schritt zugegriffen werden soll. Gleichzeitig kann in die übrigen Bits von WRO ein Befehl eingetragen werden (siehe unten). Im nächsten Zugriff auf das Control-Register kann dann das gewünschte Register gelesen oder beschrieben werden, wobei gleichzeitig die Pointer-Bits auf Null gesetzt werden.

Soll auf die oberen acht Register zugegriffen werden, muß in WRO das sogenannte Point-High-Kommando eingetragen werden. Aufgrund dessen kann natürlich nicht gleichzeitig noch ein anderes Kommando ausgeführt werden. Da das Point-High-Kommando jedoch den Code 001 (bin) hat, sieht ein Befehls-Byte wie in Abb. 4 aus.

Auf WRO (sowie RRO) kann in nur einem Schritt zugegriffen werden, indem in die Pointer-Bits Null eingetragen wird. Auf WR8 und RR8 kann über das Datenregister ebenfalls in nur einem Schritt zugegriffen werden. Es muß noch darauf hingewiesen werden, daß es im 8530 nur einen Satz Pointer-Bits gibt, den beide Kanäle gemeinsam haben. Es ist somit egal, ob auf das zum Kanal A oder B gehörige WRO zugegriffen wird, die Pointer-Bits sind identisch. Nach dem nächsten Schreiboder Lesezugriff auf das Control-Register werden diese Bits wieder zurückgesetzt, damit der darauffolgende Zugriff wieder das Control-Register erreicht.

Abb. 3: SDLC-Frame-Format
angegeben real
19200 19230
9600 9615
4800 4807
3600 3571
2400 2381
2000 1984
1800 1785
1200 1190
600 596
300 298
200 199
150 149
134 134
110 109
75 74
50 50

Tabelle 4: Baud-Raten des 8530

Ein Beispiel...

Verwirrt? Das ist kein Wunder. Deshalb soll hier ein Beispiel den Zugriff verdeutlichen. Wir wollen eine neue Baud-Rate eintragen:

  1. $0C ins Control-Register eintragen (enthält Point-High-Bits!)
  2. niederwertiges Byte in Control-Register schreiben (und damit in internes Register 12)
  3. $0D ins Control-Register schreiben
  4. höherwertiges Byte in Control-Registerschreiben (ins interne Register 13)

Ein Lesezugriff auf z.B. das „Receive-Condition-Status“-Register (RR1) läuft ähnlich ab:

  1. $01 ins Control-Register schreiben
  2. Control-Register lesen (enthält jetzt den Inhalt von RR1)

Aus Programmierersicht gibt es also 4 Register, in die die CPU schreiben kann. Intern besitzt der Baustein aber mehr Register, die auf die eben beschriebene Art und Weise angesprochen werden können. Von jetzt an werden wir nur noch die internen Registemummern benutzen.

00 001 xxx
       ^— Registernummer 8-15 (nur untere 3 Bits)
   ^----- Point High Kommando
^-------- keine internen Register zurücksetzen

Abb. 4: Aufbau des Point-High-Kommandos

Die internen Register

Wir wollen uns nun den wichtigsten Registern etwas detaillierter widmen; auch hier können wir aufgrund des Umfangs wieder nur eine Auswahl treffen.

Der Aufbau des internen Registers WR0 ist in Abb. 5 beschrieben. Die unteren drei Bits dienen in der beschriebenen Weise der Adressierung der übrigen Register, die nächsten drei Bits können optional noch einen Befehl enthalten. Wird hier das Null-Kommando eingetragen, wird kein zusätzlicher Befehl ausgeführt, sondern nur die Register-Bits gesetzt. Die beiden letzten Bits dienen dazu, verschiedene interne Register zurückzusetzen.

Wie auch der MFP, ist der SCC in der Lage, Vektornummern zu generieren. In WR2 kann eine Vektomummer eingetragen werden, in der die Bits 1 bis 3 je nach Interrupt-Quelle modifiziert werden. Bit 0 ist dabei immer Null. Daraus ergibt sich, daß der SCC 16 Interrupt-Vektoren belegt, obwohl er nur 8 verschiedene Interrupts erzeugen kann; das liegt daran, daß die ungeraden Vektornummem nicht benutzt werden können.

Der SCC belegt im TT die Vektornummern ab 96, also ab Adresse $180. Die Belegung ist aus Tabelle 3 ersichtlich.

Abb. 6 zeigt den Aufbau des WR4-Registers. Mit den oberen beiden Bits kann der Vorteiler für den externen Takt eingestellt werden, bevor er intern zur Baud-Ratengenerierung herangezogen wird. Mit den Bits 3 und 2 läßt sich die Anzahl der Stop-Bits einstellen, mit den Bits 1 und 0 die Parity.

WR8 ist der Sendepuffer; auf WR8 kann allerdings direkt über das Datenregister zugegriffen werden.

In WR12 bzw. WR13 kann die Konstante für die Baud-Ratengenerierung eingetragen werden, wobei WR12 das niederwertige Byte enthält. Die Zeitkonstante berechnet sich folgendermaßen:

Zeitkonstante = (Taktfrequenz /2 * Baud-Rate * Vorteiler) -2

Die Taktfrequenz beträgt auf dem TT 8 MHz, der Vorteiler ist normalerweise auf 16 eingestellt. Daraus ergeben sich für den TT folgende Baud-Raten (siehe Tabelle 4), die über Rsconf() eingestellt werden können.

Natürlich läßt sich durch direkte Programmierung der Register nahezu jede beliebige Baud-Rate einstellen.

Eines der wichtigsten Leseregister ist RRO (siehe Abb. 7). Aus diesem kann u.a. der Status der Sende- und Empfangspuffer sowie der Zustand der Leitungen DCD und CTS abgelesen werden.

RR8 ist das Empfangsdaten-Register, auf das wie auf WR8 direkt zugegriffen werden kann.

Aus RR12 und RR 13 kann die eingestellte Baud-Rate ausgelesen werden.

Fußangeln

Das beendet unsere kurze Rundreise durch die Register des SCC; eines ist jedoch noch zu beachten: Man sollte sich beim Initialisieren an die vom Hersteller vorgegebene Reihenfolge der Register halten, ansonsten kann der Baustein durch höchst merkwürdiges Verhalten glänzen.

Die Initialisierung zerfällt dabei in drei Phasen. Zunächst werden die Operationsmodi eingestellt (Bits pro Zeichen, Parität etc.) sowie die Konstanten geladen (Interrupt-Vektoren, Zeitkonstanten etc.). In der zweiten Phase werden die Hardware-Funktionen freigegeben (Sender, Empfänger, Baud-Ratengenerator). In der dritten Phase schließlich werden dann die verschiedenen Interrupt-Quellen freigegeben.

Zurück in die Zukunft

Zum Schluß wollen wir jetzt noch eine Kleinigkeit aus dem ersten Teil dieser Serie klarstellen.

Es ist bei einigen Lesern offenbar der Eindruck entstanden, daß der eingebaute SCSI-Bus des TT nicht DMA-fähig sei. Dem ist natürlich nicht so! Der gute alte DMA-Chip ist allerdings trotzdem nicht für die SCSI-DMA zuständig, sondern auch weiterhin nur für den ACSI-Port. Der SCSI-Bus des TT wird durch einen NCR 5380 bedient, der DMA-fähig ist. Die XBIOS-Funktionen DMAread() und DMAwrite() nutzen diese Fähigkeit jedoch nicht, sondern sie holen die Bytes einzeln (!) aus dem entsprechenden Datenregister. Also machen DMAread() und DMAwrite() sowie die Harddisk-Boot-Routine (die ihrerseits DAMread() aufruft) keinen Gebrauch vom DMA. Ob jedoch ein Festplattentreiber DMA benutzt oder nicht, hängt ganz allein von seinem Programmierer ab.

Auch in diesem Teil gibt es nachfolgend wieder ein Listing, das den dritten Teil unseres Terminal-Programms bildet; nächsten Monat gibt es dann den abschließenden Teil des Programmes.

Oliver Scholz & Uwe Hax

Literatur:

[1] Zilog Z8030/Z8530 SCC Serial Communications Controller Technical Manual, August 1988

Vektornr. Adresse Beschreibung
96 $180 Kanal B: Sendepuffer leer
98 $188 Kanal B: Statusänderung
100 $190 Kanal B: Zeichen im Empfängerpuffer
102 $198 Kanal B: Empfänger-Sonderbedingung
104 $1A0 Kanal A: Sendepuffer leer
106 $1A8 Kanal A: Statusänderung
108 $1B0 Kanal A: Zeichen im Empfängerpuffer
110 $1B8 Kanal A: Empfänger-Sonderbedingung

Tabelle 3: Vektorbelegung des SCC

Abb. 5: Aufbau des WR0-Registers
Abb. 6: Aufbau des WR4-Registers
Abb. 7: Aufbau des RR0-Registers
/*
 * WINDOW.C
 * Fensterroutinen für TT44TT
 * Copyright (c) 1991 by MAXON
 * Autoren: Oliver Scholz & Uwe Hax 
 */

#inelude <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"

#define GLOBAL extern 
#include "variable.h”

/*
 * Fenster öffnen 
 */

WORD open_window(WORD wind_index)
{
    WORD x.y,w,h,iconidx;

    wind_get(DESKTOP,WF_WORKXYWH,&x,&y,&w,&h) ;

    if (window[wind_index].handle==-1)
    {
        window[wind_index].handle=wind_create(elements,x,y.w,h);
        if (window[wind_index].handle >= 0)
        {
            if (curr_icon == -1)
            {
                curr_device = wind_index; 
                ienable(TRUE);
            }
            top_window = wind_index; 
            window[wind_index].x_corner=0; 
            window[wind_index].y_corner=0; 
            init_terminal(wind_index); 
            wind_set(window[wind_index].handle,WF_NAME,window[wind_index].title);
            wind_set(window[wind_index].handle,WF_INFO,window[wind_index].info); 
            wind_snap(&window[wind_index].x, 
                      &window[wind_index].y, 
                      &window[wind_index].w, 
                      &window[wind_index].h); 
            iconidx=iconlist[wind_index];

            if (zoomflag)
                graf_growbox(newdesk[iconidx].ob_x, 
                             newdesk[iconidx].ob_y, 
                             newdesk[iconidx].ob_width, 
                             newdesk[iconidx].ob_height, 
                             window[wind_index].x, 
                             window[wind_index].y, 
                             window[wind_index].w, 
                             window[wind_index].h);

            wind_info(wind_index); 
            wind_open(window[wind_index].handle, 
                      window[wind_index].x, 
                      window[wind_index].y, 
                      window[wind_index].w, 
                      window[wind_index].h);

            size_slider(window[wind_index].handle); 
            pos_slider(wind_index,VERTICAL); 
            pos_slider(wind_index,HORIZONTAL);
        }
        else
        {
            form_alert(1,NO_WINDOW); 
            return (FALSE);
        }
    }
    else
        wm_topped(window[wind_index].handle);
    return(TRUE);
}

/*
 * Koordinaten des Fensterarbeitsbereiches
 * auf Zeichengrenzen ausrichten 
 */

VOID wind_snap(WORD *x,WORD *y,WORD *w,WORD *h)
{
    WORD wx,wy,ww,wh;

    wind_calc(WC_WORK, elements,*x,*y,*w,*h,&wx,&wy,&ww,&wh);

    wx &= 0xfff8; 
    ww &= 0xfff8; 
    if (hchar==8) 
        wh &= 0xfff8; 
    else
        wh &= 0xfff0;

    wind_calc(WC_BORDER,elements,wx,wy,ww,wh,x,y,w,h);
}

/*
 * Fenster nach vorne bringen
 */

VOID wm_topped(WORD whandle)
{
    WORD index;

    index=get_index(whandle); 
    wind_set(whandle, WF_TOP); 
    if (curr_icon == -1)
    {
        curr_device = index; 
        ienable(TRUE);
    }
    top_window = index;
}

/*
 * Fensterinhalt neuzeichnen 
 */

VOID wm_redraw(WORD whandle,WORD wx,WORD wy,WORD ww,WORD wh)
{
    GRECT t1,t2;
    WORD i, k;
    CHAR out[TERM_WIDTH+1];
    WORD index;
    WORD x,y,h,w;

    t2.g_x=wx; 
    t2.g_y=wy; 
    t2.g_w=ww;
    t2.g_h=wh;

    index=get_index(whandle); 
    cursor(index,CURSOR_OFF);

    wind_get(whandle,WF_FIRSTXYWH,&t1.g_x,
             &t1.g_y,
             &t1.g_w,&t1.g_h); 
    wind_get(whandle,WF_WORKXYWH,&x,&y,&w,&h);

    while (t1.g_w && t1.g_h)
    {
        if (rc_intersect(&t2,&t1))
        {
            /* schnellere Ausgabe */ 
            if (t1.g_x+t1.g_w==x+w) 
                t1.g_w += wchar; 
            clipping(&t1,TRUE);

            for (i=window[index].y_corner,k=0; i<=TERM_HEIGHT; i++,k++)
            {
                /* schnellere Ausgabe */ 
                strcpy(out,&terminal[index].screen[i][window[index].x_corner]); 
                out[w/wchar]=EOS; 
                v_gtext(vdi_handle,x,y+k*hchar+distances[4],out);
            }
        }
        wind_get(whandle,WF_NEXTXYWH,&t1.g_x,&t1.g_y,&t1.g_w,&t1.g_h);
    }
    clipping(&t1,FALSE); 
    cursor(index,CURSOR_ON);
}

/*
 * na, die kennt wohl jeder ..
 */

WORD rc_intersect(GRECT *r1,GRECT *r2)
{
    WORD x,y,w,h;

    x=max(r2->g_x,r1->g_x); 
    y=max(r2->g_y,r1->g_y);
    w=min(r2->g_x+r2->g_w,r1->g_x+r1->g_w); 
    h=min(r2->g_y+r2->g_h,r1->g_y+r1->g_h);

    r2->g_x=x;
    r2->g_y=y;
    r2->g_w=w-x;
    r2->g_h=h-y;

    return((w>x) && (h>y));
}

/*
 * Clipping Rectangle setzen/löschen 
 */

VOID clipping(GRECT *rect,WORD mode)
{
    WORD pxyarray[4];

    pxyarray[0]=rect->g_x;
    pxyarray[1]=rect->g_y;
    pxyarray[2]=rect->g_x+rect->g_w-1;
    pxyarray[3]=rect->g_y+rect->g_h-1;

    vs_clip(vdi_handle,mode,pxyarray);
}

/*
 * Fenster verschieben
 */

VOID wm_moved(WORD *mesg_buff)
{
    wind_snap(&mesg_buff[4],&mesg_buff[5], 
              &mesg_buff[6],&mesg_buff[7]); 
    wind_set(mesg_buff[3],WF_CURRXYWH, 
             mesg_buff[4],mesg_buff[5], 
             mesg_buff[6],mesg_buff[7]);
}

/*
 * Fenster schließen 
 */

VOID wm_closed(WORD whandle)
{
    WORD iconidx;
    WORD widx;
    WORD top_hnd;

    widx=get_index(whandle); 
    window[widx].handle=-1;

    /* für's nächste Offnen alte Pos. merken */
    wind_get(whandle,WF_CURRXYWH,
             &window[widx].x,&window[widx].y,
             &window[widx].w,
             &window[widx].h);

    wind_close(whandle); 
    iconidx=iconlist[widx]; 
    if (zoomflag)
        graf_shrinkbox(newdesk[iconidx].ob_x, 
                       newdesk[iconidx].ob_y, 
                       newdesk[iconidx].ob_width, 
                       newdesk[iconidx].ob_height, 
                       window[widx].x, window[widx].y, 
                       window[widx].w, window[widx].h); 
    wind_delete(whandle);

    wind_get(whandle,WF_TOP,stop_hnd,&dummy,&dummy,&dummy);

    top_window=get_index(top_hnd);

    if (curr_icon == -1)
        curr_device=top_window;

    if (curr_device == -1) 
        ienable(FALSE);
}

/*
 * Fenster auf volle Größe 
 */

VOID wm_fulled(WORD *mesg_buff)
{
    WORD x,y,w,h;
    WORD index;

    index=get_index(mesg_buff[3]);

    if (window[index].fulled)
        wind_get(mesg_buff[3],WF_PREVXYWH,&x,&y,&w,&h);
    else
        wind_get(DESKTOP,WF_WORKXYWH,&x,&y,&w,&h);

    wind_max(&x,&y,&w,&h); 
    wind_snap(&x,&y,&w,&h);
    wind_set(mesg_buff[3],WF_CORRXYWH.x,y,w,h);
    size_slider(mesg_buff[3]); 
    window[index].fulled=!window[index].fulled; 
    adjust(mesg_buff[3]);
}

/*
 * Einer der Pfeile wurde angeklickt 
 */

VOID wm_arrowed(WORD *mesg_buff)
{
    WORD wind_index;
    WORD x,y,w,h;
    WORD xpage,ypage;

    wind_index=get_index(mesg_buff[3]); 
    wind_get(mesg_buff[3],WF_WORKXYWH,&x,&y,&w,&h); 
    xpage=w/wchar; 
    vpage=h/hchar;

    switch (mesg_buff[4])
    {
        case WA_UPLINE:
            if (window[wind_index].y_corner>0)
            {
                cursor(wind_index,CURSOR_OFF); 
                window[wind_index].y_corner--;

                scroll(wind_index,SCROLL_DOWN); 
                pos_slider(wind_index,VERTICAL); 
                cursor(wind_index,CURSOR_ON);
            }
            break;

        case WA_DNLINE:
            if (window[wind_index].y_corner+ypage < TERM_HEIGHT)
            {
                cursor(wind_index,CURSOR_OFF); 
                window[wind_index].y_corner++;

                scroll(wind_index,SCROLL_UP); 
                pos_slider(wind_index,VERTICAL); 
                cursor(wind_index,CURSOR_ON);
            }
            break;

        case WA_LFLINE:
            if (window[wind_index].x_corner > 0)
            {
                window[wind_index].x_corner—-;

                wm_redraw(mesg_buff[3],x,y,w,h); 
                pos_slider(wind_index,HORIZONTAL);
            }
            break;

        case WA_RTLINE:
            if (window[wind_index].x_corner+xpage < TERM_WIDTH)
            {
                window[wind_index].x_corner++;

                wm_redraw(mesg_buff[3],x,y,w,h); 
                pos_slider(wind_index,HORIZONTAL)
            }
            break;

        case WA_UPPAGE:
            if (window[wind_index].y_corner-ypage<0) 
                window[wind_index].y_corner=0; 
            else
                window[wind_index].y_corner -= ypage;
            wm_redraw(mesg_buff[3],x,y,w,h); 
            pos_slider(wind_index,VERTICAL); 
            break;

        case WA_DNPAGE:
            if (window[wind_index].y_corner+2*ypage > TERM_HEIGHT)
                window[wind_index].y_corner=TERM_HEIGHT-ypage,
            else
                window[wind_index].y_corner += ypage;
            wm_redraw(mesg_buff[3],x,y,w,h); 
            pos_slider(wind_index,VERTICAL); 
            break;

        case WA_LFPAGE:
            if (window[wind_index].x_corner-xpage<0) 
                window[wind_index].x_corner=0; 
            else
                window[wind_index].x_corner -= xpage;
            wm_redraw(mesg_buff[3],x,y,w,h); 
            pos_slider(wind_index,HORIZONTAL); 
            break;

        case WA_RTPAGE:
            if (window[wind_index].x_corner+2*xpage > TERM_WIDTH)
                window[wind_index].x_corner= TERM_WIDTH-xpage;
            else
                window[wind_index].x_corner += xpage,
            wm_redraw(mesg_buff[3],x,y,w,h); 
            pos_slider(wind_index,HORIZONTAL); 
            break;
    }
}

/*
 * Vertikalen Slider setzen
 */

VOID wm_vslid (WORD *mesg_buff)
{
    WORD x, y, w, h;
    WORD index;

    index=get_index(mesg_buff[3]);

    wind_get(mesg_buff[3],WF_WORKXYWH,&x,&y,&w,&h); 
    window[index].y_corner=(WORD)((TERM_HEIGHT-h/hchar)*(LONG)mesg_buff[4]/1000L);

    wind_set(mesg_buff[3],WF_VSLIDE,mesg_buff[4]); 
    wm_redraw(mesg_buff[3],x,y,w,h); 
    pos_slider(index,VERTICAL);
}

/*
 * Horizontalen Slider setzen 
 */

VOID wm_hslid(WORD *mesg_buff)
{
    WORD index,
    WORD x, y, w, h;

    index=get_index(mesg_buff[3]); 
    wind_get(mesg_buff[3],WF_WORKXXWH,&x,&y,&w,&h); 
    window[index].x_corner=(WORD)((TERM_WIDTH-w/wchar) *(LONG)mesg_buff[4]/1000L);

    wind_set(mesg_buff[3],WF_HSLIDE,mesg_buff[4]); 
    wm_redraw(mesg_buff[3],x,y,w,h); 
    pos_slider(index,HORIZONTAL);
}

/*
 * Fenstergröße verändern 
 */

VOID wm_sized(WORD *mesg_buff)
{
    wind_max(&mesg_buff[4],&mesg_buff[5],&mesg_buff[6],&mesg_buff[7]); 
    wind_snap(&mesg_buff[4],&mesg_buff[5],&mesg_buff[6],&mesg_buff[7]); 
    wind_set(mesg_buff[3],WF_CURRXYWH,
             mesg_buff[4],mesg_buff[5], 
             mesg_buff[6],mesg_buff[7]); 
    size_slider(mesg_buff[3]); 
    adjust(mesg_buff[3]);
    window[get_index(mesg_buff[3])].fulled=FALSE;
}

/*
 * Fenster nicht über Terminalgröße setzen 
 */

VOID wind_max(WORD *x,WORD *y,WORD *w,WORD *h)
{
    WORD wx,wy,ww,wh;

    wind_calc(WC_WORK,elements,*x,*y,*w,*h,&wx,&wy,&ww,&wh);

    if (ww>wchar*TEPM_WIDTH) 
        ww=wchar*TERM_WIDTH;

    if (wh>hchar*TERM_HEIGHT) 
        wh=hchar*TERM_HEIGHT;

    wind_calc (WC_BORDER,elements,wx,wy,ww,wh,x,y,w,h);
}

/*
 * Informationszeile im Fenster setzen
 */


VOID wind_info (WORD device)
{
    CHAR buffer[20];
    CHAR *p;

    read_port(device);

    get_baud_string(port[device].baudrate,buffer),
    strcpy(window[device] info," "); 
    strcat(window[device].info,buffer); 
    strcat(window[device] info," Baud ");

    if (((port[device].ucr) & 0x60) == 0x20) 
        buffer[0]='7'; 
    else
        buffer[0]='8';

    switch((port[device] ucr) & 0x06)
    {
        case 0x00:
        case 0x02:  buffer[1]='N';
                    break;

        case 0x04:  buffer[1]='0';
                    break;

        case 0x06:  buffer[1]='E';
    }

    if (((port[device].ucr) & 0x18) == 0x18) 
        buffer[2] = '2'; 
    else
        buffer[2] = '1';

    buffer[3]='\0';
    strcat(window[device].info,buffer);

    switch(port[device].flowctrl)
    {
        case P_NONE: p=" kein Protokoll"; 
                     break;

        case P_XON:  p=" XON/XOFF";
                     break;

        case P_RTS:  p=" RTS/CTS";
    }

    strcat(window[device].info,p); 
    if (window[device].handle >= 0)
        wind_set(window[device].handle,WF_INFO,window[device].info);
}

/*
 * Slidergröße einstellen 
 */

VOID size_slider(WORD wind_handle)
{
    WORD x,y,w, h;

    wind_get(wind_handle, WF_WORKXYWH,&x,&y,&w,&h);

    wind_set(wind_handle,WF_VSLSIZE,
            (WORD)min(1000,
            (ULONG)((1000L*h)/hchar)/TERM_HEIGHT));

    wind_set(wind_handle,WF_HSLSIZE,
            (WORD)min(1000,
            (ULONG)((1000L*w)/wchar)/TERM_WIDTH));
}

/*
 * Slider positionieren 
 */

VOID pos_slider(WORD wind_index, WORD vh_flag)
{
    WORD x,y,w,h;
    UWORD pos;

    wind_get(window[wind_index].handle,WF_WORKXYWH,&x,&y,&w,&h);

    if (vh_f1ag==VERTICAL)
    {
        if (TERM_HEIGHT-h/hchar==0) 
            pos=1000; 
        else
            pos=(UWORD)(1000L*window[wind_index].y_corner/(TERM_HEIGHT-h/hchar));

        wind_set(window[wind_index].handle,WF_VSLIDE,pos);
    }
    else
    {
        if (TERM_WIDTH-w/wchar==0) 
            pos=1000; 
        else
            pos=(UWORD)(1000L*window[wind_index].x_corner/(TERM_WIDTH-w/wchar));

        wind_set(window[wind_index].handle,WF_HSLIDE,pos);
    }
}

/*
 * Fensternummer zur Handle ermitteln 
 */

WORD get_index(WORD whandle)
{
    WORD i;

    for (i=0; i<num_aux; i++)
        if(window[i].handle==whandle) 
            return(i); 
    return (-1);
}

/*
 * Bei seitenweisem Scrolling nicht über
 * die Terminalgrenzen hinausgehen 
 */

VOID adjust(WORD whandle)
{
    WORD x,y,w,h,
    WORD c_height,c_width;
    WORD wind_index;

    wind_index=get_index(whandle); 
    wind_get(window[wind_index].handle,WF_WORKXYWH,&x,&y,&w,&h);

    c_height=h/hchar; 
    c_width=w/wchar;

    if (window[wind_index].y_corner+c_height > TERM_HEIGHT)
        window[wind_index].y_corner=TERM_HEIGHT-c_height;

    if (window[wind_index].x_corner+c_width > TERM_WIDTH)
        window[wind_index].x_corner=TERM_WIDTH-c_width;

    pos_slider(wind_index,VERTICAL);
    pos_slider(wind_index,HORIZONTAL);
}


Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]