Die meisten C-Compiler für den ATARI ST werden mit umfangreichen Bibliotheken ausgeliefert, welche die Programmentwicklung wesentlich erleichtern. Jedoch fehlt bei den meisten Bibliotheken die Möglichkeit, die LineA-Funktionen aufzurufen. Für den Programmierer heißt das, will er beispielsweise einen Punkt auf dem Bildschirm setzen, eine eigene Routine zu programmieren oder sich dem langsamen und umständlichen VDI (Applikation anmelden, Workstation öffnen, VDI-Aufruf, Workstation schließen, Applikation abmelden) anzuvertrauen.
Das hier vorliegende Programm soll da Abhilfe schaffen. Es ermöglicht den Aufruf der Line A-Funktionen von Turbo-C aus. Das Programm läßt sich mit geringem Aufwand auch an jeden anderen C-Compiler anpassen.
Die Funktionsweise ist bei allen LineA-Aufrufen prinzipiell gleich und vollzieht sich in drei Schritten. Im ersten Schritt werden die Parameter in die LineA-Variablen eingetragen. Um eine höhere Geschwindigkeit zu erreichen, wird die Turbo-C Option der Registerübergabe ausgenutzt, d.h. die ersten drei Werte-Parameter werden in den Registern D0-D2, die ersten beiden Zeiger in A0-A1 übergeben. Die restlichen Parameter liegen ab 4(sp) auf dem Stack (siehe Kapitel 7 des Turbo-C-Handbuchs). Im zweiten Schritt werden die Register D3-D7 und A2-A6 gerettet (Turbo-C hätte sie gerne unbeschädigt zurückerhalten). Schritt zwei kann auch vor Schritt eins ausgeführt werden, wenn keine Parameter auf dem Stack übergeben werden, da ein ‘movem’-Befehl den Stackpointer verändert (siehe Listing). Im dritten Schritt wird schließlich die entsprechende LineA-Funktion mit ‘.dc.w $a00x’ aufgerufen, und daran anschließend werden die Register restauriert.
Um das Programm mit anderen Compilern zu verwenden (nicht nur C-Compiler verwenden das DR-Format...), müssen Sie das Listing so ändern, daß alle Parameter vom Stack genommen werden. Wo auf dem Stack die Parameter liegen und welche Register gerettet werden müssen, entnehmen Sie bitte dem entsprechenden Handbuch. Beachten Sie bitte auch die Reihenfolge, in der die Parameter übergeben werden.
Laden Sie in jedes Programm, das auf die LineA-Grafik zugreift, die Datei ‘linea.h' mit Hilfe der Preprozessor-Anweisung ‘#include <linea.h>' Die besondere Art der Funktionsdeklaration von Turbo-C (siehe Kapitel 5 des Turbo-C-Handbuchs) ermöglicht es, daß grobe Fehler bei der Parameterübergabe vom Compiler erkannt und angezeigt werden. Achtung: Der Compiler erkennt nur Fehler bei unkorrekter Parameteranzahl und falschen Typen!!!
Beachten Sie bitte, daß die Compiler-Option Registerübergabe eingeschaltet ist.
Gehen Sie sehr sorgfältig bei der Nutzung von LineA vor. Unkorrekte Parameter können zu unerwünschten Resultaten oder gar zum Abstürzen des Betriebssystems führen!
Die Bedeutung der LineA-Parameter zu erklären, würde an dieser Stelle wohl den Rahmen sprengen. Die Bezeichnungen der Parameter entsprechen denen in der Fachliteratur. Einschlägige Bücher wie Data Beckers “ATARI ST Intern” oder das Markt&Technik-Buch “ATARI ST Assemblerbuch” geben jedoch knappe Auskunft über die Bedeutung der Parameter. Am besten probiert man nach dem Studium dieser Bücher ein wenig herum. Vielleicht schreibt ja auch mal jemand einem LineA-Kurs in dieser Zeitschrift...
Wenn sich ein Fehler bei der Benutzung der LineA-Bibliothek partout nicht finden lassen will, verwenden Sie einen Debugger zur Fehlersuche. Befreien Sie das fehlerhafte Programm mit den Befehlen '/’ und '/' von unnötigem Ballast. Beim GFA-Debugger ist folgendes Vorgehen sinnvoll:
Laden Sie das Programm mit dem Befehl ‘e “programmname”' in den Debugger. Auf dem Bildschirm wird unter anderem die Startadresse des Text-Segments angegeben. Schalten Sie die Registerüberwachung mit ‘regon ein. Tracen Sie nun das Programm mit dem Befehl ‘ta Adresse_des_Textsegments’. Anhand des gerade abgearbeiteten Befehls (die LineA -Routinen werden übrigens mit ‘jsr $xxxxxx’ angesprungen) und der Registerinhalte sollten Sie nun den Fehler lokalisieren können. Um die LineA-Parameter zu überwachen, die auf dem Stack übergeben werden, erstellen Sie eine zweite LineA-Datei, bei der Sie hinter jeden ‘move.x x(sp),x(a0)’ - Befehl die Anweisung 'move.x x(sp),d0’ setzen. In D0 können Sie sich so den Stack ansehen. Vergessen Sie nicht, vorher D0 zu retten. Nennen Sie die daraus entstehende Objekt-Datei 'lineadbg.o’ und erstellen Sie eine entsprechende Projektdatei. Wie Sie vielleicht bemerkt haben, sind nicht alle LineA-Routinen implementiert. Wenn Sie Lust haben, vervollständigen Sie die LineA-Bibliothek.
/*
************************************************************
* *
* Definitionsdatei für die LineA-Aufrufe unter Turbo-C *
* *
************************************************************
geschrieben von Thomas Schlesinger
im Oktober 1988
mit Turbo-C 1.0
*/
void Init_LineA (void);
void Put_Pixel ( int x, int y, int c );
int Get_Pixel ( int x, int y ) ;
void Line ( int x1, int y1, int x2, int y2,
int fgbp0, int fgbp1, int fgbp2, int fgbp3,
int lnmask, int wrtmod, int lstlin );
void H_Line ( int x1, int y1, int x2, int fgbp0,
int fgbp1, int fgbp2, int fgbp3,
int wrtmod, int pattern[], intpatmask);
void Fill_Rect ( int x1, int y1, int x2, int y2,
int fgbp0, int fgbp1, int fgbp2,
int fgbp3, int wrtmod, int pattern[],
int patmask, int multfil, intclip,
int xminclp, int xmaxclp, intyminclp,
int ymaxclp);
void Fill_Poly ( int ptsin[], int size, int y,
int fgbp0, int fgbp1, int fgbp2,
int fgbp3, int wrtmod, int pattern[],
int patmask, int multifil, intclip,
int xminclp, int xmaxclp, intyminclp,
int ymaxclp);
void Mouse_on (void);
void Mouse_Off (void);
void Mouse_Form ( int mask[], int cursdt[] );
void Undraw_Sprite ( int save[] );
void Draw_Sprite ( int x, int y, int data[], int save[] );
/* Hinweis:
Die Bedeutung der Parameter kann in einschlägige n Büchern,
wie z.B. Data Becker's >Atari ST Intern< nachgelesen werden.
Die Namen entsprechen den üblicherweise verw Bezeichnungen.
Bei den Funktionen Fill_Poly($a006), Fill_Rect($ aD05) müssen
die Clipping-Parameter mit übergeben werden. Falls nicht
geclippt wird, übergibt man Scheinparameter(Dummies).
*/
Listing 1: Header-Datei zur LineA-Bibliothek
; DEFAULT.PRJ, angepasst an LineA-Bibliothek
; ------------------------------------------
* ; Programmname vom obersten Fenster
= ;
tcstart.o ; Initialisierungs-Modul
* ;
linea.o ; LineA-Bibliothek
tcfltlib.lib ; Fliesskomma-Bibliothek
tcstdlib.lib ; Standard-Bibliothek
tcextlib.lib ; Extended-Bibliothek
tctoslib.lib ; TOS-Bibliothek
tcgemlib.lib ; AES & VDI-Bibliothek
; unnötige Bibliotheken entfernen
Listing 2: Neue Projektdatei
;************************************************
;* *
;* Line-A Grafik-Bibliothek für Turbo-C 1.0 *
;* *
;* *
;* geschrieben von Thomas Schlesinger *
;* am 13.10.1988 *
;* mit dem GFA-Assembler *
;* *
;************************************************
; ** Alle Funktionen dem Linker bekanntmachen **
.GLOBL Init_LineA
.GLOBL Put_Pixel
.GLOBL Get_Pixel
.GLOBL Line
.GLOBL H_Line
.GLOBL Fill_Rect
.GLOBL Fill_Poly
.GLOBL Mouse_On
.GLOBL Mouse_Off
.GLOBL Mouse_Form
.GLOBL Undraw_Sprite
.GLOBL Draw_Sprite
; *** LineA-Variablen definieren ***
contrl equ 4
intin equ 8
ptsin equ 12
fgbp0 equ 24
fgbp1 equ 26
fgbp2 equ 28
fgbp4 equ 30
lstlin equ 32
ln_mask equ 34
wrt_mode equ 36
x1 equ 38
y1 equ 40
x2 equ 42
y2 equ 44
pat_ptr equ 46
pat_msk equ 50
multifil equ 52
clip equ 54
xmin_clp equ 56
ymin_clp equ 58
xmax_clp equ 60
ymax_clp equ 62
; *** Jetzt geht's los! ***
.TEXT
; ***********************************************
; * *
; * Init_LineA initialisiert die LineA-Grafik *
; * *
; ***********************************************
Init_LineA:
movem.l d3-d7/a2-a6,-(sp) ; Register retten
.DC.w $a000 ; Line-A-Init aufrufen
move.l a0,vars ; Adresse der LineA-Varliste
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Ab nach Hause
; **********************************************
; * *
; * Put_Pixel setzt einen Punkt x,y in der *
; * Farbe c *
; **********************************************
Put_Pixel:
movem.l d3-d7/a2-a6/-(sp) ; Register retten
movea.1 vars,a0 ; LineA-Varliste
movea.l intin(a0),a1 ; Intin-Array
movea.l ptsin(a0),a2 ; Ptsin-Array
move.w d0,(a2) ; x-Koordinate
move.w d1,2(a2) ; y-Koordinate
move.w d2,(a1) ; Farbe
.DC.w $a001 ; LineA-Putpixel
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; See you later, alligator !
; **********************************************
; * *
; * Get_Pixel ermittelt die Farbe des *
; * Punktes x,y *
; **********************************************
Get_Pixel:
movem.l d3-d7/a2-a6,-(sp) ; Register retten
movea.l vars,a0 ; LineA-Varliste
movea.l ptsin(a0),a1 ; Ptsin-Array
move.w d0,(a2) ; x-Koordinate
move.w d1,2(a2) ; y-Koordinate
.DC.w $a002 ; LineA-Getpixel (Ergebnis in d0)
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Good bye, crocodile !
; **********************************************
; * *
; * Line zieht eine Linie vom x1,y1 nach *
; * x2,y2 *
; **********************************************
Line:
movea.l vars,a0 ; LineA-Varliste
move.w d0,x1(a0) ; x1
move.w d1,y1(a0) ; y1
move.w d2,x2(a0) ; x2
move.w 4(sp),y2(a0) ; y2 vom Stack
move.w 6(sp),fgbp0(a0) ; Plane 0 vom Stack
move.w 8(sp),fgbp1(a0) ; Plane 1 vom Stack
move.w 10(sp),fgbp2(a0) ; Plane 2 vom Stack
move.w 12(sp),fgbp3(a0) ; Plane 3 vom Stack
move.w 14(sp),ln_mask(a0) ; LineMask v.Stack
move.w 16(sp),wrt_mode(a0) ; WriteModev.Stack
move.w 18(sp),lstlin(a0) ; LstLine v.Stack
movem.l d3-d7/a2-a6,-(sp) ; erst jetzt Register retten
.DC.w $a003 ; LineA-DrawLine
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Adios!
; **********************************************
; * *
; * H_Line zieht eine hor. Linie vom x1,y1 *
; * nach x2,y1 *
; **********************************************
H_Line:
move.l a0,pp ; PatternPointer retten
movea.l vars,a0 ; LineA-Varliste
move.w d0,x1(a0) ; x1
move.w d1,y1(a0) ; y1
move.w d2,x2(a0) ; x2
move.w 4(sp),fgbp0(a0) ; Plane 0 vom Stack
move.w 6(sp),fgbp1(a0) ; Plane 1 vom Stack
move.w 8(sp),fgbp2(a0) ; Plane 2 vom Stack
move.w 10(sp),fgbp(a0) ; Plane 3 vom Stack
move.w 12(sp),wrt_mode(a0) ; WriteMode v. Stack
move.l pp,pat_ptr(a0) ; PatternPointer vom Stack
move.w 14(sp),pat_msk(a0) ; PatternMask vom Stack
movem.l d3-d7/a2-a6,-(sp) ; erst jetzt Register retten
.DC.w $a004 ; LineA-DrawLine
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Auf Wiedersehen!
; **********************************************
; * *
; * Filled Rect zeichnet gefülltes Rechteck *
; * (was sonst?) *
; **********************************************
Fill_Rect:
move.l a0,pp ; PatternPointer retten
movea.l vars,a0 ; LineA-Varliste
move.w d0,x1(a0) ; x1
move.w d1,y1(a0) ; y1
move.w d2,x2(a0) ; x2
move.w 4(sp),y2(a0) ; y2
move.w 6(sp),fgbp0(a0) ; Plane 0 vom Stack
move.w 8(sp),fgbp1(a0) ; Plane 1 vom Stack
move.w 10(sp),fgbp2(a0) ; Plane 2 vom Stack
move.w 12(sp),fgbp3(a0) ; Plane 3 vom Stack
move.w 14(sp),wrt_mode(a0) ; WriteMode v.Stack
move.l pp,pat_ptr(a0) ; PatternPointer vom Stack
move.w 16(sp),pat_msk(a0) ; PatternMask vom Stack
move.w 18(sp),multifil(a0) ; Multifill
move.w 20(sp),clip(a0) ; clip vom Stack
move.w 22(sp),xmin_clp(a0) ; xminclp vom Stack
move.w 24(sp),ymin_clp(a0) ; yminclp vom Stack
move.w 26(sp),xmax_clp(a0) ; xmaxclp vom Stack
move.w 28(sp),ymax_clp(a0) ; ymaxclp vom Stack
movem.l d3-d7/a2-a6,-(sp) ; erst jetzt Register retten
.DC.w $a005 ; LineA-FilledRect
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Go Home!
; **********************************************
; * *
; * Filled Polygon zeichnet ein gefülltes *
; * Polygon *
; **********************************************
Fill_Poly:
move.l a0,arrp ; Koordinaten-Pointer retten
move.l a1,pp ; Pattern-Pointer retten
movea.l vars,a0 ; Linea-Varliste
movea.l contrl(a0),a1 ; contrl[0]
addq.l #2,a1 ; contrl[1]
move.w d0,(a1) ; Anzahl der Punkte in contrl[1]
addq.w #1,d0 ; Punkte+1, da Rückkehr zu Punkt 0
asl.w #1,d0 ; mal 2, da 1 Punkt=2 Koordinaten
movea.l arrp,a1 ; PointerKoord-Array
move.l a2,help ; A2 retten
movea.l ptsin(a0),a2 ; Array-Pointer in ptsin
movea.l a2,a0 ; A2 retten
loop1: move.w (a1)+,(a2)+ ; Kopie User->ptsin Array
dbra d0,loop1 ; Schleifenende
movea.l help,a2 ; A2 restaurieren
movea.l vars,a0 ; A0 restaurieren
move.w d1,y1(a0) ; y-Wert
move.w d2,fgbp0(a0) ; fgbp0
move.w 4(sp),fgbp1(a0) ; fgbp1
move.w 6(sp),fgbp2(a0) ; fgbp2
move.w 8(sp),fgbp3(a0) ; fgbp3
move.w 10(sp),wrt_mode(a0) ; writemode
move.w 12(sp),pat_msk(a0) ; PatternMask
move.w 14(sp),multifil(a0) ; Multifill
move.w 16(sp),clip(a0) ; clip
move.w 18(sp),xmin_clp(a0) ; xminclp
move.w 20(sp),ymin_clp(a0) ; xmaxclp
move.w 22(sp),xmax_clp(a0) ; yminclp
move.w 24(sp),ymax_clp(a0) ; ymaxclp
move.l pp,pat_ptr(a0) ; Pattern Pointer
movem.l d3-d7/a2-a6,-(sp) ; Register retten
.DC.w $a006 ; LineA-Filled Polygon
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Let's go home!
; **********************************************
; * *
; * Mouse_On schaltet die Maus ein *
; * *
; **********************************************
Mouse_On:
movem.l d3-d7/a2-a6,-(sp) ; Register retten
.DC.w $a009 ; LineA-Mouse on
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Adios!
; **********************************************
; * *
; * Mouse_Off schaltet die Maus aus *
; * *
; **********************************************
Mouse_Off:
movem.l d3-d7/a2-a6,-(sp) ; Register retten
.DC.w $a00a ; LineA-Mouse off
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; und ab!
; ************************************
; * *
; * Mouse_Form veraendert die Maus *
; * *
; ************************************
Mouse_Form:
move.l a0,arrp ; Pointer Maske sichern
move.l a1,pp ; Pointer Cursordaten sichern
movem.l d3-d7/a2-a6,-(sp) ; Register retten
movea.l vars,a0 ; LineA-Varlist
movea.l intin(a0),a1 ; intin-Array
move.w #0,6(a1) ; intin[3]=0
move.w #1,8(a1) ; intin[4]=1
movea.l arrp,a0 ; Masken-Pointer
adda.l #10,a1 ; intin[5]
move.w #15,d0 ; 16 Worte kopieren
loop2: move.w (a0)+,(a1)+ ; Kopie Mask->intin[5..20]
dbra d0,loop2 ; Schleifenende
movea.l pp,a0 ; Cursordaten-Pointer
move.w #15,d0 ; 16 Worte kopieren
loop3: move.w (a0)+,(a1)+ ; Kopie Cursd->intin[21..36]
dbra d0,loop3 ; Schleifenende
.DC.w $a00b ; LineA-MouseForm
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; That's it!
; ************************************
; * *
; * Undraw_Sprite löscht ein Sprite *
; * *
; ************************************
Undraw_Sprite:
movem.l d3-d7/a2-a6,-(sp) ; Register retten
movea.l a0,a2 ; Sicherungsbereich in A2
DC.w $a00c ; LineA-UndrawSprite
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Zurück ins Hauptprogramm!
; *************************************
; * *
; * Draw Sprite zeichnet ein Sprite *
; * *
; *************************************
Draw_Sprite:
movem.l d3-d7/a2-a6,-(sp) ; Register retten
movea.l a1,a2 ; Sicherungsbereiche in A2
DC.w $a00d ; LineA-DrawSprite
movem.l (sp)+,d3-d7/a2-a6 ; Register restaurieren
rts ; Good Bye!
; *** Zwischenspeicher ***
.DATA
vars: .DC.l 1
arrp: .DC.l 1
pp: .DC.l 1
help: .DC.l 1
;***** Jetzt ist aber Schluss! *****
.END
Listing 3: Assemblerlisting (GFA-Assembler)