← ST-Computer 01 / 1989

LineA für Turbo-C

Grundlagen

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.

Installation der LineA-Bibliothek

  • Geben Sie die Definitions-Datei ein und speichern Sie diese im Ordner ‘INCLUDE' unter dem Namen ‘linea.h’.
  • Geben Sie das Assemblerlisting ein und lassen Sie vom Assembler eine Objektdatei erstellen (DR-kompatibel). Das Assemblerlisting ist mit dem GFA-Assembler 1.1 entwickelt worden, dürfte aber auch auf allen anderen Assemblern laufen, die DR-Objekt -code erzeugen können.
  • Speichern Sie das Objektcode-File unter dem Namen ‘linea.o’ im Ordner 'LIB' ab.
  • Erstellen Sie eine Projektdatei entsprechend Listing 2.

Wie funktionierte?

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.

Die Anpassung an andere Compiler

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.

Die Benutzung in eigenen Programmen

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...

Fehlersuche mit dem Debugger

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)

Thomas Schlesinger