Home Computer Pascal Kurs Site Map counter

Jetzt lerne ich Pascal... Teil 5

Units

Bisher bestehen ihre Programme aus einer Datei. Schon jetzt sehen sie, das es ganz nützliche Routinen gibt die man immer wieder verwenden könnte. Sie können z.B. den Sprachumfang von Pascal um folgende Routine ergänzen:
FUNCTION Trim_Left(Strng: STRING): STRING;

BEGIN
WHILE
(Length(Strng)>0) AND (Strng[1]=' ') DO Delete(Strng,1,1);
Trim_Left:=Strng;
END;

Diese Routine löscht Leerzeichen die links (Position 1 aufwärts) in einem String sind bis zum ersten Zeichen das kein Leerzeichen ist. So was braucht man häufig wenn man von Dateien einliest, oder Benutzereingaben von Leerzeichen befreien will.

Es ist nun sehr mühselig das jedes mal neu zu programmieren. Daher gab es schon immer bei Pascal die Möglichkeit eine Datei einzufügen über den Compilerswitch:

{$I Dateiname.inc}

Damit würde man häufig benutzte Routinen in Dateiname.inc auslagern, was auch den Vorteil hat, dass man bei einer Änderung nicht den Quelltext mehrere Dateien ändern müsste. Doch dieses Verfahren ist nicht optimal: Zum einen muss man den Quelltext bei jeder Compilierung neu übersetzen, wo es doch reichen würde ihn einmal zu übersetzen, zum anderen benötigen komplexe Routinen eigene Variablen, Unterroutinen etc. und man sollte vermeiden, dass diese vom Programm aus aufgerufen werden, wenn es dort keinen Sinn macht. Es gibt Namenskonflikte, wenn sie Namen mehrmals verwenden. Für professionelle Softwareentwickler ist vor allem aber wesentlich, dass man mit dieser Methode bei Tools für Pascal den Sourcecode herausgeben muss, und diesen dann jeder ändern kann oder erweitern und als neues Produkt vermarkten.

Schon 1986 hat Turbo Pascal daher das Konzept der Units eingeführt, das sich ein bisschen an die Headerdateien von C und ein bisschen an Modula anlehnt. Der Grundgedanke ist: Man legt eine neue Pascal Datei wie bei Includedateien an. Man definiert einem Kopf alle Variablen und Routinen die öffentlich bekannt sein sollen. Auf diese kann man dann von einem anderen Programm aus zugreifen. In einem zweiten Teil werden die Routinen kodiert, wobei hier auch andere Variablen und andere Routinen stehen können, die dann aber nur lokal, d.h. innerhalb dieser Quelltextdatei bekannt sind.

Das ganze wird vom Compiler compiliert und wenn ein Programm diese Quelltextdatei (die man Unit nennt) benötigt, wird nur aus dem compilierten Code die Routinen herausgeholt die verwendet werden. D.h. Wenn sie in einer Unit 100 Routinen definieren und ihr Programm benötigt nur 2 so wird auch nur deren Code verwendet. Die Tatsache, das die Units compiliert sind, ist ein Unterschied zu den C Header Dateien. Hier kann es sein, das Sie ein Quellcodeprogramm mit 20 Kilobyte Länge haben, und 10 Includes von Headerdateien die ihren Quellcode auf 300 KByte aufblähen und die Compilierzeit verlängern. Das ist auch ein Grund warum C beim Compilieren so langsam im Vergleich zu Pascal ist.

Units: Definition

Um eine Unit anzulegen, müssen Sie vor jeder anderen Codezeile folgende Zeile festlegen:

Unit Dateiname;

Unit ist ein Schlüsselwort und Dateiname - Das ist wichtig ist der Bezeichner der Unit, der wenn er auf 8 Zeichen gekürzt wird deren Dateinamen ergibt. Das ist so notwendig, damit Turbo Pascal auch die zugehörige Unit zu einem Namen findet. Der Unitname kann durchaus länger sein, doch die ersten 8 Zeichen müssen identisch mit dem Dateinamen sein (Wenn Sie unter Delphi 2 aufwärts arbeiten gilt entsprechendes für alle Zeichen).

Beispiel:

Unit Mathematik;

wird als mathemat.pas gespeichert. Stimmt dies nicht, so bekommen Sie eine Fehlermeldung beim Einbinden von Units.

Eine Unit benutzen können Sie in ihrem Hauptprogramm mit

USES Unitname1,Unitname2,..... Unitnamen;

obige Unit würde man also einbinden mit

USES Mathematik;

Danach sind in dem Programm welches Uses benutzt, alle öffentlich deklarierten Variablen und Funktionen von Mathematik bekannt und benutzbar. USES muß vor allen anderen Anweisungen benutzt werden.

Auch in ihren Units können Sie USES benutzen um andere Units einzubinden. Dann folgt die USES Klausel nach dem Namen der UNIT mit UNIT Unitname; Es ist allerdings auch möglich die Unit erst später aufzunehmen doch dazu später mehr.

Der Interface Teil

Im Anschluss an den Unitnamen und eine eventuelle USES Klausel folgt ein Interface Teil. Dieser Teil ähnelt schon bekanntem: sie können hier Typen mit TYPE definieren, Variablen mit VAR festlegen und Konstanten mit CONST. Alle diese Typen, Variablen und Konstanten kennt dann das Programm, welches ihre Unit aufruft und kann auf diese zugreifen. Neu ist das sie danach alle Funktionsköpfe aufführen, die sie später verwenden. Der Funktionskopf ist die Zeile in der PROCEDURE und FUNCTION steht, d.h. der Namen der Funktion, ihre Parameter und Rückgabewerte. Jedoch nicht der Code. Die gleichen Funktionen, dann mit dem Code kodieren Sie später im Implementationsteil. Jetzt geht es nur darum, das sie für das aufrufende Programm bekannt machen: "Ok hier gibt es eine Funktion xy mit zwei Integerzahlen als Parameter und einer Realzahl als Rückgabewert, die kannst Du später so aufrufen". Alle im Interface so definierten Funktionen kennt dann das Programm, welche die Unit einbindet.

Das ist doch nicht so schwierig oder? Also eine Mathematik Unit sähe z.B. so im Interface Teil aus:

unit Mathematik;
interface
const Winkelfaktor 180 / Pi;
function Sinus(X : Extended): Extended;
function Cosinus(X : Extended): Extended;
function Tan(X : Extended): Extended;
function Atn(X : Extended): Extended;
function ArcSin(X : Extended): Extended;
function ArcCos(X : Extended): Extended;
function Min(A : Extended;B : Extended): Extended;
function Max(A : Extended;B : Extended): Extended;
function Sqrt3(X : Extended): Extended;
function Sqr3(X : Extended): Extended;
function Pot(X,Y : Extended): Extended;
function Log10(X : Extended): Extended;
procedure Rounddown(var X : Extended);
procedure Roundup(var X : Extended);
function Sgn(var X : Extended): Shortint;

Hier werden einige Routinen definiert die Delphi nicht kennt, wie Sinus im Gradmaß und eine Konstante um Grad in Bogenmaß umrechnen zu können. Das auffälligste: Sie sehen eben nur die Köpfe, nicht die komplette Funktion.

Der Implementationsteil

Nach dem Interface Teil folgt das reservierte Wort IMPLEMENTATION. An dieses schließt sich nun der eigentliche Programmcode an. Insbesondere sollte man hier die im Interface Teil definierten Routinen kodieren. Sie tauchen hier nochmals in voller Schönheit auf, d.h. mit dem Kopf und nun auch mit dem Code. Sie können aber auch weitere Routinen, die nicht im Interface Teil genannt wurden, Typen, Konstanten und Variablen definieren. Diese sind dann nach außen hin für das Programm welches die Unit benutzt nicht bekannt, also nur lokal in der Unit existent. Hier ist Pascal etwas kleinlich: Es verlangt das nicht nur Parameter und Typ stimmen, sondern auch der genaue Name wiederholt wird. Es ist daher zu empfehlen den Funktionskopf aus dem Implementationsteil mittels Copy+Paste zu kopieren.

Vielleicht wird Ihnen nun klar, was das bedeutet: Wenn Sie intern etwas ändern, neue Variablen einführen müssen oder Routinen schreiben, so wirkt sich das überhaupt nicht auf die Programme aus, die ihre Units benutzen. Das gibt Sicherheit beim Programmieren.

Zwei Dinge sind noch wichtig. Zum einen können direkt hinter IMPLEMENTATION eine weitere Uses Klausel kommen um andere Units einzubinden. Wozu dies? Nun wenn Units sich gegenseitig voraussetzen, geht dies nur, wenn zumindest eine in der USES Klausel im Implementationsteil vorkommt. Zum anderen benötigen Sie eine USES Klausel bei Units im Interfaceteil nur wenn sie dort Typen oder Variablen definieren, die auf anderen Typen in anderen Units basieren, ansonsten nicht. Die USES Klausel im Implementationsteil ist die wichtigere, denn so können sie noch mehr den Code schützen.

Die zweite Besonderheit ist das eine Unit ein etwas anderes "Hauptprogramm" hat. Es lautet entweder:

end.

oder

begin
...Code...
end.

Die erste Variante nehmen Sie, wenn ihr Hauptprogramm eigentlich gar nichts machen soll, also ihre Unit z.B. nur Routinen hat die man aufruft. Jedoch können Sie auch im Hauptprogramm Routinen festlegen die vor dem Aufruf jeder Routine ausgeführt werden müssen: Zuweisung von Startwerten an Variablen, Initialisierungsprozeduren etc. Dann schreiben Sie ein Hauptprogramm in der Zweiten Art. "Hauptprogramm" ist als Begriff falsch, es handelt sich um Initialisierungsanweisungen, aber ich habe dies so gewählt, weil die Struktur identisch mit dem eines Hauptprogramms in Pascal ist.

Vor dem Aufruf der ersten Anweisung ihres Programms durchläuft der Computer erst alle Initialisierungsprozeduren ihrer Units, d.h. alle dort gemachten Anweisungen sind ausgeführt bevor sie zum ersten Mail auf eine Unit zugreifen können und bevor die erste Zeile ihres Hauptprogramms abgearbeitet wird.

Hier nun als Beispiel für eine Unit der komplette Quelltext der Mathematik Unit:

unit Mathematik;
interface {nach außen hin sichtbare Definitionen}
function Sinus(X : Extended): Extended;
function Cosinus(X : Extended): Extended;
Function tan(X : Extended): Extended;
function Atn(X : Extended): Extended;
Function arcsin(X : Extended): Extended;
Function arccos(X : Extended): Extended;
Function min(A : Extended;B : Extended): Extended;
Function max(A : Extended;B : Extended): Extended;
function Sqrt3(X : Extended): Extended;
function Sqr3(X : Extended): Extended;
function Pot(X,Y : Extended): Extended;
Function log10(X : Extended): Extended;
procedure Rounddown(var X : Extended);
procedure Roundup(var X : Extended);
function Sgn(var X : Extended): Shortint;
{Umsetzung in Code und interne Defintionen}
implementation
const Winkelfaktor 180 / Pi;
{Winkelfaktor ist durch Positionierum im Implementationstteil außen nicht bekannt}
function Sqrt3(X : Extended): Extended;
begin
  Sqrt3:=Pot(X,1/3);
end;
function Sqr3(X : Extended): Extended;
begin
  Sqr3:=Pot(X,3);
end;
function Pot(X,Y : Extended): Extended;
begin
  if X<0 then Pot:=-Exp(Y*Ln(-X)) else Pot:=Exp(Y*Ln(X));
end;
function Log10(X : Extended): Extended;
begin
  Log10:=Ln(X)/Ln(10);
end;
function Sinus(X : Extended): Extended;
begin
  Sinus:=Sin(X/Winkelfaktor);
end;
function Cosinus(X : Extended): Extended;
begin
  Cosinus:=Cos(X/Winkelfaktor);
end;
function Min(A : Extended;B : Extended): Extended;
begin
  if A<B then Min:=A Elsemin:=B;
end;
function Max(A : Extended;B : Extended): Extended;
begin
  if A>B then Max:=A Elsemax:=B;
end;
function Tan(X : Extended): Extended;
begin
  X:=X/Winkelfaktor; Tan:=Sin(X)/Cos(X);
end;
function Atn(X : Extended): Extended;
begin
  Atn:=Winkelfaktor*ArcTan(X);
end;
function ArcSin(X : Extended): Extended;
begin
  ArcSin:=Winkelfaktor*ArcTan(X/Sqrt(-X*X+1.0));
end;
function ArcCos(X : Extended): Extended;
begin
  ArcCos:=90-ArcSin(X);
end;
procedure Rounddown(var X : Extended);
var S : string [40];
    S2: string [8];
    Err: Integer;
begin
  Str(X,S); S2:=Copy(S,Pos('E',S),8); S:=Copy(S,1,3)+'0'+S2;
  Val(S,X,Err);
end;
procedure Roundup(var X : Extended);
var S : string [40];
    S2: string [8];
    Err: Integer;
begin
  Str(X,S); S2:=Copy(S,Pos('E',S),8); S:=Copy(S,1,3);
  if S[2]='9' then S:=S[1]+'10.' else S[2]:=Chr(Ord(S[2])+1);
  S:=S+'0'+S2;
  Val(S,X,Err);
end;
function Sgn(var X : Extended): Shortint;
begin
  if X<0 then Sgn:=-1 else if X>0 then Sgn:=1 else Sgn:=0;
end;
end.

Namespaces

  1. An dieser Stelle ein Wort zu Namensräumen und Gültigkeit von Variablen. Das ganze kann ganz schön verwirrend sein. Denn so sieht es bisher aus:
  2. Alle Variablen (Konstanten, Typen) die im Interface Teil einer Unit deklariert wurden sind in dieser Unit bekannt, und in allen Units die diese mit uses referenzieren
  3. Alle Variablen (Konstanten, Typen) die im Implementation Teil einer Unit deklariert sind, innerhalb dieser Unit bekannt, aber nicht außerhalb. Die Gültigkeit beginnt ab der Stelle an der Sie deklariert wurden.
  4. Alle Variablen im Hauptprogramm sind ebenfalls ab der Deklarationsstelle bekannt, aber nicht in Units (Man kann das Hauptprogramm nicht mit uses einbinden).
  5. Und zuletzt sind in lokalen Funktionen (und eingeschlossenen Unterfunktionen) alle Variablen nur innerhalb dieser Funktion sowie in den Unterfunktionen bekannt.

Dabei überschrieben lokale Deklarationen globale:

Var x : integer;

procedure Dummy

var x : integer;

begin
writeln(x);
x:=5;
end;

begin
x:=7;
dummy;
writeln(x);
end.

Sie stellen Fest:

× gibt es lokal (in Dummy) und global. Das lokale × macht das globale "unsichtbar". Es ist nicht sichtbar, wird aber auch nicht überschrieben.

Das ist ganz nützlich, da man z.B. bestimmte Variablen wie i,j,k... sehr häufig als Schleifenvariablen braucht. Zumindest an Deklarationen aus Units kommt man noch heran, wenn man den Unitsnamen mit einem Punkt davor stellt. Wenn Sie wie der Autor gerne eigene Proceduren mit dem Namen "Copy" schreiben, so kommen sie mittels "System.Copy" immer noch an die String Copy Funktion.

Wichtiger ist die Gültigkeit von Variablen. Hier gibt es entscheidende Unterschiede:

Wichtiger Merksatz: Gehe nie davon aus, das eine lokale Variable beim nächsten Aufruf noch den Wert hat, den sie beim letzten Verlassen hatte.

Doch nach diesem Ausflug zurück zu den Units.

Units sind auch so wichtig, weil die meisten Funktionen von Turbo Pascal 7.0 in Units sind:

Damit ist die Laufzeitbibliothek von Pascal auch funktionell gesplittet: Vorteil: Geringerer Speicherplatzverbrauch und schnelleres Compilieren (Heute bedeutungslos, doch als Rechner nicht so schnell waren, wichtig. Übrigens: Beim Compilieren ist Pascal erheblich schneller als C oder C++).

Noch etwas zu Graph: Turbo Pascal 7.0 ist 1992 erschienen, in der Zeit seitdem haben sich Graphikkarten weiterentwickelt, leider nicht im Sinne einer höheren DOS Komptabilität. Die 16 Farben Modi des VESA Treibers von Turbo Pascal 7.0 werden von meiner Graphikkarte z.B. nur noch teilweise unterstützt, im Gegensatz zu meinen beiden Vorgängermodellen. Ein Treiber der sehr oft funktioniert ist der SUPER VGA Treiber von Knight Software für 256 Farben, denn ich zum Download anbiete, nachdem er zur Freeware geworden ist.

Er arbeitet bis 1280 × 1024 in 256 Farben und wird so eingebunden:

{        Mode     Description   Memory    Who uses it
0 320x200x256: (64K) Standard VGA and MCGA
1 640x400x256: (256K) Some Super VGAs
2 640x480x256: (512K) Most Super VGAs
3 800x600x256: (512K/1M) Some Super VGAs
4 1024x768x256: (1M) Some Super VGAs
5 1280x1024x256: (2M) Some Super VGAs
}

uses graph;

var driver,Mode : integer;

begin
Mode;=2; {Das sollte jede Grafikkarte schaffen, experimentieren indem man Mode immer höher setzt was ihre Karte schafft}
driver:=installuserdriver('BGI256',nil);
initgraph(driver,mode,'c:\tp\bgi');
end.

{Pfad (c:\tp\bgi\ anpassen, nach Ort wo der Driver BGI256.BGI abgelegt wurde, dort sollten auch Symbolzeichensätze liegen}

Der ganze Rest des Pakets ist sehr nett, aber nicht nötig. Wenn Sie die Clones Virtual Pascal oder Free Pascal benutzen oder Delphi so sieht die Sache anders aus. Virtual Pascal und Delphi bieten unter Win 32 DOS keine Grafik. Free Pascal hat eigene Grafiktreiber die auch bis 1280 × 1204 nutzbar sind.

Prozedur und Funktionstypen

Bevor ich erkläre was dies sind ein Beispiel wozu man sie braucht. Nehmen wir an sie schreiben ein eigenes Tabellenkalkulationsprogramm (Das ist nicht sehr schwierig, der Autor hat das schon in seinen frühen Programmiertagen gemacht). Dann ist eines der zentralen Probleme die sie zu lösen haben, die Übersetzung eines Formel wie "A1+B1*C$" in einen Programmcode - und zwar zur Laufzeit. Mit den bisherigen Sprachmitteln kämen Sie irgendwann an einen Punkt, wo sie die Formel in Tokens hätten und dann in einer riesigen CASE Anweisung so was programmieren würden:

Case Token of

0: res:=Addfunc(par1,par2);
1: res:=Subfunc(par1,par2);

...

Das ist weder elegant noch schnell! Dafür gibt es Prozedur und Funktionstypen. Das sind Variablen die eine Adresse einer Funktion oder Prozedur aufnehmen. Wie Variablen können diese zugewiesen werden, als Parameter übergeben oder ein Array kann davon gebildet werden. Für obiges Beispiel gäbe es z.B. folgende Codeteile:

type Func_Mit_Zwei_Parametern Function(Par1,Par2 : double): double;

VAR myfuncs :Array [1..100] of Func_Mit_Zwei Parametern;
...
Function Addfunc(a,b : double): double;
...
begin
myfuncs[0]:=Addfunc;
myfuncs[1]:=subfunc;
...
res:=myfuncs[0](par1,par2);
...
Hier werden die Vorteile und die Anwendung deutlich:
  1. Deklaration: Schaffen Sie einen neuen Typ, wobei sie einem Typ nur eine Art von Funktionen zuweisen können, d.h. mit identischen Typen von Rückgabewert und Parametern. Für verschiedene Funktionen brauchen Sie auch verschiedene Typen. Das ganze geht auch mit Prozeduren. Die Syntax ist wie bei einer Funktionsdeklaration, nur lassen sie den Namen weg.
  1. Eine Variable deklarieren oder ein Array von Variablen wie hier: einfach eine normale VAR Anweisung nur eben von diesem Funktionstyp
  1. Zuweisung von Funktionen die Sie irgendwo im Programm stehen haben und die kompatibel zu dem Typ der Prozedurvariablen sind. Das können sie auch zur Laufzeit machen und beliebig oft ändern - z.B. für eigene Parser, Programmiersprachen, selbstmodifizierende Codes... Hier müssen sie nur den Namen der Funktion, aber keine Parameterliste angeben.
  1. Aufruf: Einfach der Funktionsvariable nun noch die Klammern mit Parametern angeben und das Ergebnis einer Variable zuweisen (bei Funktionen). Wäre Myfuncs im obigen Beispiel vom Typ "Procedure" also ohne jegliche Parameter und Rückgabewert so steht die etwas komische Quelltextzeile:

Myfuncs[0] da - Das ist jedoch nicht zu vermeiden, da man in Pascal eben keine Klammern angibt, wenn eine Prozedur oder Funktion keine Parameter hat.

Noch ein Unit Beispiel

Ein Beispiel einer komplexeren Unit mit eigenen nicht nach außen hin bekannten Subroutinen ist folgende, die eine Hardcopy auf HP Laserjet (PCL 4 aufwärts) kompatiblen Laserdruckern anfertigt:

unit Lpunit;
interface
function Ljbeg(Lport:Byte; Lsize:Word):Boolean;
procedure Ljend;
procedure Ljprint(Land:Boolean; Size,Threshold:Word);
procedure Printscreen(Lpt,Threshold:Word; Land:Boolean);
implementation
uses Graph;
type String12 string[12];
var Lst : Text;
const Prnport : Word 0;       {0=LPT1, 1=LPT2}
      {-------------------------------------------------------------}
      {LASER PRINTER SETUP TABLES}
const Eschr #$1B;
      Null  #0;
      Prnbeg Eschr+'E'; {RESET PRNTR, EJECT PAGE IF REQ}
      Prn75  Eschr+'*t75R'; {SET 75 DOTS PER INCH}
      Prn100 Eschr+'*t100R'; {SET 100 DOTS PER INCH}
      Prn150 Eschr+'*t150R'; {SET 150 DOTS PER INCH}
      Prn300 Eschr+'*t300R'; {SET 300 DOTS PER INCH}
      Prn200 Eschr+'*t200R'; {SET 200 DOTS PER INCH}
      Pritop Eschr+'&a100H'+ {SET CURSOR TO START POS}
      Eschr+'&a100V';
      Prlini Eschr+'*b'; {RASTER LINE HEADER FOR PRINTER}
      Prline 'W';         {RASTER LINE HEADER FOR PRINTER}
      Prnrst Eschr+'*r1A'; {BEGIN RASTER GRAPHICS}
      Prnend Eschr+'*rB'; {END RASTER GRAPHICS}
      Prnres Eschr+'E'; {RESET PRINTER AND EJECT PAGE}
function Prnstat:Byte; assembler;
asm
  Mov Ah,2
  Mov Dx,[Prnport]
  Int $17
  Mov Al,Ah
end;
procedure Printchar(C:Char); assembler;
asm
  Mov Al,[C]
  Mov Dx,[Prnport]
  Mov Ah,0
  Int $17
end;
procedure Print(C:Char);
begin
  while Prnstat and $A0 <> $80 do {NOP};
  Printchar(C);
end;
procedure Lprint(S:string);
var I : Word;
begin
  for I := 1 to Length(S) do
  begin
    Print(S[I]);
  end;
end;
function Fstr(L:LongInt):String12;
var S:string;
begin
  Str(L,S);
  Fstr := S;
end;
{---------------------------------------------}
{PREPARE EXTERNAL PRINTER FOR OUTPUT}
function Ljbeg(Lport:Byte; Lsize:Word):Boolean;
begin
  Ljbeg := True;
  if Lport > 2 then Exit;
  Prnport := Lport;
  Lprint(Prnbeg);
  case Lsize of
    50: Lprint(Prn75);
    75: Lprint(Prn75);
    100: Lprint(Prn100);
    150: Lprint(Prn150);
    200: Lprint(Prn200);
    300: Lprint(Prn300);
    else Lprint(Prn300);
  end;
end;
{---------------------------------------------}
{RESTORE EXTERNAL PRINTER TO NORMAL OPERATION}
procedure Ljend;
begin
  Lprint(Prnres);
end;
{----------------------------------------------}
{PRINT GRAPHICS AS LANDSCAPE}
procedure Doland(Size,Threshold:Word);
var Y,X,L,I,B : Word;
    C : Byte;
    D : Char;
    Raster : string;
begin
  for X := Getmaxx downto 0 do
  begin
    Y := 0;
    L := (Getmaxy+1) div 8;
    Lprint(Prlini+Fstr(L)+Prline);
    for B := 1 to L do
    begin
      D := #0;
      for I := 0 to 7 do
      begin
        C := Getpixel(X,Y);
        if C > Threshold then C := 1 else C := 0;
        D := Char((Ord(D) shl 1) or C);
        Inc(Y);
      end;
      Raster[B]:= D;
    end;
    Raster[0]:= Char(B);
    Lprint(Raster);
  end;
end;
{----------------------------------------------}
{PRINT GRAPHICS AS PORTRAIT}
procedure Doport(Size,Threshold:Word);
var Y,X,L,I,B : Word;
    C : Byte;
    D : Char;
    Raster : string;
begin
  for Y := 0 to Getmaxy do
  begin
    X := 0;
    L := (Getmaxx+1) div 8;
    Lprint(Prlini+Fstr(L)+Prline);
    for B := 1 to L do
    begin
      D := #0;
      for I := 0 to 7 do
      begin
        C := Getpixel(X,Y);
        if C > Threshold then C := 1 else C := 0;
        D := Char((Ord(D) shl 1) or C);
        Inc(X);
      end;
      Raster[B]:= D;
    end;
    Raster[0]:= Char(B);
    Lprint(Raster);
  end;
end;
{----------------------------------------------}
{DUMP THE SCREEN TO EXTERNAL LASER PRINTER}
{LAND=TRUE DO LANDSCAPE; LAND=FALSE DO PORTRAIT}
{THRESHOLD=COLOR->MONOCHROME CONVERSION THRESHOLD}
procedure Ljprint(Land:Boolean; Size,Threshold:Word);
begin
  Lprint(Pritop); {POSITION CURSOR}
  Lprint(Prnrst); {ENTER GRAPHICS MODE}
  if Land then
  Doland(Size,Threshold)
  else
  Doport(Size,Threshold);
  Lprint(Prnend); {CLOSE PRINTER GRAPHICS}
end;
{----------------------------------------------}
{DO A SCREEN DUMP. LPT=LPT PORT (0-2)}
{LAND=TRUE=LANDSCAPE; FALSE=PORTRAIT}
{THRESHOLD=COLOR->MONOCHROME CONVERSION THRESHOLD}
procedure Printscreen(Lpt,Threshold:Word; Land:Boolean);
var   Y : Integer;
    Dpi : Integer;
    Size : Word;
begin
  if Land then
  Y:=Getmaxy
  else
  Y := Getmaxx;     {7" max high on page}
  Size := 1;
  case Y of
    0..350: begin Dpi := 50; Size := 2; end;
    351..525: Dpi := 75;   {320x200, 640x480}
    631..700: Dpi := 100;  {800x600 (laserjet)}
    701..1050: Dpi := 150;  {1024x768}
    1051..1400: Dpi := 200;   {1280x 1024}
    else Dpi := 300;  {say what!?}
  end; {CASE Y OF}
  Ljbeg(Lpt,Dpi);
  Ljprint(Land,Size,Threshold);
  Ljend;
end;
end.

Ach ja wer macht denn so schöne Listings in HTML Code? Tja das erledigt unter anderem mein Delphi-Strukter...


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 Top 99