| |
Nutzen von Unterprogrammen aus dem Grundprogramm
Das Grundprogramm beinhaltet viele Unterprogramme, die auch in C-Programmen genutzt werden können. Die Routinen können allerdings nicht direkt wie andere C-Funktionen genutzt werden. Stattdessen erfolgt der Aufruf durch einen Escape-Code beim der Bildschirmausgabe mittels der printf() Funktion von C.
In den Ausgaben 4 und 5 der Zeitschrift LOOP sind diese Möglichkeiten ausgiebig beschrieben worden und sollen hier nicht weiter behandelt werden. Statt dessen wird hier auf eine in Assembler umgesetzte Bibliothek zur direkten Nutzung der Funktionen beschrieben.
Zurück zum Inhaltsverzeichnis der Beiträge
Die Bibliothek GP.S
Die Bibliothek kann nach den eigenen Bedürfnissen erweitert werden, wenn andere Funktionen des Grundprogramms verwendet werden sollen. Hier werden nur Funktionen umgesetzt, die eine Nutzung der Grafik unter C ermöglichen.
Vorteile der Bibliothek GP.S
Obwohl eine zusätzliche Objekt-Datei in das Programm gelinkt wird, ist das ausführbare Programm kleiner als bei der Verwendung der Escape-Codes. Der C-Quellcode ist deutlich einfacher zu lesen, da die Grafik-Funktionen wie normale C-Funktionen aufgerufen werden. Gegenüber einer Umsetzung mit Escape-Codes ist die Ausführung um ein vielfaches schneller. Das erste Beispiel-Programm läuft mit der Bibliothek etwa 15-mal schneller ab.
Funktionen in GP.S
Die Bibliothek stellt folgende Funktionen zur Verfügung. Die exakte Funktion kann in der Dokumentation des Grundprogramms nachgelesen werden.
moveto(x,y) - Setzen des Grafik-Cursors
drawto(x,y) - Zeichnen einer Linie von der letzten Position des Grafik-Cursors
plot(x,y) - Setzen eines Pixels an der angegebenen Position
clrscreen() - Löscht die sichtbare Bildschirmseite
clrpage(p) - Löscht eine nicht sichtbare Bildschirmseite
curon() - Schaltet den Text-Cursor ein
curoff() - Schaltet den Text-Cursor aus
setpen() - Grafikausgaben werden sichtbar gezeichnet
erapen() - Grafikausgaben löschen darunter liegende Pixel
setxor() - Aktiviert den XOR-Modus zum invertierenden Zeichnen
setflip() - Bestimmt die Umschaltrate von Bildschirmseiten
page(w,r) - Setzen der Schreib- und Leseseite des Bildschirms
waitkey() - Warten auf Tastendruck
Funktionsweise von GP.S
Beim ersten Aufruf einer dieser Funktionen wird die Adresse des Grundprogramms ermittelt und gespeichert. Dazu dient die Init-Routine, die entweder sofort die schon ermittelte Adresse des Grundprogramm in A1 liefert oder vorher mithilfe der BIOS-Funktion 23 die Adresse ermittelt und im Datenbereich sichert.
Alle anderen Funktionen sichern das für CP/M essentielle Register A6 aus dem Stack, rufen Init auf, um die Adresse des Grundprogramms zu erhalten, speichern die Funktion-Parameter in den passenden Registern und rufen dann die Funktion im Grundprogramm-ROM auf.
*******************************************
* Grafik-Bibliothek zur Nutzung des GP in C
*******************************************
* Zu beachten
* - Das Register A6 muss gesichert werden
*******************************************
__init:
tst.l gpadr ; Schon initialisiert?
beq.s __ini2 ; nein, GP Adresse holen
movea.l gpadr,a1 ; ja, GP Adresse nach A1
rts
__ini2:
move #23,d0 ; BIOS Funktion 23
trap #3 ; GP Adreese nach A4
move.l a4,gpadr ; dort sichern
move.l a4,a1 ; und nach A1 kopieren
rts
* Zeichenposition auf X,Y setzen
.globl _moveto
_moveto:
link a6,#0
movem.l a6,-(a7) ; A6 sichern
bsr __init ; GP-Adresse holen
move 8(A6),d1 ; X-Koordinate nach D1
move 10(A6),d2 ; Y-Koordinate nach D2
move #8,d7 ; Funktionsnummer GP
jsr $420(a1) ; Funktion aufrufen
movem.l (a7)+,a6 ; A6 wiederherstellen
unlk a6
rts
* Linie Zeichnen von letzter Position nach X,Y
.globl _drawto
_drawto:
link a6,#0
movem.l a6,-(a7) ; A6 sichern
bsr __init ; GP-Adresse holen
move 8(A6),d1 ; X-Koordinate nach D1
move 10(A6),d2 ; Y-Koordinate nach D2
move #9,d7 ; Funktionsnummer GP
jsr $420(a1) ; Funktion aufrufen
movem.l (a7)+,a6 ; A6 wiederherstellen
unlk a6
rts
* Zeichnen eines Punktes an X,Y
.globl _plot
_plot:
link a6,#0
movem.l a6,-(a7) ; A6 sichern
bsr __init ; GP-Adresse holen
move 8(A6),d1 ; X-Koordinate nach D1
move 10(A6),d2 ; Y-Koordinate nach D2
move #8,d7 ; Funktion moveto
jsr $420(a1) ; Aufruf GP
move #9,d7 ; Funktion drawto
jsr $420(a1) ; Funktion aufrufen
movem.l (a7)+,a6 ; A6 wiederherstellen
unlk a6
rts
* Loeschen der sichtbaren Bildschirmseite
.globl _clrscreen
_clrscreen:
movem.l a6,-(a7)
bsr __init
move #20,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* Loeschen einer unsichtbaren Bildschirmseite
.globl _clrpage
_clrpage:
movem.l a6,-(a7)
bsr __init
move #17,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* Cursor einschalten
.globl _curon
_curon:
movem.l a6,-(a7)
bsr __init
move #81,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* Cursor ausschalten
.globl _curoff
_curoff:
movem.l a6,-(a7)
bsr __init
move #82,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* Schreibmodus fuer drawto setzen
.globl _setpen
_setpen:
movem.l a6,-(a7)
bsr __init
move #37,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* Loeschmodus fuer drawto setzen
.globl _erapen
_erapen:
movem.l a6,-(a7)
bsr __init
move #38,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* XOR Modus fuer drawto setzen
.globl _setxor
_setxor:
movem.l a6,-(a7)
bsr __init
move #77,d7
jsr $420(a1)
movem.l (a7)+,a6
rts
* Bildschirmumschaltrate in Millisekunden setzen
.globl _setflip
_setflip:
link a6,#0
movem.l a6,-(a7) ; A6 sichern
bsr __init ; GP-Adresse holen
move 8(A6),d0 ; Rate nach D0
move #34,d7 ; Funktion setflip
jsr $420(a1) ; Funktion aufrufen
movem.l (a7)+,a6 ; A6 wiederherstellen
unlk a6
rts
* Lese und Schreibseite setzen
.globl _page
_page:
link a6,#0
movem.l a6,-(a7) ; A6 sichern
bsr __init ; GP-Adresse holen
move 8(A6),d0 ; Schreibseite nach D0
move 10(A6),d1 ; Leseseite nach D1
move #27,d7 ; Funktionsnummer GP
jsr $420(a1) ; Funktion aufrufen
movem.l (a7)+,a6 ; A6 wiederherstellen
unlk a6
res
* Warten auf Tastendruck
.globl _waitkey
_waitkey:
movem.l a6,-(a7)
bsr __init
__wait1
move #17,d7
jsr $420(a1)
beq.s __wait1
movem.l (a7)+,a6
rts
* Ablage der Grundprogramm-Adresse
.data
gpadr: dc.l 0
Beispielprogramm SINCOS.C
In diesem Beispiel wird ein Koordinatensystem mit den Funktionswerten der Sinis- und Cosinus-Funktion gezeichnet. Das Programm ist selbsterklärend.
#include "stdio.h"
EXTERN FLOAT sin(),cos();
main() {
FLOAT winkel;
int x,y;
clrscreen();
setflip(0);
curoff();
// X-Achse zeichnen
moveto(0,128); drawto(511,128);
for (x=0; x<512; x+=25) {
moveto(x,124); drawto(x,132);
}
// Y-Achse zeichnen
moveto(0,0); drawto(0,255);
for (y=0; y<256; y+=16) {
moveto(0,y); drawto(5,y);
}
// Sinus- und Cosinus-Kurve plotten
for (winkel=0,x=0; x<511; x++,winkel=winkel+3.1416/100) {
y = (sin(winkel)*120+128);
plot(x,y);
y = (cos(winkel)*120+128);
plot(x,y);
}
waitkey();
clrscreen();
exit(0);
}
Wegen der Nutzung von Fließkomma-Arithmaetik muss zum Übersetzen die Submit-Datei CF.SUB oder CE.SUB verwendet werden. Er ergibt sich eine Programmgröße von "nur" 12 bzw. 14 kByte je nach gewählter Fließkomma-Bibliothek. Es ist jedoch zu beachten, dass jede neue Funktion auch die Bibliothek und die erzeugte Programm-Datei vergrößern. LO68 bindet nur komplette Objekt-Dateien ein und analysiert nicht, welche Funktionen tatsächlich genutzt werden.
cf sincos gp
ce sincos gp
Screenshot
Nach dem Aufruf des generierten Programms SINCOS.68K zeigt der NKC folgendes Bild.

Beispielprogramm RANDOM.C
Dieses Programm verzichtet auf Fließkomma-Arithmetik und zeichnet zufällige Linien auf dem Bildschirm. Die C-Funktion rand() erzeugt pseudo-zufällige Integer Zahlen von 0 bis 32767, die durch die Division durch 64 bzw. 128 auf die Auflösung des Bildschirms skaliert werden.
#include "stdio.h"
main() {
int x,y;
long I;
clrscreen(); curoff();
for(i=0;i<300;i++) {
moveto(rand()/64, rand()/128);
drawto(rand()/64, rand()/128);
}
waitkey(); clrscreen();
exit(0);
}
Zum Übersetzen des Quelltextes wird jetzt die Submit-Datei C.SUB verwendet, da wir keine Fließkomma-Arithmetik verwenden.
c random gp
Screenshot
Nach dem Aufruf des generierten Programms RANDOM.68K zeigt der NKC folgendes Bild.

Zurück zum Inhaltsverzeichnis der Beiträge
|