Home | Computer | Pascal Kurs | Site Map |
Eigentlich wollte ich nach dem 7.ten Kapitel Schluss machen, doch weil ich auf den Kurs so wohlmeinende E-Mails und Einträge ins Gästebuch bekommen habe, nun noch ein paar Kapitel mehr. Es geht um einige wichtige Prozeduren und Funktionen die bei Pascal mitgeliefert werden. Leider muss ich nun auch den Weg des gemeinsamen Standards verlassen und beziehe mich nur noch auf Turbo Pascal.
Hier gibt es einen Unterschied zu anderen Units, die Sie selber schreiben oder vom System einbinden. Das Problem ist: Es gibt nicht einen Grafikadapter sondern viele. Als Turbo Pascal 7.0 erschien war seit 13 Jahren der letzte gemeinsame Standard VGA, das sind 640 × 480 Punkte in 16 Farben - schon damals eine viel zu geringe Auflösung. Das Problem ist, dass alles was höhere Auflösungen liefert kein Standard mehr ist.
So hat man bei Borland eine Lösung erfunden, die dynamisch abläuft. Sie geht nach folgendem Prinzip:
Programmtechnisch läuft dies so ab:
USES Graph;
VAR Driver,Modus : Integer;
BEGIN
Driver:=Detect;
Modus:=Detect;
InitGraph(Driver,Modus,'C:\TP\BGI');
IF GraphResultGrOk THEN
BEGIN
SetColor(Red);
Line(0,0,GetMaxX,GetMaxY);
ReadLn;
CloseGraph;
END
ELSE WriteLn('Kein Treiber gefunden!');
END.
Ich habe hier für Demozwecke etwas gemacht, was Sie, wenn sie die Dateien weitergeben wollen
nicht machen sollten: Das Verzeichnis wo die.bgi Dateien sind "hart" kodiert.
GetmaxX und GetmaxY sind zwei Konstanten welche die höchsten
verfügbaren Bildschirmkoordinaten enthalten, und daher sehr nützlich wenn das Programm auf
Rechnern mit verschiedenen Auflösungen arbeiten soll.
Der einzig schwierige Punkt ist Initgraph. Diese Prozedur hat 3 Parameter
Dann ist das aktuelle Verzeichnis nicht C:\tp\run sondern C:\work. Das kann man aber leicht lösen, denn der Programmname mit Pfad steht nach dem Start in der Kommandozeile und kann mit paramstr(0) ausgelesen werden und daraus der Pfad extrahiert werden.
Solange nun dieser Grafiktreiber aktiv ist, kann man beliebiges machen, vor allem mehrmals mit RestoreCRTMode in den Textmodus und mit SetGraphmode wieder in den Grafikmodus wechseln. Geschlossen wird der Grafiktreiber erst durch Closegraph. Diesen Befehl brauchen sie wenn sie mehrere Treiber ausprobieren wollen.
Die Lösung ist daher nicht allgemeingültig. Hier mein Vorgehen:
Eine Reihe von Grafiktreibern mit hoher Auflösung einbinden, und programmtechnisch diese durchchecken und den mit der höchsten Auflösung nehmen. Mit immer besser werden Grafikkarten wird die Komptabilität immer kleiner und so verwende ich nur noch 3 Grafiktreiber:
Alles was danach kommt ist leider sehr schlecht implementiert. Eine Abfrage mittels Graphresult ob der Treiber geladen und initialisiert. werden konnte geht ja noch. Leider haben alle BGI Treiber die ich kenne, keine Unterstützung für die GetModeRange Prozedur welche die gültigen Grafikmodi ermittelt, so das ich im nächsten Demo die ersten 10 Abfrage. Manche Treiber stürzen aber hier ab, wenn man einen Modus wählt den es nicht gibt, z.B. Treiber die einen Drucker oder Plotter ansteuern und nicht auf den Bildschirm schreiben.
Hat man einen Treiber mittels Installuserdriver eingebunden so nimmt man den Rückgabewert dieser Funktion als Driver für Initgraph und kann dann wie gewohnt arbeiten. Die folgende Unit nutzt dies aus um nach der höchsten installierten Auflösung zu suchen:
UNIT Suche_Driver;
INTERFACE
FUNCTION Get_Best_Driver(VAR Driver,Mode : Integer; VAR Name : STRING): Boolean;
{true wenn andere Treiber als VGA anwesend}
IMPLEMENTATION
USES Graph,Dos;
FUNCTION Get_Best_Driver(VAR Driver,Mode : Integer; VAR Name : STRING): Boolean;
VAR Info : SearchRec;
Max : Integer;
Path : STRING;
PROCEDURE Suche_Driver;
VAR Mydriver,Mymode,Lomode,I : Integer;
Dir,Ext,Tempname : STRING;
CONST Bekannt : ARRAY [1..6] OF STRING[8] = ('ATT','EGAVGA','CGA','PC3270','IBM8514','HERC');
BEGIN
FSplit(Info.Name,Dir,Tempname,Ext);
FOR I:=1 TO 6 DO IF TempnameBekannt[I] THEN Exit;
Mydriver:=InstallUserDriver(Tempname,NIL);
Mymode:=0;
InitGraph(Mydriver,Mymode,Path);
IF GraphResult<>grOk THEN
BEGIN
CloseGraph;
Exit;
END;
GetModeRange(Mydriver,Lomode,Mymode);
IF Mymode<=0 THEN Mymode:=10;
FOR I:=0 TO Mymode DO
BEGIN
SetGraphMode(I);
IF GraphResult<>grOk THEN
BEGIN
CloseGraph;
Exit;
END;
IF GetMaxX> Max THEN
BEGIN
Driver:=Mydriver;
Mode:=Mymode;
Name:=Tempname;
Max:=GetMaxX;
END;
END;
CloseGraph;
END;
BEGIN
Max:=0;
Path:=Name;
Name:='EGAVGA';
Driver:=VGA;
Mode:=VGAHi; {Initzialsierung}
Get_Best_Driver:=False;
IF Path'' THEN Path:='.';
IF Path[Length(Path)]<>'\' THEN Path:=Path+'\';
FindFirst(Path+'*.bgi',AnyFile,Info);
WHILE DosError0 DO
BEGIN
Get_Best_Driver:=True;
Suche_Driver;
FindNext(Info);
END;
END;
BEGIN
END.
Man ruft diese dann so auf :
USES Suche_Driver,Graph;
VAR Name : STRING;
Driver,Mode : Integer;
BEGIN
Name:='C:\tp\bgi';
Get_Best_Driver(Driver,Mode, Name);
InitGraph(Driver,Mode, Name);
Mode:=GetMaxX;
CloseGraph;
WriteLn(Driver);
WriteLn(Mode);
WriteLn(Name);
ReadLn;
END.
Man ruft diese dann so auf: Für praktische Zwecke ist dies natürlich nicht in ihrem
Anwendungsprogramm gedacht, sondern in einem Installer, denn dieses Codefragement bindet ja alle
BGI Treiber ein, und nicht nur den einen den man braucht.
Ansonsten finden sie in der Grafikunit vieles, was keine großen Erklärung Bedarf. Es empfiehlt sich hier besonders mit den vordefinierten Konstanten zu arbeiten, sie machen das Programm lesbarer. Ihr Programm sollte immer mit Getmaxx und Getmaxy arbeiten. Eine Besonderheit gegenüber dem mathematischen Koordinatensystem ist übrigens das die Koordinaten (0,0) nicht links unten sondern links oben sind, d.h. höhere Y Koordinaten bedeuten das die sie tiefer und nicht höher liegen.
Für dieses und andere Umrechnungen hier zwei Routinen von mir:
const Offset 32;
Halfoffset Offset div 2;
var faky,faky : double;
procedure Set_Koord(const Xu,Yu,Xo,Yo : double);
begin
Fakx:=(Xo-Xu)/GetmaxX-Offset);
Faky:=(Yo-Yu)/GetMaxY-Offset);
end;
procedure Point(const X,Y : double; var ix,iy : integer);
begin
Ix:=Halfoffset+Trunc((X-Xu)/Fakx);
Iy:=ClientHeight-Halfoffset-(Trunc((Y-Yu)/Faky));
end;
Diese Paar von Prozeduren richtet eine Möglichkeit ein eine Grafik einzupassen. Wenn Sie
einen Funktionsplot zwischen x=-5 und +6 und Y=-1 und +3 machen wollen so richten sie mit
Set_koord(-5,-1,6,3) die Umrechnungsfaktoren fest und können dann jeden Punkt in die wahren
Bildschirmkoordinaten ix und iy umrechnen. So was braucht man fast immer wenn man Messwerte
visualisiert oder Kurven zeichnet (In diesem Fall stammt das Schnipsel aus meinem Programm
Lineare Regression). Der Offset bewirkt, das die
Grafik an der Rändern etwas Platz für Beschriftungen lässt.
Oftmals will man über die Kommandozeile Parameter übergeben. Hier gibt es zwei Funktionen:
Paramcount liefert die Anzahl der Kommondozeilen parameter (sie sind durch Leerzeichen getrennt)
Paramstr(n). Liefert einen String zurück der dem n.ten parameter entspricht. Über Paramstr(0) bekommt man heraus wie denn heute gerade die EXE Datei des Programmes heißt und in welchem Pfad sie liegt (sehr nützlich für Aufrufe von Chdir....)
So liest man z.B. die Parameter aus:
for i:=1 to paramcount do writeln(paramstr(i));
Will man DOS einen Statuscode zurückgeben so sollte man das Programm mit Halt(nr) verlassen. Nr. ist eine Integerzahl die DOS in der Variable ERRORLEVEL ablegt. So kann man z.B. sagen ob das Programm fehlerfrei lief.
Ach ja, gehört eigentlich nicht hierher: Man kann auch eigene Exit Prozeduren definieren. Was passiert wenn ihr Programm einen Runtime Fehler hat? Na sie wollen es doch sicher artig beenden und zumindest die offenen Dateien schließen, damit es keine verlorenen Inhalte gibt. Daher kann man eine Prozedur definieren die zu Programmende aber auch in Fehlerfällen aufgerufen wird. Dies geschieht in 3 Schritten:
Var Exitsave: Pointer;
PROCEDURE Error; FAR;
BEGIN
ExitProc:=Exitsave;
CloseGraph;
IF NOT Update THEN Speichern;
IF offen THEN Dateiclose;
END;
{Zu Beginn des Hauprogrammes Zeiger retten und dann neu setzen}
BEGIN
Exitsave:=ExitProc;
ExitProc:=@Error;
{Alternative Schreibweise: ExitProc:=Addr(Error);}
END.
ExitProc ist ein Zeiger auf die Standard Prozedur am ende. Er muss gesichert und auf jeden
Fall am Ende jeder eigenen Fehlerroutine wieder auf die richtige Routine zeigen. In diesem Beispiel
wird der alte Exitcode Zeiger zuerst in der Variablen Exitsave gesichert (erste Zeile des
Hauptprogrammes!) Und in der benutzerdefinierten Routine als erste Anweisung wieder restauriert.
Danach kann ExitProc die Adresse der eignen Fehlerroutine zugewiesen werden. Dieses ist eine
Prozedur ohne Parameter die - das ist wichtig! - als far deklariert werden muss, denn sie kann von
überall aus angesprungen werden. Dort sollte man dann Grafiktreiber schließen (sonst endet das
Programm im Grafikmodus) oder offene Dateien schließen. Alles was Ihnen im Fehlerfall wichtig ist.
Sie können auch mehrere Routinen haben, die dann wie in einer Liste nacheinander abgearbeitet werden, z.B. für jede Unit eine, die lokale Datein schließt Sie brauchen aber für jede dieser Routinen so einen Zeiger, der sich die letzte ExitProc Routine merkt. Bei Units werden sie diese im Initailisierungsteil (begin...end am Schluss) anlegen.
Alle Prozeduren mit Interrupts haben in etwa solche Form:
PROCEDURE Hardcopy;
VAR
I,J : Byte;
S : STRING [82];
R : Registers;
BEGIN
FOR J:=0 TO 24 DO
BEGIN
S:='';
FOR I:=0 TO 79 DO
BEGIN
R.AH:=2; R.BH:=0;
R.DH:=J; R.DL:=I;
Intr($10,R); {Cursor Positionieren}
R.AH:=8; R.BH:=0;
Intr($10,R);
S:=S+Chr(R.AL); {Zeichen lesen und einfügen}
END;
WHILE S[Length(S)]=' ' DO Delete(S,Length(S),1);
{überflüssige Leerzeichen Löschen}
Ausgabe(S+);
END;
END;
Diese Routine macht eine Bildschirmhardcopy - nicht nur auf den Drucker sondern auch in eine
Datei oder wohin immer sie wollen. Dabei wichtig ist der Interrupt $10 (Dezimal 16) und die
Funktion 8, die das Zeichen beim Cursor ausliest. Den zweiten Interruptaufruf zum Cursor
positionieren hätte man auch mit gotoxy machen können.
Sie finden hier alle Elemente für Interrupts: Sie brauchen einen Record namens Registers in dem die einzelnen Felder die Register des Prozessors repräsentieren. Diese füttern Sie mit den Daten und rufen dann über die Funktion intr den Interrupt auf. Rückgabewerte stehen dann auch in dem Register Record. Für komplexere Typen ist es ratsam sich mal durch die Kapitel über Parameterübergaben an Assembler und Interrupts im Handbuch durchzuarbeiten.
LPT1: für den Drucker 1 (analog LPT2:, LPT3:)
COM1:... COM4: für die serielle Schnittstelle. Die Doppelpunkte sind wichtig und gehören zum Dateinamen.
Um auch den Bildschirm einer Datei zuordnen zu können gibt es die Procedure AssignCRT(dateivariable) die wie Assign arbeitet nur das der Dateiname entfallen kann.
Ich möchte zum Schluss auch noch etwas vorstellen, was Sie wahrscheinlich schon dutzendfach im Internet gefunden haben. Ein Programm womit man Fractale zeichnen oder Mandelbrotmengen erstellen kann. Mein Programm ist hier sehr schlicht, das hat auch seinen Grund: dieses Programm ist von einigen Anpassungen an die Grafik abgesehen 16 Jahre alt und lief zuerst auf einem CPC 464 (einem 8 Bit Rechner mit 4 MHz und 64 KB Speicher). Dafür können sie mit diesem Programm beurteilen warum man damals Stunden gebraucht hat um diese Grafiken zu zeichnen. (Das Übersichtsbild mit den Eingaben links -0.5, rechts 2.5, oben -1.2 und unten 1.2 brauchte damals 45 min zum Berechnen, eine komfortablere Version in BASIC brauchte für Teilausschnitte bis zu 8 Stunden). Sie verstehen nun sicher warum man das Aufbereiten der Grafiken damals "Number Crunching" nannte.
Die Mandelbrotmengen machen nun folgendes: Sie lösen die Gleichung z=z²+c. Dabei sind z und c komplexe Zahlen wobei man hier zum Zeichen die komplexe Zahl in einen Realteil (X Achse) und einen Imaginärteil (Y Achse) zerlegt. Man beginnt also mit den Koordinaten eines Punktes denn man dann in eine imaginärzahl konvertiert, führt die Rechnung aus und schaut nach ob z größer als der rechte Teil ist - ist dies der Fall so ist dies auch bei allen weiteren Näherungen der Fall, und man kann die Schleife verlassen. Andernfalls erhält man einen neuen Näherungswert für z und wiederholt die Schleife. Was man nun plotet sind die Anzahl der Iterationen bis zum Abbruch. Schwarz bleibt der Hintergrund wenn man bis zu einer bestimmten Tiefe nicht vorher aus der Schleife heraussprang. Bei meinem Ursprungsprogramm war die Abbruchbedingung auf 30 gesetzt, was bei der damaligen Auflösung von 320 × 200 Punkten gute 2 Millionen Schleifendurchläufe als Maximum bedeutet hat - und das bei einem Rechner der wenn er gut gelaunt war 200-500 Fliesskommarechnungen pro Sekunde ausführte....
Mein heutiger PC ist durch Fliesskommaprozessor, 1200 MHz Takt und 32 Bit etwa 11000 mal schneller, obgleich hier das Zeichnen der Punkte die meiste Zeit beansprucht, anders als früher wo man zuschauen konnte wie das Bild "gemalt" wurde. Das Programm ist vielleicht ein guter Einsteig wenn sie selber experimentieren wollen. Es zeigt aber auch das auch ich damals nicht gerade perfekte Programme schrieb. (Achten Sie mal auf globale Variablen....)
und als Delphi 4 Projekt:
Zum Thema Computer ist auch von mir ein Buch erschienen. "Computergeschichte(n)" beinhaltet, das was der Titel aussagt: einzelne Episoden aus der Frühzeit des PC. Es sind Episoden aus den Lebensläufen von Ed Roberts, Bill Gates, Steve Jobs, Stephen Wozniak, Gary Kildall, Adam Osborne, Jack Tramiel und Chuck Peddle und wie sie den PC schufen.
Das Buch wird abgerundet durch eine kurze Erklärung der Computertechnik vor dem PC, sowie einer Zusammenfassung was danach geschah, als die Claims abgesteckt waren. Ich habe versucht ein Buch zu schreiben, dass sie dahingehend von anderen Büchern abhebt, dass es nicht nur Geschichte erzählt sondern auch erklärt warum bestimmte Produkte erfolgreich waren, also auf die Technik eingeht.
Die 2014 erschienene zweite Auflage wurde aktualisiert und leicht erweitert. Die umfangreichste Änderung ist ein 60 Seiten starkes Kapitel über Seymour Cray und die von ihm entworfenen Supercomputer. Bedingt durch Preissenkungen bei Neuauflagen ist es mit 19,90 Euro trotz gestiegenem Umfang um 5 Euro billiger als die erste Auflage. Es ist auch als e-Book für 10,99 Euro erschienen.
Mehr über das Buch auf dieser eigenen Seite.
Hier geht's zur Gesamtübersicht meiner Bücher mit direkten Links zum BOD-Buchshop. Die Bücher sind aber auch direkt im Buchhandel bestellbar (da ich über sehr spezielle Themen schreibe, wird man sie wohl kaum in der Auslage finden) und sie sind natürlich in den gängigen Online-Plattformen wie Amazon, Libri, Buecher.de erhältlich.
Sitemap | Kontakt | Impressum / Datenschutz | Neues | Hier werben / advertisment here | Buchshop | Bücher vom Autor |