The following article was printed in October 1987 of the magazine „MC".
Tips and hints for Turbo Pascal
Peter Engels

Turbo-Tricks

Im Lauf der Zeit sammeln sich bei manchem Turbo-Pascal-Programmierer nützliche Prozeduren und Tricks an, die viel zu schade sind, um einfach in der Schublade zu landen. Auch die folgenden Routinen sind Nebenprodukte praktischer Arbeit - entwickelt und getestet auf einem Apple-II unter CP/M-80.

In der Turbo-Version 3.0 werden die logischen Gerätenamen CON: und TRM: nicht mehr unterschieden. Die B-Compiler-Directive ist daher unwirksam. Ein Blick in den Compiler zeigt, daß es sich vermutlich nicht um einen Bug handelt, sondern diese Unterscheidung aus Platzgründen nicht mehr vorgenommen wird. Ein Patch ist mir nicht bekannt.

Abhilfe schafft die Prozedur von Bild 1. Außerdem funktionieren die Editierhilfen ^R und ^D entgegen den Angaben im Handbuch nicht.
PROCEDURE READTRM (VAR KEY : CHAR);


BEGIN
 READ(KBD,KEY);
 IF  KEY  >= ' ' THEN  WRITE(KEY)
END;
Bild 1. Einen Lesevorgang vom logischen Gerät TRM: simuliert diese Prozedur

Assign mit Joker

Obwohl es im Handbuch nirgends erwähnt wird, akzeptiert die ASSIGN-Prozedur der Turbo-Version 2.0 das Fragezeichen als Joker (den Stern allerdings nicht). Dadurch können Prozeduren zum Löschen von Dateien sehr einfach und komfortabel geschrieben werden. Turbo 3.0 weigert sich hier und bricht den String beim ersten Auftreten des Fragezeichens ab: alle folgenden Zeichen werden ignoriert. Schaut man sich die Runtime-Library genauer an, so erkennt man, daß hier eine Routine implementiert wurde, die neben „?" sogar „*" richtig behandelt. Allerdings wird diese Routine nie angesprungen. Ursache ist das Byte in $3F3, das die Aufgabe eines Flags übernimmt: Ist der Wert ungleich null, werden die Jokerzeichen richtig behandelt. Da die Speicherstelle in der Library liegt, kann der Patch vom Pascal-Programm selbst vorgenommen werden. Man vereinbart im Deklarationsteil:
Var Wildcard: Boolean absolute $3F3:
Im Programm ordnet man dann der Variablen Wildcard den Wert TRUE zu, wenn man Jokerzeichen verwenden will.

Error-Handler

Der in mc 6/86 auf Seite 89 abgedruckte Error-Handler arbeitet auch in Turbo 3.0 ohne weitere Änderung, wenn man die absolute Adresse des Jump-Vektors von $1F75 auf $207A ändert und Zeile 51 zu
51: Inline ($32/ERROR_CODE/$DD/$E5)
ergänzt. Durch den Error-Handler werden Runtime-Fehler abgefangen: (*$I-*) schützt vor I/O-Fehlern. Kritisch sind dann nur noch BDOS-Fehler bzw. das Betätigen der Reset-Taste. Aber auch hier kann man sich schützen (Bild 2).
VAR  STACK     : INTEGER;
     WBOOTLO   : BYTE;
     WBOOTHI   : BYTE;
     BIOSADDR  : INTEGER ABSOLUTE $0001;


PROCEDURE CLR_ERROR;
BEGIN
 MEM[BIOSADDR+1] := WBOOTLO;
 MEM[BIOSADDR+2] := WBOOTHI
END;

PROCEDURE RESETHANDLER;
BEGIN
 STACKPTR := STACK;
 INLINE ($C3/$100)
END;

PROCEDURE INIT_ERROR;
BEGIN
 STACK := STACKPTR;
 WBOOTLO := MEM[BIOSADDR+1];
 WBOOTHI := MEM[BIOSADDR+2];
 MEM[BIOSADDR+1] := LO(ADDR(RESETHANDLER));
 MEM[BIOSADDR+2] := HI(ADDR(RESETHANDLER));
END;
Bild 2. Schutz vor dem unbeabsichtigten Betätigen der Reset-Taste bieten diese Prozeduren
Man ruft zu Beginn des Programms die Prozedur INIT_ERROR auf, die den entsprechenden Vektor im BIOS-Teil auf den Reset-Handler umlenkt. Am Ende des Programms wird dann die Prozedur CLR_ERROR aufgerufen, die den Vektor wieder geradebiegt.
Diese Prozeduren sollte man aber erst implementieren, wenn das eigene Programm vollständig ausgetestet ist. Sonst gerät man leicht in eine Endlosschleife - dann hilft nur noch der Netzschalter!

Zusätzliche Variablen

Turbo 3.0 verfügt über (mindestens) zwei zusätzliche Variablen, die im Handbuch nicht erwähnt werden:
  1. ERRORPTR ist vom Typ Integer und ermöglicht eine eigene Fehlerbehandlung;
  2. CBREAK ist vom Typ Boolean. Diese Variable steuert die C-Compiler-Option, so daß man diese Option nun auch vom Programm aus steuern kann, also zur Laufzeit.
CBREAK := TRUE entspricht (*$C+*) oder {$C+}
CBREAK := FALSE entspricht (*$C-*)

Die Variable ERRORPTR bedarf noch einiger Erläuterungen. Man ordnet ihr zu Beginn des Programms die Adresse der eigenen Fehlerbehandlungsroutine zu, also etwa
ERRORPTR := ADDR(ERRORHANDLER)

Der Prozedur ERRORHANDLER werden dann im Fehlerfall zwei Integer-Variablen übergeben, die Auskunft über die Art des aufgetretenen Fehlers geben. Das Programmierbeispiel von Bild 3 soll das verdeutlichen.

PROCEDURE ERRORHANDLER(ERRNR, ERRADDR : INTEGER);
VAR  NR    : BYTE;
BEGIN
  CLRSCR;
  GOTOXY(28,10);
  LOWVIDEO; WRITE(^G);
  CASE HI(ERRNR) OF
    0 : WRITE('   User - Break  ');
    1 : WRITE('   I/O - Error   ');
    2 : WRITE(' Runtime - Error ');
  END;
  NORMVIDEO;
  WRITELN;
  GOTOXY(20,12);
  LOWVIDEO;
  WRITE(' Error # $');
  NR := LO(ERRNR);
  INLINE ($3A/NR/$CD/$4B4);
  WRITE(' occurs at PC : $');
  INLINE ($2A/ERRADDR/$CD/$4AF);
  WRITE(' ');
  NORMVIDEO;
  DELAY(2000);
  INLINE ($C3/$100)
END;

PROCEDURE INIT_ERROR;
BEGIN
  ERRORPTR := ADDR(ERRORHANDLER)
END;
Bild 3. Der Prozedur ERRORHANDLER werden im Fehlerfall zwei Integer-Variablen übergeben
Die angeführten Inline-Statements sind ebenfalls Tricks, die im Handbuch nicht erwähnt werden, aber dennoch bekannt sein dürften: Das erste Statement gibt das Byte NR in hexadezimaler Schreibweise auf dem Bildschirm aus; das zweite die Integer-Variable ERRADDR. Die absoluten Adressen $4B4 und $4AF gelten für Turbo 3.0. In Turbo 2.0 sind die Adressen auf $492 und $48D zu ändern. Das letzte Inline-Statement veranlaßt einen Sprung zur Adresse $100, wodurch das Programm neu gestartet wird. Die Frage, ob der Turbo-eigene Error-Handler über die Möglichkeit eines RESUME verfügt, konnte ich bislang noch nicht klären. Ich vermute aber, daß das unmöglich ist.

Man muß bei der Verwendung des ERRORPTR lediglich beachten, daß nach Auftreten eines Fehlers diese Variable neu initialisiert werden muß, gleiches gilt für eine eventuelle OVRDRIVE-Anweisung.

Chain-Files

Abschließend noch ein Tip zur Verwendung von CHN-Files.

Will man auf einem Apple-II hochauflösende Grafik benutzen, so muß das Programm als COM-File mit einer Startadresse über $5000 compiliert werden. Dadurch werden auf der Diskette etwa 10 KByte Speicherplatz verschenkt, außerdem dauert das Laden des Programms unnötig lang. Es ist schon vorgeschlagen worden, statt dessen das Grafikprogramm als CHN-File zu compilieren und es von einem als COM-File compilierten Menü-Programm zu starten. Dieses Verfahren ist aber erst dann effektiv, wenn man mehrere Grafikprogramme von diesem Menü-Programm aus starten will, was mir wenig sinnvoll erscheint. Mit einem Trick kann man aber diesen Speicherplatz völlig einsparen: Nehmen wir an, das Hauptprogramm hieße GRAFIK.PAS und wäre als CHN-File mit einer Startadresse von $5000 auf Diskette compiliert. Das Programm von Bild 4 startet dann dieses CHN-File, obwohl es als COM-File mit der kleinstmöglichen Startadresse compiliert wurde.

PROGRAM STARTUP;

VAR DATEI       : FILE;
    STARTADDR   : INTEGER ABSOLUTE $101;

BEGIN
  STARTADDR := $5000;  { Startadresse des Chain-Files }
{$I-}
  ASSIGN(DATEI,'GRAFIK.CHN');
  RESET(DATEI);
  IF IORESULT <>
   0 THEN WRITELN(^G ,'Datei GRAFIK.CHN nicht gefunden. ',
                      'Programm abgebrochen.')
  ELSE CHAIN(DATEI)
{$I+}
END.
Bild 4. Dieses Programm startet ein Programm mit dem Namen GRAFIK.CHN, das als Chain-File compiliert wurde

Scanned by Werner Cirsovius
February 2005
© Franzis' Verlag