ST-Ecke - Line-A, Teil 1: Auf ganzer Linie A oder Wie man einer Linie das Laufen lehrt

Nun hat der ST schon ein paar Jährchen auf dem Buckel: Am Anfang meiner ‘ST-Karriere’ hörte ich irgendwann einmal, daß unser Freund ein paar Grafikroutinen (genannt LINE-A-Routinen) eingebaut haben soll, die angeblich sehr schnell seien. Das Wort ‘binding’ (sprich beinding) konnte mir kaum jemand erklären, geschweige denn, daß ich jemanden fand, der mir etwas Genaueres über diese Grafikroutinen hätte sagen können. Nach ein paar Monaten wurde das sagenhafte ‘long awaited LINE A document’ (langerwartete LINE-A-Dokument) von ATARI an alle eingetragenen ST-Entwickler verschickt. Was darauf folgte, war das Chaos...

Ärgernis

Vergleiche ich mein heutiges Wissen mit dem Inhalt dieses ‘Dokuments’, so ziehe ich meinen Hut vor denen, die damals schon einen Großteil der Routinen zum Laufen gebracht haben. Dieses Schriftstück ist leider so unvollständig und einfach schlecht, daß sogar eine Routine überhaupt nicht erwähnt wurde. Ein bekanntes Buch für den ATARI ST war gar der Meinung, daß zwei Routinen nicht einmal funktionierten, da sie bei ihren Tests nur Abstürze ernteten. Glücklicherweise ist mit den Jahren die Erfahrung und die Informationsflut gewachsen, so daß die Informationen im ‘Profi-Buch’ wenigstens fast vollständig sind (bis auf Kleinigkeiten). Trotzdem habe ich die Erfahrung gemacht, daß das Aufzählen der verwendeten Parameter in keinster Weise reicht, so daß ich (auch wenn ich nicht unbedingt ein Maßstab bin) einige Tage gebraucht habe, alle Routinen komplett zum Laufen zu bringen. Über sogenannte LINE-A-Bindings (Einbindung und damit ermöglichtes Nutzen der Routinen) habe ich mich schon oft geärgert, da sie meist ohne zu Überlegen kreiert wurden. Der Erfolg war, daß teilweise viel zu viele Parameter übergeben wurden, und, was viel schlimmer ist, die Bindings unvollständig waren, so daß fast die Hälfte der Routinen fehlte.

Wenn man nur will...

... geht alles, oder? Ich möchte in den nächsten Folgen der ST-Ecke LINE-A von Grund auf vollständig implementiert erklären, wobei es mir wichtig war, daß die Routinen möglichst sinnvoll aufgerufen werden, denn was soll es beispielsweise, immer wieder den Textstil vor jedem Zeichen zu setzen, wenn man einen Text ausgeben möchte? Also lassen wir uns nicht aufhalten und stürzen uns auf ganzer Linie ins Getümmel.

Ach ja, bevor ich’s vergesse: Ich habe den Assembler-Teil bewußt auf ein Minimum reduziert, um die Lesbarkeit zu erhöhen. Außerdem gibt es zu JEDER Routine ein kleines Beispiel, um die Anwendung der einzelnen Routinen zu beschreiben. Die Routinen sind in MEGAMAX LASER C entwickelt worden, können aber auch mit MEGAMAX C verwendet werden. Die einzelnen Beispiele sind in ‘Listings’ unterteilt - dabei bedeutet dies (siehe beispielsweise Listing 2) nicht, daß alles ein zusammenhängendes Listing ist. Sie sehen sicherlich, daß zunächst die Eingabe-/Ausgabewerte der zu besprechenden Routine, dann die eigentliche Routine und darauf die Anwendung abgebildet sind.

Das Konzept von LINE-A

Wichtig ist folgendes: Man sollte immer nur dann auf die LINE-A-Grafikroutinen zugreifen, wenn es unbedingt erforderlich ist. Das heißt, GEM sollte immer höchste Priorität wegen der Kompatibilität haben. Da LINE-A auf unterster Betriebssystemebene anzusiedeln ist, braucht man wohl kaum zu erwähnen, daß diese Routinen auf anderen Rechnern nicht zu finden sind. Warum dann überhaupt LINE-A-Routinen benutzen? Die Grafikroutinen sind verglichen mit GEM unschlagbar schnell, wenn auch nicht ganz so flexibel, und auch im Interrupt benutzbar. Man kann einfach nicht alles haben.

‘Trickreich...werden wir’s angehen', haben sich die ATARI-Entwickler gedacht, und haben die Hauptgrafikroutinen des Rechners über einen kleinen ‘Trick’ des 68000-Prozessors bedienbar gemacht. Jedem Maschinensprachebefehl entspricht eine Zahl, die der Prozessor interpretieren kann. Als die Firma MOTOROLA den MC68000 entwickelte, achtete sie darauf, daß sie keine Befehlszahlenkodierungen benutzte, deren Befehlswort Bits 15 bis 12 mit den Werten 10 bis 15 beginnen- diese Kodierungen sollten nämlich ganz besondere Eigenschaften zugeordnet bekommen. Wird von dem Prozessor ein Befehl entdeckt, der mit einer der Zahlen anfängt, so springt er an bestimmte vom Programmierer vorher definierte Stellen. Dabei wurde damals schon festgelegt, daß die $F0-Kodierung für spätere Koprozessorbefehle von Programmierern freigehalten werden sollte, was ATARI dummerweise nicht tat, so daß es einige Probleme bei einer vernünftigen Koprozessoreinbindung gibt. Werden wir etwas konkreter! Entdeckt die CPU einen Befehl, der mit $A0 anfängt, schaut sie an der Adresse 10*4=40 nach und findet dort wiederum eine Adresse, an die er dann verzweigt. Dort kann dann der Befehl genauer untersucht werden, so daß man beispielsweise im unteren Byte des Befehls eine Zusatzinformation zur Befehlsunterscheidung (Beispiel: $A000, $A001, $A002, $F043 usw...) unterbringen kann. Der Vorteil dieses gesamten Verfahrens ist, daß man sehr schnell und geschickt Routinen aufrufen kann, von denen nur der Prozessor über einen Zeiger weiß, wo sie sich befinden. Sicherlich werden Sie sich schon gefragt haben, warum ich oben dauernd von ‘LINE-A’-Grafikroutinen gesprochen habe. Das liegt daran, daß ATARI als Aufruf der Grafikroutinen die von MOTOROLA nicht benutzten A-Befehlscode (LINE A bedeutet soviel wie Zeile A und damit 10. Zeiger der Prozessor-Zeiger-Tabelle) definiert haben, so daß das Ziehen einer Linie beispielsweise über den Maschinencodebefehl $A003 verursacht wird. Hätte ATARI damals den Vektor B (dezimal 11) auserkoren, würden die Grafikroutinen heute sicherlich Line-B-Routinen heißen. Nachdem wir prinzipiell wissen, wie eine Routine angesprungen wird, stellt sich die Frage: “Wie werden einer Routine Parameter übergeben?”

typedef struct {
   int  PLANES;         /* Anzahl der Bildebenen */
   int  WIDTH;          /* Breite in Bytes */
   int  *CONTRL;        /* GEM-Variablen */
   int  *INTIN;         /* Eingabe-Feld */
   int  *PTSIN;         /* Eingabe-Punkte */
   int  *INTOUT;        /* Ausgabe-Feld */
   int  *PTSOUT;        /* Ausgabe-Punkte */
   int  COLBIT_0;       /* Bit 0 der Farbe */
   int  COLBIT_1;       /* Bit 1 der Farbe */
   int  COLBIT_2;       /* Bit 2 der Farbe */
   int  COLBIT_3;       /* Bit 3 der Farbe */
   int  LSTLIN;         /* letzen Punkt zeichnen? */
   int  LNMASK;         /* Linienmuster */
   int  WRMODE;         /* Zeichenmodus */
   int  X1;             /* erster Punkt: X */
   int  Y1;             /* Y */
   int  X2;             /* zweiter Punkt Y */
   int  Y2;             /* Y */
   int  *PATPTR;        /* Zeiger auf Füllmuster */
   int  PATMSK;         /* Musterhöhe */
   int  MFILL;          /* alle Farbebenen benutzen? */
   int  CLIP;           /* Clipping? */
   int  XMINCL;         /* Koord.des Clip-Bereichs:*/
   int  YMINCL;         /* links obere Ecke */
   int  XMAXCL;         /* rechter untere */
   int  YMAXCL;         /* Ecke */
   int  XDDA;           /* immer auf 0x8000 setzen */
   int  DDAINC;         /* Änderung der Skalierung? */
   int  SCALDIR;        /* Skalierungsrichtung */
   int  MONO;           /* gleiche Breiten? */
   int  SOURCEX;        /* Quellbildkoordinaten X */
   int  SOURCEY;        /* Y */
   int  DESTX;          /* Zielbildkoordinaten X */
   int  DESTY;          /* Y */
   int  DELX;           /* Buchstabenabstände X */
   int  DELY;           /* in Y-Richtung */
   unsigned int *FBASE; /* Zeiger a.Zeichensatz */ 
   int  FWIDTH;         /* Breite */
   int  STYLE;          /* Zeichenstil: kursiv, hell etc. */
   int  LITEMASK;       /* Maske für Hellschrift */
   int  SKEWMASK;       /* Maske für Schrägschrift */
   int  WEIGHT;         /* Fettschriftänderung */
   int  ROFF;           /* rechter Buchtstabenoffset */
   int  LOFF;           /* linker Buchtstabenoffset */
   int  SCALE;          /* Skalierungsfaktor */
   int  CHUP;           /* Drehrichung */
   int  TEXTFG;         /* Text-Vordergrundfarbe */
   long SCRTCHP;        /* Puffer für Texteffekte */
   int  SCRPT2;         /* Mitte im Textpuffer */
   int  TEXTBG;         /* Text-Hintergrundfarbe */
   int  COPYTRAN;       /* Flag für a00e: opaque/transparent */
   long SEEDABORT;      /* Zeiger auf Zeilenende-Routine */
} LINE_A;

Listing 1: Struktur der Line-A-Variablen

Futter für das Tier

Um die Routinen auch mit Parametern zu versorgen, wurde von ATARI ein Speicherbereich festgelegt, in dem eine Sammlung von Variablen zu finden ist. In diese Variablen werden vor dem Aufruf der Routine die Parameter hineingeschrieben. Die Struktur dieser Variablen finden Sie in Listing 1, das Sie bitte unter dem Namen ‘LINE_A.H’ als Include-Datei abspeichern - später kommen noch andere Strukturen zu dieser Include-Datei hinzu.... Bei der Gelegenheit sollten Sie auch gleich Listing 2 mit abtippen, das den Kopf für unser Demoprogramm enthält. In den weiteren Listings finden Sie das Stichwort Aufruf. Die unter diesem Begriff stehenden Aufrufe sind in die main()-Routine von Listing 2 einzubauen, die Routinen selbst finden hinter main() ihren Platz.

Nun fehlt eigentlich nur noch das Wissen, wie man herausbekommt, wo diese Variablen stehen.

#include <line_a.h>
#include <gemdefs.h>
#include <stdio.h>
#include <osbind.h>

#define BIG_CHAR 16        /* größtes Zeichen, sollte mind. max (s. set_line_font) */
                           /* aller Zeichensätze sein */

#define void /**/          /* Nur definieren, falls nicht bekannt */

#ifndef abs                /* Makro für Absolutbetrag */
   #define abs(x) (x<0) ? x*-1:x
#endif

LINE_A *linea, *a000();    /* Zeiger auf Line-A-Variablen */
long *fonts, *routines;    /* Zeiger auf Routinen und Fonts */

int scratch_buf[BIG_CHAR*2];     /* Puffer für Texteffekte */
MFDB bildschirm={0L,640,400,40,0,1,0,0,0}; /* Monochrom */

int fill[]={ /* Füllmuster, 16 Zeilen hoch */
0x2E74,0xA5A5,0x300C,0x9C39,
0xCFF3,0x4812,0x6426,0xD3CB,
0xD3CB,0x6426,0x4812,0xCFF3,
0x9C39,0x300C,0xA5A5,0x2E74};

                           /* Ein Ufo-Sprite */ 
int sprite_array[]={ 0x0000,0x0000,0x0001,0x0002,0x0005,
                     0x0000,0x0000,0x0FF0,0x0000,
                     0x1FF8,0x0FF0,0x3FFC,0x1FF8,
                     0x7FFE,0x3FFC,0xFFFF,0x7FFE,
                     0x4002,0x2AAC,0x3FFC,0x1FF8,
                     0x1FF8,0x0FF0,0x0FF0,0x0000,
                     0x0000,0x0000,0x0000,0x0000,
                     0x0000,0x0000,0x0000,0x0000,
                     0x0000,0x0000,0x0000,0x0000};

                           /* Eine Maus-Maus */ 
int mouse[]= { 0x0000,0x0008,0x0001,0x0000,0x0001,
               0x0000,0x0030,0x0038,0x0FFC,
               0x1FFC,0x3FFC,0x6FFC,0xFFFC,
               0xFFF8,0xFFF8,0x0000,0x0000,
               0x0000,0x0000,0x0000,0x0000,
               0x0000,0x0000,0x0010,0x0028,
               0x0F88,0x1FC8,0x2FF8,0x7FF8,
               0xFFF0,0x1040,0x0000,0x0000, 
               0x0000,0x0000,0x0000,0x0000};

main()
{
   /* Hier kommen die unter AUFRUF aufgeführten Aufrufe hinein */
}

Listing 2: Der Hauptteil für unsere Demos

A000, denn aller Anfang...

... ist in diesem Fall leicht. Bevor überhaupt eine einzige Grafikroutine aufgerufen wird, hat man sich zunächst einmal die Adresse der Line-A-Variablen zu besorgen, was mit dem Maschinenbefehl A000 geschieht. Zum genauen Verständnis wollen wir uns die Routine ‘LINEA *A000()’ (Listing 3) genauer anschauen. Allein aus der Definition läßt sich schon erkennen, daß sie einen Zeiger auf die Line-A-Variablenstruktur (Listing 1) zurückgeben wird. Zunächst wird der Line-A-Befehl A000 ausgeführt. Nachdem der Rechner dieses ‘Unterprogramm’ durchgeführt hat, kehrt er zurück und hat in Register D0 einen auf die gewünschten Line-A-Variablen, in A1 einen Zeiger auf die Systemzeichensätze des ST und in A2 einen auf die Adressen der Line-A-Routinen stehen. Diese Werte werden nun in die Argumentvariablen der C-Funktionsübergabe geschrieben, die Adresse der Line-A-Variablen befindet sich in D0 und wird daher als Rückgabeparameter verwendet. Dieser Rückgabeparameter wird im Aufruf von A000() in einen globalen Zeiger auf eine Line-A-Variablenstruktur geschrieben, so daß wir sie später einfach handhaben können. (Möchte man keine globalen Variablen, hat man im weiteren in allen Routinen noch einen zusätzlichen Zeigerparameter einzufügen.) Die Zeiger auf die Zeichensätze werden später bei der Zeichenausgabe noch verwendet, den Zeiger auf die Line-A-Routinen habe ich noch nie gebraucht, da alle Routinen ausschließlich über die Maschinenbefehle A00? aufgerufen werden.

Auf den Punkt gebracht - A001

Der Sinn des Ganzen ist das Zeichnen von Grafiken, und am Anfang einer Grafik (wie auch am Ende eines Satzes) steht der obligatorische Punkt. Daher ist es auch nicht verwunderlich, daß die erste Line-A-Routine (A001) uns das Setzen eines Punktes ermöglicht. Dabei werden als Parameter die eigentliche Punktkoordinate sowie die Farbe des Punktes angegeben. Schauen wir uns also die Routine A001 (Listing 4) an. Bei dieser gibt es im Gegensatz zu A000 nur Eingabe- und keine Ausgabewerte. Die Eingabewerte werden in die Line-A-Variablen hineingeschrieben, deren Anfangsadresse wir durch A000 erfragt haben. Am einfachsten funktioniert das Beschreiben der Variablen durch die Verwendung der in Listing 1 und damit in der Include-Datei 'line_a.h’ zu findenden Struktur LINEA. Die Felder INTIN, PTSIN etc. müssen im Gegensatz zu GEM-Programmen vom Programmierer nicht selbst zur Verfügung gestellt werden, da Line-A intern schon Speicherplatz reserviert hat. Daher weisen wir die Werte einfach den Variablen zu und führen den Maschinenbefehl A001 aus, der den Punkt auf dem Bildschirm setzt. Wie diese Routine gehandhabt wird, sehen Sie in der Demo in Listing 4, in der aufgrund von Zufallszahlen Punkte auf dem Bildschirm dargestellt werden. Der Einfachheit der Zufallszahlenerzeugung halber wurden hier Bereiche von 0-511 und 0-255 verwendet. Es ist klar, daß bei einer Farbdarstellung der Bereich entsprechend angepaßt werden sollte - eine Tatsache, die auch bei den folgenden Beispielen zu beachten ist.

Punkt für Punkt erkannt - A002

Das Erfragen einer Punktfarbe ist genauso einfach wie das Setzen eines Punktes. Dabei werden Line-A die Koordinaten übergeben, deren Farbe herausgefunden werden soll. Die Farbe steht nach dem Aufruf in dem Register D0, wodurch der Wert in C automatisch zurückgegeben wird. In der Demonstrationsroutine sind noch einige andere Aufrufe von Line-A (A008, A009, A00B) zu finden, die der Mausmanipulation dienen und verhindern, daß Line-A bei der Abfrage einen Punkt des Mausbildes erwischt - sie sind hier aber für das Verständnis von A002 nicht wichtig und werden daher erst später genauer erklärt. Auch nicht zu übersehen ist die Art und Weise, wie die Mauskoordinaten und der Status abgefragt werden. Dieses Verfahren ist absolut offiziell (auch wenn hier auf Adressen zugegriffen wird), soll aber auch erst später erklärt werden, also bitte noch ein wenig Geduld.

Eingabe: keine Werte

Ausgabe: Register D0= Zeiger auf Line-A-Variablen 
         Register A1= Zeiger auf die Zeichensätze 
         Register A2= Zeiger auf Line-A-Routinen

Routine:

LINE_A *a000(font_poi, routine_poi) /* Gibt Zeiger auf LINE_A-Struktur zurück */

long *font_poi, *routine_poi;
{
   asm{
      dc.w 0xA000                /* Line-A-Initialisierung aufrufen */
      move.l font_jpoi(A6), A0   /* Zeiger auf die Zeichensätze */ 
      move.l A1, (A0)            /* abspeichern */
      move.l routine_poi(A6), A0 /* Zeiger auf die Line-A-Rout. */ 
      move.l A2, (A0)            /* abspeichern */
      }
}                                /* Zeiger auf LINE-A-Struktur in D0 */

Aufruf:

#include <osbind.h>
#include <line_a.h>

   linea= a000(&fonts,&routines); /* Initialisierung */

   demo_a000();                   /* Demo */

Demonstration:

void demo_a000()
{
   int i;

   printf("\33E\n");    /* Bildschirm löschen */

   printf("Line-A-Variablen              : %8lx\n",linea);
   printf("Adressen der drei Zeichensätze: %8lx\n",*fonts); 
   printf("                              : %8lx\n",*(fonts+1));
   printf("                              : %8lx\n",*(fonts+2));
   printf("Adressen der Line-A-Routinen:\n");

   for (i=0; i<16; i++)
      printf("A00%x: %lx\n",i,*(routines+i));

   Crawcin();        /* Auf Taste warten */
}

Listing 3: Die Initialisierung von Line-A: A000

Viele Punkte ergeben eine Linie - A003

(Nicht nur) deshalb stellt Line-A eine Routine zur Verfügung, die eine Linie zwischen zwei Punkte ziehen kann. Als Eingabeparameter sind die Anfangs- und Endkoordinaten der Linie sowie die Farbe zu nennen. Im Gegensatz zum Setzen eines Punktes wird die Farbe (auch bei den meisten anderen Routinen) nicht in INTIN[0] geschrieben. Stattdessen wird die Farbe in ihre vier Bits aufgespalten und in die Line-A-Variablen COLBIT_0, COLBIT_1, COLBIT_2, COLBIT_3 geschrieben, das heißt, daß diese Variablen gesetzt (=1) werden müssen, wenn das entsprechende Bit der Farbe gesetzt ist. Da dieser Vorgang in Zukunft auch noch in anderen Routinen verwendet wird, habe ich daraus eine eigene Routine set_line_color() programmiert, die in Listing 7 zu finden ist.

Variation ist alles

Als weitere Variationsparameter einer Linie gibt es den Linientyp (LNMASK) und den Schreibmodus (WRMODE). Mit dem Linientyp kann man erreichen, daß eine Linie nicht nur durchgezogen, sondern in den unterschiedlichsten Möglichkeiten gestrichelt wird. Dabei entspricht in der 16 Punkte (Bits) breiten Maske eine 1 einem gesetztem und eine 0 einem nicht erscheinenden Punkt, so daß wir mit binär 1010101010101010 eine gepunktete und mit 0011110000111100 eine gestrichelte Linie erzwingen können. Mit dem Schreibmodus kann der Programmierer Einfluß darauf nehmen, wie die Linie mit dem Zielbild verknüpft werden soll. Dieser Schreibmodus entspricht dem VDI-Schreibmodus und ermöglicht zum Beispiel das Verodern, Exklusiv-Verodern, das direkte Schreiben und vieles mehr.

Bei der Implementation der Line-A-Routinen habe ich Wert darauf gelegt, daß beim Aufruf der eigentlichen Routine nur die Werte übergeben werden, die häufig geändert werden. Alle anderen Parameter einer Routine sollten deshalb vorher explizit gesetzt werden, was in dem Fall auf LNMASK und WRMODE zutrifft. A003 verlangt einen weiteren Parameter, der als Flag dient, ob der letzte Punkt der Linie gezeichnet werden soll oder nicht. Da ich das Zeichnen des letzten Punktes in 99% aller Fälle für sinnvoll halte, habe ich das Setzen von LSTLIN mit in A003 übernommen.

Eingabe: PTSIN[0]= X-Koordinate 
         PTSIN[1]= Y-Koordinate 
         INTIN[0]= Farbe

Ausgabe: keine

Routine:

void a001(x,y, color)       /* Punktkoordinate und Farbe */
int x,y,color;
{
   linea->INTIN[0]=color;  /* Farbe */
   linea->PTSIN[0]=x;      /* Koordinate X */
   linea->PTSIN[1]=y;      /* Koordinate Y */
   asm{
      dc.w 0xa001          /* Line-A-Put_Pixel */
      }
}

Aufruf:

#include <osbind.h>
#include <line_a.h>

demo_a001() /* erzeugt ein paar Punkte */

Demonstration: 

demo_a001()
{

   printf("\33E\n");    /* Bildschirm löschen */

   while(!Cconis())     /* Solange keine Taste gedrückt */
      a001((int)Random()&511,(int)Random()&255,1);

   Crawcin();           /* Zeichen auch abholen */

}

Listing 4: Das Setzen eines Punktes: A001

Geradlinig

Eine Sonderform des normalen Linienziehens ist das Ziehen einer waagrechten Linie, da bei dieser Linienart kein besonderer Linienalgorithmus (Bresenham-Algorithmus) verwendet werden muß. Da es diverse Anwendungen gibt, in denen waagrechte Linien erwünscht sind und diese auch sehr schnell ausgeführt werden, stellt Line-A diese Routine zur Verfügung.

Wenn Sie allerdings denken, daß es sich dabei um eine abgespeckte Version von A003 handelt, haben Sie weit gefehlt, denn A004 kann eine nette Kleinigkeit, die nicht unerwähnt bleiben soll. Wie Sie von A003 schon wissen, kann diese Routine ein Linienmuster schreiben. A004 geht darüber hinaus und kann ein sich änderndes Linienmuster schreiben, wobei mehrere untereinanderliegende Linienmuster ein Flächenmuster ergeben. Dieses Flächenmuster hat sich in einem 16Bit-Feld zu befinden, und die Adresse wird in PATPTR eingetragen; die Anzahl der verwendeten Einträge (Höhe) wird in PATMSK eingetragen. Beachten Sie aber bitte bei der Höhe des Linienmusters, daß hier von der Höhe 1 abgezogen werden muß, so daß der Parameter bei einem 16 Zeilen hohen Muster auf 15 gesetzt werden sollte!

Da ich der Meinung bin, daß man beim Aufruf von A004 selten das Muster ändert, habe ich auch diese beiden Parameter wieder ausgeklammert, um sie getrennt zu setzen (Listing 8). Im weiteren Fortgang unseres Line-A-Streifzuges werden uns noch einige solcher Variablen begegnen. Deshalb möchte ich - ein wenig vorgreifend - die Routine set_line_a() (Listing 9) vorstellen, mit der das Setzen von einigen Line-A-Variablen (unter anderem auch PATMSK und PATPTR) möglich ist.


Eingabe: PTSIN[0]= X-Koordinate 
         PTSIN[1]= Y-Koordinate

Ausgabe: Register D0= Farbe

Routine:

int a002(x,y)         /* Punktabfrage */
int x,y;              /* Koordinaten */
{
   linea->PTSIN[0]=x; /* Koordinaten einsetzen */
   linea->PTSIN[1]=y;

   asm{
      dc.w 0xa002 /* Line-A-Get_Pixel ausführen */
      }           /* D0 wird in C zurückgelief. */
}

Aufruf:

#include <osbind.h>
#include <line_a.h>

   demo_a001()    /* erzeugt ein paar Punkte */
   demo_a002();   /* Demo, für Punktabfrage */

Demonstration:

/* In der folgenden Routine werden auch andere */
/* Routinen und Adressen verwendet, die später */
/* erst erklärt werden. */

demo_a002() /* Punktabfrage-Demonstration */
{
   int *gcur_x, *gcur_y; 
   char *cur_ms_stat;

   gcur_x=(int*)((long)linea-0x25aL); /* akt.X */ 
   gcur_y=(int*)((long)linea-0x258L); /* akt.Y */ 
   cur_ms_stat=(char*) ((long)linea-0x15cL); /* Maustaste */

   a00b(mouse);      /* Mausform ändern */

   while(!Cconis())  /* Bis Tastendruck */
   {
      printf("\33H-> x:%4d y:%4d\n",*gcur_x,*gcur_y);
      if (*cur_ms_stat&3)  /* Maustaste gedrückt */
      {
         a00a(0);          /* Maus aus */
         printf("\33H-> x:%4d y:%4d Punktfarbe:%2d\n",*gcur_x,*gcur_y,a002 (*gcur_x, *gcur_y)); 
         a009(0);          /* Maus ein */
      }
   }
   Crawcin();              /* Zeichen holen */

}

Listing 5: Das Erfragen eines Punktes: A002

Neun auf einen Streich

Mit der Routine set_line_a() lassen sich neun Variablen von Line-A setzen, wobei Sie die ersten vier Parameter schon erkennen. Die ersten zwei Parameter sind die eben erwähnten Mustereinstellungen, der dritte Parameter gibt an, ob alle Farbebenen verwendet werden sollen und der vierte informiert Line-A darüber, wieviele Bits an Farbinformationen in der eingeschalteten Grafik unterstützt werden: bei monochromer Auflösung 1, bei mittlerer 2 und bei geringer 4.

Eingabe: PTSIN[0]= X-Koordinate (Anfang)
         PTSIN[1]= Y-Koordinate (Anfang)
         PTSIN[2]= X-Koordinate (Ende)
         PTSIN[3]= Y-Koordinate (Ende)
         COLBIT_n= Farbe der Linie (siehe set_line_color)
         LNMASK  = Linienmuster

Ausgabe: LNMASK  = verändertes Linienmuster

Routine:

int a003(x1,y1,x2,y2,color)      /* Linie ziehen */
int x1,y1,x2,y2,color;
{
   /* WRMODE und LNMASK wird extern gesetzt */
   linea->X1=x1;                 /* Koordinaten setzen */
   linea->Y1=y1;
   linea->X2=x2;
   linea->Y2=y2;

   set_line_color(color);        /* Farbe aufteilen */

   linea->LSTLIN = 1;            /* letzten Punkt zeichnen */

   asm{
      dc.w 0xa003                /* Linie ziehen */
      }
}                                /* geshiftetes Ergebnis */

Aufruf:

#include <osbind.h>
#include <line_a.h>

   demo_a003();                  /* Linien-Demo */


Demonstration:

demo_a003()
{
   int i,c=1;                    /* Zähler, Farbe */

   printf("\33E\n");             /* Bildschirm löschen */

   a00a(0);                      /* Maus ausschalten */
   linea->WRMODE=MD_XOR;         /* XOR-Zeichenmodus */
   while(!Cconis())              /* Bis Taste gedrückt */
      for(i=0; i<640;c++)        /* Fächer zeichnen */
         a003(i,0,320,399,c);i++;

   Crawcin();                    /* Taste holen */
   a009(0);                      /* Maus einschalten */
}

Listing 6: Das Zeichnen einer Linie: A003

Beschränkt:

Viele der Line-A-Routinen bieten die Möglichkeit, ihr Resultat auf einen Bildschirmausschnitt zu beschränken. Diese Ausschnittsbeschränkung, genannt Clipping (von englisch to clip, was ausschneiden bedeutet), kann man ein- und ausschalten, was durch die Variable CLIP geschieht. In den zugehörigen Variablen XMINCL, YMINCL, XMAXCL, YMAXCL wird der Rechteckbereich in Form der linken oberen und rechten unteren Ecke festgelegt.

Fortsetzung folgt...

Wie Sie sehen, ist Line-A ein zwar nicht unerschöpfliches aber relativ umfangreiches Thema, bedenkt man, daß die komplizierten Routinen erst noch folgen. Deshalb habe ich mich auch entschieden, Line-A über mehrere ST-Ecken verteilt zu erklären, um vielleicht mal ein vollständiges Nachschlagewerk in Sachen Line-A fertigzustellen. Zum Schluß unserer Line-A-Reihe werde ich noch ein hübsches Programm veröffentlichen, das so richtig ausgiebig von LINE-A Gebrauch macht - lassen Sie sich überraschen, es wird Ihnen aber sicherlich gefallen.


Eingabe: Farbe 

Ausgabe: keine 

Routine:

set_line_color(color) /* Farbe in Line-A-Variablen schreiben */

int color;
{
   linea->COLBIT_0 = color&1;       /* Bit 0 d. Farbe */
   linea->COLBIT_1 = (color&2)==1;  /* Bit 1 */
   linea->COLBIT_2 = (color&4)==1;  /* Bit 2 */
   linea->COLBIT_3 = (colors8)==1;  /* Bit 3 */
}

Aufruf:

#include <line_a.h>

   set_line_color(farbe);

Listing 7: Das Setzen der Farbe

Appell

Zum Schluß möchte ich doch noch einmal an alle Line-A-Anwender appellieren, diese Grafikroutinen nur dann zu benutzen, wenn GEM nicht anwendbar ist, was im allgemeinen nur aus dem Interrupt der Fall ist.

Jetzt wünsche ich viel Spaß beim Eintippen und Ausprobieren, das nächste Mal werden wir uns dann mit gefüllten Rechtecken und Vielecken sowie dem Kopieren von Bildbereichen (Blitter hab’ acht) beschäftigen.

SH


Eingabe: X1       = Anfangskoordinate X
         Y1       = Y-Koordinate
         X2       = Endkoordinate X
         COLBIT_n = aufgespaltene Farbe
         WMODE    = Zeichenmodus
         PATPTR   = Zeiger auf Muster
         PATMSK   = Höhe des Musters-1
         MFILL    = Flag: alle Farbbits benutzen?

Ausgabe: keine 

Routine:

void a004(x1,y1,x2, color) /* Anfang,Ende,Farbe */ 
int x1,y1,x2,color;
{
   linea->X1=x1;           /* Koordinaten setzen */
   linea->Y1=y1;
   linea->X2=x2;

   set_line_color(color); /* Farbe auf spalten */

   asm{
      dc.w 0xa004
      }
}

Aufruf:

#include <osbind.h>
#include <line_a.h>

demo_a004 ()
{
   int i;                  /* Zähler */
   int fill1=0xffff;       /* Füllmuster */

   printf("\33E\n");       /* Bildschirm löschen */
   linea->WRMODE=MD_REPLACE;

   set_line_a(fill,15,0,1,0,0,0,0,0); /* kein Clipping bei a004 */

   for (i=0; i<200; i++) /* eig.Muster zeichnen */
      a004(0,i,320,1);

   set_line_a (&fill1,0,0,1,0,0,0,0,0);   /* kein Clipping bei a004 */ 
   for(i=200; i<400; i++) /* Schwarze Fläche zeichnen */
      a004(0,i,320,1) ;
   Crawcin();
}

Listing 8: Das Ziehen einer horizontalen Linie


Eingabe: pattern      = Zeiger auf Muster
         pat_nr       = Höhe des Musters
         plane_flag   = Benutzen aller Farbbits?
         plane_no     = Anzahl der Planes
         clip_flag    = nur Bildausschnitt bearbeiten? 
         cl_x1, cl_y1 = linke obere Ecke des Ausschnitts 
         cl_x2, cl_y2 = rechte untere Ecke des Ausschnitts

Ausgabe: keine 

Routine:

set_line_a(pattern, pat_nr, plane_flag, plane_no, clip_flag, cl_x1, cl_y1, cl_x2, cl_y2) 
int *pattern, pat_nr;      /* Muster */
int plane_no;              /* Anzahl der Planes */
int plane_flag, clip_flag; /* Farbflag, Clip-Flag */
int cl_x1, cl_y1, cl_x2, cl_y2; /* Clip-Ber. */
{
   linea->PATPTR = pattern; /* Muster-Adresse */ 
   linea->PATMSK = pat_nr;  /* Musterhöhe */

   linea->MFILL  = plane_flag;   /* alle Farbbits benutzen? */

   if (plane_no>0)   /* 0 nicht erlaubt !!!!! */
      linea->PLANES= plane_no;

   linea->WIDTH  = (plane_no>2) ? 40:80;   /* Bytebreite des Bildes */

   linea->CLIP   = clip_flag;              /* Clipping ein/ausschalten */
   linea->XMINCL = cl_x1;                  /* Bereich setzen */ 
   linea->YMINCL = cl_y1; 
   linea->XMAXCL = cl_x2; 
   linea->YMAXCL = cl_y2;
}

Aufruf:

#include <line_a.h>

/* Musteradresse, Muster ist 10 Zeilen hoch, alle Farbits benutzen,
  4 Farbplanes (Color), Clipping an, Bereich 10,10 ->102,100 */

   set_line_a(muster, 10, 1, 4, 1, 10,10,102,100);

Listing 9: Das Setzen von verschiedenen LINE-A-Variablen



Aus: ST-Computer 04 / 1989, Seite 100

Links

Copyright-Bestimmungen: siehe Über diese Seite