software:diy:pascal:pascalk
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| software:diy:pascal:pascalk [27/01/2006 20:01] – mischroeder | software:diy:pascal:pascalk [Unbekanntes Datum] (aktuell) – Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1 | ||
|---|---|---|---|
| Zeile 3: | Zeile 3: | ||
| **__von Torsten Häßer__** | **__von Torsten Häßer__** | ||
| - | ===== Teil 1 ===== | + | ====== Teil 1 ====== |
| - | ==== Vorweg ==== | + | ===== Vorweg |
| Als ich vor gut einem halben Jahr meinen Portfolio (im weiteren Verlauf liebevoll Pofi genannt) bekommen habe, war eine der tollsten Dinge an dem kleinen Kasten, daß man nicht nur auf die internen Anwendungen beschränkt war. Nach der ersten RAM-Karte und den ersten Quellen für Pofisoft machte sich dann aber schnell der Wunsch breit, daß man das, was man nicht bekommen konnte ebend selbst schreibt. | Als ich vor gut einem halben Jahr meinen Portfolio (im weiteren Verlauf liebevoll Pofi genannt) bekommen habe, war eine der tollsten Dinge an dem kleinen Kasten, daß man nicht nur auf die internen Anwendungen beschränkt war. Nach der ersten RAM-Karte und den ersten Quellen für Pofisoft machte sich dann aber schnell der Wunsch breit, daß man das, was man nicht bekommen konnte ebend selbst schreibt. | ||
| Gesagt, getan. Ich war natürlich der Meinung, PC bleibt PC und Programme die für den PC geschrieben sind laufen auch auf dem Pofi. Einschränkungen ergeben sich daher nur aus der Größe des Bildschirms und der Größe der Programme. Was lag da also näher als die Programmiersprache zu benutzen, in der ich auch sonst meinen Unsinn produziere. | Gesagt, getan. Ich war natürlich der Meinung, PC bleibt PC und Programme die für den PC geschrieben sind laufen auch auf dem Pofi. Einschränkungen ergeben sich daher nur aus der Größe des Bildschirms und der Größe der Programme. Was lag da also näher als die Programmiersprache zu benutzen, in der ich auch sonst meinen Unsinn produziere. | ||
| - | Ich suchte also eines meiner alten Listings heraus und feilte ein bißchen an der Bilschirmausgabe herum, um diese dann auf den Pofi zu übertragen. Die Überraschung war nicht schlecht als ich dezent vom Transferprogramm darauf hingewiesen wurde, daß zu wenig Platz auf dem Pofi ist. Wenn man sich einmal den Dimensionssprung vor Augen führt, mit dem wir es hier zu tun haben ist das auch wenig verwunderlich. Unser kleiner Liebling hat nun einmal von Haus aus keine Festplatte und nur 640 KB RAM. Programme, die man vorher für relativ klein gehalten hat, sind aus der Sicht des Pofi regelrechte Speicherriesen. | + | Ich suchte also eines meiner alten Listings heraus und feilte ein bißchen an der Bilschirmausgabe herum, um diese dann auf den Pofi zu übertragen. Die Überraschung war nicht schlecht als ich dezent vom Transferprogramm darauf hingewiesen wurde, daß zu wenig Platz auf dem Pofi ist. Wenn man sich einmal den Dimensionssprung vor Augen führt, mit dem wir es hier zu tun |
| + | haben ist das auch wenig verwunderlich. Unser kleiner Liebling hat nun einmal von Haus aus keine Festplatte und nur 640 KB RAM. Programme, die man vorher für relativ klein gehalten hat, sind aus der Sicht des Pofi regelrechte Speicherriesen. | ||
| Nachdem ich also etwas Platz geschaffen hatte, paßte es dann doch noch auf die RAM-Karte. Start ... BUM. Ich mußte dann erstmal für eine geraume Zeit die Batterien entfernen, um den Pofi wieder zu beleben ( ein wirklich zäher kleiner Bursche, wenn es darum geht Daten im RAM zu halten ). | Nachdem ich also etwas Platz geschaffen hatte, paßte es dann doch noch auf die RAM-Karte. Start ... BUM. Ich mußte dann erstmal für eine geraume Zeit die Batterien entfernen, um den Pofi wieder zu beleben ( ein wirklich zäher kleiner Bursche, wenn es darum geht Daten im RAM zu halten ). | ||
| Zeile 16: | Zeile 17: | ||
| Resümee dieser Aktion : Einfache Portierung der Programme ist anscheinend nicht möglich. Na gut. Ich war allerdings dickköpfig genug, um mich nicht so leicht von meinem Vorhaben abbringen zu lassen. Es hat mich dann viel Mühe gekostet an einigermaßen brauchbare Unterlagen zu kommen, die mir bei diesem Problem behilflich sein konnten. Darum geht es also in diesem(n) Artikel(n). Kein Programmierkurs in Pascal. Dafür gibt es genügend andere und bessere Literatur (siehe Anhang). Es geht darum, wie ich Anwendungen schreibe die speziell auf den Portfolio zugeschnitten sind und trotzdem mit einer PC-Hochsprache compiliert werden. | Resümee dieser Aktion : Einfache Portierung der Programme ist anscheinend nicht möglich. Na gut. Ich war allerdings dickköpfig genug, um mich nicht so leicht von meinem Vorhaben abbringen zu lassen. Es hat mich dann viel Mühe gekostet an einigermaßen brauchbare Unterlagen zu kommen, die mir bei diesem Problem behilflich sein konnten. Darum geht es also in diesem(n) Artikel(n). Kein Programmierkurs in Pascal. Dafür gibt es genügend andere und bessere Literatur (siehe Anhang). Es geht darum, wie ich Anwendungen schreibe die speziell auf den Portfolio zugeschnitten sind und trotzdem mit einer PC-Hochsprache compiliert werden. | ||
| - | ==== Die ersten Schritte ==== | + | ===== Die ersten Schritte |
| Grundsätzlich müssen Pascal-Programme für den Pofi also vor allem immer mit dem vorhandenen Speicherplatz vor Augen geschrieben werden. Pascal selber geht bei der Übersetzung des Codes nicht gerade sparsam damit um. Debug-Informationen, | Grundsätzlich müssen Pascal-Programme für den Pofi also vor allem immer mit dem vorhandenen Speicherplatz vor Augen geschrieben werden. Pascal selber geht bei der Übersetzung des Codes nicht gerade sparsam damit um. Debug-Informationen, | ||
| Zeile 44: | Zeile 45: | ||
| Natürlich gibt es ein paar entscheidende Unterschiede zwischen einem // | Natürlich gibt es ein paar entscheidende Unterschiede zwischen einem // | ||
| - | ==== Die Units ==== | + | ===== Die Units ===== |
| Tataaaa ... Der nächste Hammer. Die Pascal-CRT-Unit kann man leider nicht verwenden. Sie ist verantwortlich für den massiven Absturz, den ich in der Einleitung beschrieben habe. Anscheinend wird hier doch näher an der Hardware gearbeitet als es von außen scheint. Es gibt aber wohl kaum ein DOS-Programm, | Tataaaa ... Der nächste Hammer. Die Pascal-CRT-Unit kann man leider nicht verwenden. Sie ist verantwortlich für den massiven Absturz, den ich in der Einleitung beschrieben habe. Anscheinend wird hier doch näher an der Hardware gearbeitet als es von außen scheint. Es gibt aber wohl kaum ein DOS-Programm, | ||
| + | |||
| "Oh Gott ... was kommt jetzt ...". Grundsätzlich hört sich das natürlich schlimmer an als es ist. Sehen wir uns erst einmal an, was das für Funktionen sind. | "Oh Gott ... was kommt jetzt ...". Grundsätzlich hört sich das natürlich schlimmer an als es ist. Sehen wir uns erst einmal an, was das für Funktionen sind. | ||
| Zeile 74: | Zeile 76: | ||
| Vielleicht werde ich diese Sachen aber zu einem späteren Zeitpunkt noch mit einbauen. Wenn sich aber Leute damit schon beschäftigt haben und eine einigermaßen gute und stabile Lösung darbieten können, wäre ich natürlich dankbar, wenn ich mir diese Mühe sparen könnte ... :o) | Vielleicht werde ich diese Sachen aber zu einem späteren Zeitpunkt noch mit einbauen. Wenn sich aber Leute damit schon beschäftigt haben und eine einigermaßen gute und stabile Lösung darbieten können, wäre ich natürlich dankbar, wenn ich mir diese Mühe sparen könnte ... :o) | ||
| - | Wichtig bei der ganzen Sache ist für uns natürlich auch, daß die neuen Funktionen weitgehend aufrufkompatibel zu den gewohnten bleiben. Wir wollen ja schließlich keine neuen Befehle lernen, sondern in gewohnter Weise weiter machen können. Ich hab mich also mal daran gemacht und aus verschiedenen Quellen diese Sachen zusammen gesucht. Die komplette Unit als Pascal-Quelltext liegt in der FIXME. | + | Wichtig bei der ganzen Sache ist für uns natürlich auch, daß die neuen Funktionen weitgehend aufrufkompatibel zu den gewohnten bleiben. Wir wollen ja schließlich keine neuen Befehle lernen, sondern in gewohnter Weise weiter machen können. Ich hab mich also mal daran gemacht und aus verschiedenen Quellen diese Sachen zusammen gesucht(( in den Downloads |
| Ich will jetzt auch gar nicht so intensiv darauf eingehen wie die einzelnen Routinen | Ich will jetzt auch gar nicht so intensiv darauf eingehen wie die einzelnen Routinen | ||
| Zeile 96: | Zeile 98: | ||
| - | ===== Teil 2 ===== | + | ====== Teil 2 ====== |
| - | ==== Das AES ==== | + | ===== Das AES ===== |
| Man kann sagen was man will. Aber die Firma ATARI hatte in Ihrer Glanzzeit immer einen Riecher für Sachen, die ihrer Zeit eigentlich weit voraus waren. So wie die ATARI-ST Serie vom Betriebssystem her den damaligen PC's überlegen war, genau so ist das Portfolio-DOS dem damaligen PC-DOS in einem Punkt überlegen. Den Leuten, die schon mal auf anderen Plattformen wie Amiga, Mac oder eben ST's programmiert haben wird der Begriff " | Man kann sagen was man will. Aber die Firma ATARI hatte in Ihrer Glanzzeit immer einen Riecher für Sachen, die ihrer Zeit eigentlich weit voraus waren. So wie die ATARI-ST Serie vom Betriebssystem her den damaligen PC's überlegen war, genau so ist das Portfolio-DOS dem damaligen PC-DOS in einem Punkt überlegen. Den Leuten, die schon mal auf anderen Plattformen wie Amiga, Mac oder eben ST's programmiert haben wird der Begriff " | ||
| Zeile 121: | Zeile 123: | ||
| Das ist also unser Werkzeug. Die Funktionen Fn $00 - Fn $03 sind allerdings nicht Thema in diesem Teil des Kurses. Interressant sind hier ohnehin nur der "Line Editor" | Das ist also unser Werkzeug. Die Funktionen Fn $00 - Fn $03 sind allerdings nicht Thema in diesem Teil des Kurses. Interressant sind hier ohnehin nur der "Line Editor" | ||
| - | ==== 1.0 Fn $08 Screen Save/ | + | ===== 1.0 Fn $08 Screen Save/ |
| Diese Funktion gibt uns die Möglichkeit, | Diese Funktion gibt uns die Möglichkeit, | ||
| Zeile 128: | Zeile 130: | ||
| - RestoreScreen ( xtl, ytl, xbr, ybr, buffer ) => die Daten/ | - RestoreScreen ( xtl, ytl, xbr, ybr, buffer ) => die Daten/ | ||
| - | ==== 2.0 Fn $09 Draw box ==== | + | ===== 2.0 Fn $09 Draw box ===== |
| Mit Hilfe dieser Funktion kann ich eine Box bzw. einen Rahmen zeichnen lassen. Die Koordinatenangaben, | Mit Hilfe dieser Funktion kann ich eine Box bzw. einen Rahmen zeichnen lassen. Die Koordinatenangaben, | ||
| Zeile 134: | Zeile 136: | ||
| Die Implementation | Die Implementation | ||
| - | ==== 3.0 Fn $0F Menu handling ==== | + | ===== 3.0 Fn $0F Menu handling |
| Das ist eine der mächtigsten und nützlichsten Funktionen überhaupt. Mit ihrer Hilfe kann man die bekannten Menüs aus den internen Applikationen aufrufen. Das Handling dieser Menüs ist denkbar einfach. Mit ESC kann man sie abbrechen. Durch die Cursor-Tasten kann man zu den einzelnen Menüpunkten springen. Durch drücken des ersten Buchstaben des Menütextes wird der Menüpunkt direkt aufgerufen. Wem das zu kurz war, soll nochmal lesen, oder sich seine Portfolioanleitung schnappen um es sich dort noch mal durchlesen ... | Das ist eine der mächtigsten und nützlichsten Funktionen überhaupt. Mit ihrer Hilfe kann man die bekannten Menüs aus den internen Applikationen aufrufen. Das Handling dieser Menüs ist denkbar einfach. Mit ESC kann man sie abbrechen. Durch die Cursor-Tasten kann man zu den einzelnen Menüpunkten springen. Durch drücken des ersten Buchstaben des Menütextes wird der Menüpunkt direkt aufgerufen. Wem das zu kurz war, soll nochmal lesen, oder sich seine Portfolioanleitung schnappen um es sich dort noch mal durchlesen ... | ||
| + | |||
| In der Unit sieht der Funktionsaufruf folgendermaßen aus: | In der Unit sieht der Funktionsaufruf folgendermaßen aus: | ||
| => Menu ( xtl, ytl, MenuTitle, MenuText, DefaultsText , selectedItem ) | => Menu ( xtl, ytl, MenuTitle, MenuText, DefaultsText , selectedItem ) | ||
| - | Die Werte für " | + | Die Werte für **" |
| **Beispiel: | **Beispiel: | ||
| + | |||
| + | <code pascal> | ||
| MenuText := 'Point #1' + Chr(0) + 'Point #2' + Chr(0) + ' | MenuText := 'Point #1' + Chr(0) + 'Point #2' + Chr(0) + ' | ||
| + | </ | ||
| - | Die Funktion zerlegt den String dann selbsständig, | + | Die Funktion zerlegt den String dann selbsständig, |
| + | <code pascal> | ||
| + | DefaultText := '' | ||
| + | </ | ||
| + | dann würde am rechten Rand in der Zeile, wo der Menüeintrag | ||
| - | ==== 4.0 Fn $10 Box area calculation ==== | + | ===== 4.0 Fn $10 Box area calculation ===== |
| + | |||
| + | Hiermit kann man sich eine Menge Arbeit ersparen. Mit Hilfe dieser Funktion kann man im voraus einige Daten ermitteln, die für das Menühandling oder auch für die später erklärte Funktion //Message Windows// nötig sind. Die Eingabewerte sind die gleichen wie beim Aufruf von //Menu//. Der Unterschied ist, daß man als Rückgabedaten die Koordinaten der rechten-unteren Ecke bekommt. Das ist sehr wichtig. Als Programmierer ist man selbst dafür verantwortlich, | ||
| - | Hiermit kann man sich eine Menge Arbeit ersparen. Mit Hilfe dieser Funktion kann man im voraus einige Daten ermitteln, die für das Menühandling oder auch für die später erklärte Funktion " | ||
| => BoxAreaCalculation ( xtl, ytl, TitleText, MenuText, DefaultsText, | => BoxAreaCalculation ( xtl, ytl, TitleText, MenuText, DefaultsText, | ||
| - | ==== 5.0 Fn $12 Message windows ==== | + | ===== 5.0 Fn $12 Message windows ===== |
| + | |||
| + | Der Funktion wird genau wie bei //" | ||
| - | Der Funktion wird genau wie bei " | ||
| In der Unit sieht das ganze dann so aus: | In der Unit sieht das ganze dann so aus: | ||
| + | |||
| => MessageWindow ( xtl, ytl, WindowTitle, | => MessageWindow ( xtl, ytl, WindowTitle, | ||
| - | ==== 6.0 Fn $14 Error windows ==== | + | ===== 6.0 Fn $14 Error windows |
| Die "Error Windows" | Die "Error Windows" | ||
| Zeile 223: | Zeile 236: | ||
| - | ===== Teil 3 ===== | + | ====== Teil 3 ====== |
| In den letzten beiden Kursteilen ging es um die Grundlagen, die ich brauche, um überhaupt Programme auf dem Portfolio schreiben zu können. In diesem Teil soll es nun um die Praxis gehen. | In den letzten beiden Kursteilen ging es um die Grundlagen, die ich brauche, um überhaupt Programme auf dem Portfolio schreiben zu können. In diesem Teil soll es nun um die Praxis gehen. | ||
| - | ==== Vorweg ==== | + | ===== Vorweg |
| - | Bevor wir aber etwas Handfestes anfangen, will ich noch ein paar allgemeine Sachen zur praktischen Seite der Programmierung sagen. Wenn ich nun anfange ein Programm zu schreiben, stehe ich vor zwei Problemen. Wenn ich spezielle Funktionen des Portfolio nutzen will, kann ich die Programme nicht unter einem normalen PC-DOS testen. Ich meine damit die Routinen, die vom Portfolio-AES zur Verfügung gestellt werden. Also wie kann ich meine Programme testen ohne sie jedesmal auf den Portfolio zu überspielen. Aus diesem Problem ergibt sich natürlich auch noch ein zweites. Da ich die Programme nicht auf dem PC laufen lassen kann, kann ich auch nicht die Funktionen zum Debugging nutzen, die mir das PASCAL zur Verfügung stellt. Ich kann also nicht ein Programm während der Laufzeit kurz unterbrechen, | + | Bevor wir aber etwas Handfestes anfangen, will ich noch ein paar allgemeine Sachen zur praktischen Seite der Programmierung sagen. Wenn ich nun anfange ein Programm zu schreiben, stehe ich vor zwei Problemen. |
| + | |||
| + | * Wenn ich spezielle Funktionen des Portfolio nutzen will, kann ich die Programme nicht unter einem normalen PC-DOS testen. Ich meine damit die Routinen, die vom Portfolio-AES zur Verfügung gestellt werden. Also wie kann ich meine Programme testen ohne sie jedesmal auf den Portfolio zu überspielen. | ||
| + | *Aus diesem Problem ergibt sich natürlich auch noch ein zweites. Da ich die Programme nicht auf dem PC laufen lassen kann, kann ich auch nicht die Funktionen zum Debugging nutzen, die mir das PASCAL zur Verfügung stellt. | ||
| + | |||
| + | Ich kann also nicht ein Programm während der Laufzeit kurz unterbrechen, | ||
| + | |||
| + | Zumindest das erste Problem läßt sich recht leicht lösen. Ich muß natürlich nicht die Programme erst auf den Portfolio übertragen, | ||
| + | |||
| + | Das Problem mit dem Debugging ist damit aber immer noch nicht ganz behoben. Natürlich kann ich jetzt so verfahren, und nach den TSR-Programmen das PASCAL starten. Jetzt muß ich die Programme nicht mehr übersetzen und dann laufen lassen, sondern kann sie direkt vom PASCAL-Editor starten und das Programm über Haltepunkte stoppen und meine Variablenwerte einsehen. Wem das genügt der ist damit natürlich bestens bedient. Nun leben wir aber im Zeitalter von Windows 95 und OS/2 und ich kann nicht einfach nur in einem Single-Task-System arbeiten. Ich zumindest nicht. Ich bin es seit meinem ersten Computer (ein ATARI 1040STFM ) gewohnt, mit Fenstern und der Maus zu arbeiten. | ||
| + | |||
| + | Auf meinem PC läuft Windows 95 (Oh Gott ... ich habe mich geoutet) und ich will natürlich auch da weiter alle Programm parallel laufen lassen ohne sie jedesmal neu starten zu müssen. Prinzipiell kann ich allerdings auch unter Windows eine virtuelle DOS-Sitzung starten und die TSR-Programme und den PASCAL- Editor dort hochfahren. Es geht auch so, wenn auch sehr viel schwieriger. | ||
| Dann ist da noch die Frage, welches PASCAL nimmt man denn nun. Ich persönlich benutze PASCAL 7.0 (und dann möglichst die Oberfläche unter Windows). Ab dieser PASCAL-Version unterstützt der Editor die farbliche Markierung von Schlüsselwörtern, | Dann ist da noch die Frage, welches PASCAL nimmt man denn nun. Ich persönlich benutze PASCAL 7.0 (und dann möglichst die Oberfläche unter Windows). Ab dieser PASCAL-Version unterstützt der Editor die farbliche Markierung von Schlüsselwörtern, | ||
| Zeile 235: | Zeile 259: | ||
| ==== Hooks ==== | ==== Hooks ==== | ||
| - | O.K. ... soweit zu unseren Werkzeugen. Dieser Kursteil soll sich ja mit einem konkreten Beispiel befassen. Es sieht also so aus, daß ich euch ein Programm | + | O.K. ... soweit zu unseren Werkzeugen. |
| + | |||
| + | Dieser Kursteil soll sich ja mit einem konkreten Beispiel befassen. Es sieht also so aus, daß ich euch ein Programm | ||
| + | |||
| + | Ups ... was ist ein Hook-File. Vielleicht ist es manchen Portfolio-Usern noch nicht aufgefallen, | ||
| + | |||
| + | Das Geheimnis ist, ich kann vom Texteditor kleine Programme nachladen, die dann irgendwas mit meinem Text anstellen können. Hook-Files müssen immer im Root-Verzeichnis von Laufwerk A liegen, und die Dateiendung .HOO haben. Aufgerufen werden diese netten kleinen Helfer über die Taste <key>F6</ | ||
| + | |||
| + | Die Textverarbeitung wird dabei aber nicht beendet und der gerade bearbeitete Text geht verloren. Nein ... ich habe vielmehr über spezielle BIOS-Funktionen, | ||
| + | |||
| + | Ich kann also von meinem kleinen Hook-File aus den Text komplett bearbeiten. Man sieht also, ich kann wunderbar kleine Zusatzfunktionen für die doch manchmal recht magere Textverarbeitung schreiben. Schreiben wir uns also so eine kleine Funktionserweiterung. Hook-Files sind eigentlich nichts anderes als normale Programme, bei denen man die Endung .EXE durch .HOO ersetzt hat. Hook-Files sind nur als normale Programme recht nutzlos, weil sie ja voraussetzen, | ||
| + | |||
| + | Das Problem ist, daß diese Programme recht klein sein müssen. Wenn ich nur einen Standard-Portfolio mit 128kByte RAM habe, und die Textverarbeitung hat auch noch etwas geladen, dann bleibt nicht mehr so viel Platz, um unsere kleinen Helfer unterzubringen. PASCAL ist da auf Grund der Größe, die die übersetzten Programme haben auch nicht die beste Sprache, um so etwas zu realisieren. Wir beweisen aber nun, daß es doch geht. | ||
| === Quellcode für ein HOOkfile === | === Quellcode für ein HOOkfile === | ||
| Zeile 302: | Zeile 338: | ||
| Das Hook-File soll, wenn es aufgerufen wird, das aktuelle Datum oder die Uhrzeit oder auch beides als String in den Text einfügen. Die Auswahl, was eingefügt wird, geschieht über eines der üblichen Portfolio-Menüs. | Das Hook-File soll, wenn es aufgerufen wird, das aktuelle Datum oder die Uhrzeit oder auch beides als String in den Text einfügen. Die Auswahl, was eingefügt wird, geschieht über eines der üblichen Portfolio-Menüs. | ||
| - | Gehen wir nun den Quelltext langsam durch ... | + | ===== Gehen wir nun den Quelltext langsam durch ... ===== |
| Wie schon im ersten Teil des Kurses erwähnt, muß ich den Speicherbedarf des Programms so gering wie möglich halten. Das bedeutet, daß ich mir nur soviel Speicher reserviere, wie ich auch benötige. Die Zeile mit der {$M ... Anweisung macht den Compiler darauf aufmerksam, daß mein Programm nur 3072 Byte für seinen lokalen Stack reservieren möchte. Ich kann die Bytes für den Stack leider immer nur in 1024er Schritten reservieren. Dadurch kommt diese Zahl zustande. 2048 Bytes führen zu einem Stack-Überlauf also reserviere ich 1024 mehr. | Wie schon im ersten Teil des Kurses erwähnt, muß ich den Speicherbedarf des Programms so gering wie möglich halten. Das bedeutet, daß ich mir nur soviel Speicher reserviere, wie ich auch benötige. Die Zeile mit der {$M ... Anweisung macht den Compiler darauf aufmerksam, daß mein Programm nur 3072 Byte für seinen lokalen Stack reservieren möchte. Ich kann die Bytes für den Stack leider immer nur in 1024er Schritten reservieren. Dadurch kommt diese Zahl zustande. 2048 Bytes führen zu einem Stack-Überlauf also reserviere ich 1024 mehr. | ||
| Das Programm benutzt wie schon erwähnt die Menüfunktionen, | Das Programm benutzt wie schon erwähnt die Menüfunktionen, | ||
| - | Die kleine Funktion “Expandieren“ wandelt einen numerischen Wert vom Typ “Word“ in einen String um. Wenn der Wert nur einstellig sein sollte, dann wird vorne noch eine Null als ASCII-Zeichen angefügt. Das ist nötig, wenn ich als Anzahl der Minuten nur die Ziffer 6 bekomme, denn ansonsten würde meine Uhrzeit ja “21:6 Uhr“ lauten. Und das sieht wirklich nicht so schön aus. Zum Umwandeln der Zahl in einen String, benutzt das Programm die PASCAL-Funktion “STR“. Dann wird geprüft, ob die Länge des Strings kleiner als zwei ist und wenn ja, dann wird besagte Null noch vorne angeheftet. Als Funktion liefert sie den sich ergebenden String direkt zurück. Weil ich diese Funktion mehrere Male benötige, habe ich sie in eine Funktion außerhalb der Hauptschleife platziert. Der PASCAL-Compiler braucht bei der Übersetzung zwar mehr Speicher für den Sprung in eine Subroutine, wenn ich diese aber wirklich öfter brauche, spart es mir doch wieder mehr Speicherplatz, | + | Die kleine Funktion |
| - | Die Prozedur “SendeString “ ist sozusagen das Herz des Systems. Sie stellt die Schnittstelle zur Textverarbeitung dar. In der Variablen “wasSenden“ wird der Text übergeben, den ich einfügen will. Da ich in dieser Prozedur einen Interrupt aufrufe, muß ich mir eine Variable anlegen, in der ich die Prozessor-Register direkt mit Werten füllen | + | Als Funktion liefert sie den sich ergebenden String direkt zurück. Weil ich diese Funktion mehrere Male benötige, habe ich sie in eine Funktion außerhalb der Hauptschleife platziert. Der PASCAL-Compiler braucht bei der Übersetzung zwar mehr Speicher für den Sprung |
| - | Hier mal eine kleine Auflistung, die aus der Orginalbeschreibung | + | Die Prozedur **SendeString** ist sozusagen das Herz des Systems. Sie stellt die Schnittstelle zur Textverarbeitung dar. In der Variablen **“wasSenden“** wird der Text übergeben, den ich einfügen will. Da ich in dieser Prozedur einen Interrupt aufrufe, muß ich mir eine Variable anlegen, in der ich die Prozessor-Register direkt mit Werten füllen kann. Die DOS-Unit stellt mir einen passenden Datentyp zur Verfügung. Diese Interrupt-Routine ist natürlich nur speziell auf dem Portfolio vorhanden. In “reg.ah“ wird die Funktionsnummer übergeben. Da diese Funktion auch noch andere Unterfunktionen hat, muß ich die Nummer dieser Unterfunktion im AH-Register übergeben. Die Funktion $03 beinhaltet alle Routinen, die von Hook-Files verwendet werden können, um mit der Textverarbeitung zu kommunizieren. |
| + | Hier mal eine kleine Auflistung, die aus der Orginalbeschreibung von DIP stammt : | ||
| Fn 03H Reserved for CustomAdd-ins | Fn 03H Reserved for CustomAdd-ins | ||
| Zeile 329: | Zeile 367: | ||
| - | Wir benötigen für unsere Zwecke die Unterfunktion EDHO_INS. Die Funktion benötigt die Segmentadresse im ES-Register und die Offsetadresse im BX-Register. Das BIOS erwartet allerdings einen sogenannten C-String, daß heißt einen nullterminierten String. Aus diesem Grund muß ich an meinen PASCAL-String auch noch ein Nullzeichen anhängen. Das bekomme ich durch die Funktion “CHR(0)“ oder indem ich einfache “#0“ anhänge. Der String ist jetzt nullterminiert. Warum ich dann die Adressen in dieser speziellen Form übergeben muß und was das mit den C-Strings genau zu tun hat, habe ich schon mal im zweiten Kursteil erklärt. Also, wenn es noch nicht klar ist, dann dort nachsehen. Gut, wir haben nun also eine Prozedur, der ich einen PASCAL-String mitgebe und die diesen dann an die Textverarbeitung weiterreicht. Super ... machen wir uns also an die Bedienoberfläche unseres kleinen Helfers. | + | Wir benötigen für unsere Zwecke die Unterfunktion EDHO_INS. Die Funktion benötigt die Segmentadresse im ES-Register und die Offsetadresse im BX-Register. Das BIOS erwartet allerdings einen sogenannten C-String, daß heißt einen nullterminierten String. Aus diesem Grund muß ich an meinen PASCAL-String auch noch ein Nullzeichen anhängen. Das bekomme ich durch die Funktion “CHR(0)“ oder indem ich einfache |
| Die drei nun folgenden Konstantendeklarationen sind einfach nur eine Hilfe, um im Programm selber einige Wertzuweisungen zu sparen. Es ist ohnehin besser, wenn ich Daten, die sich nie verändern werden als Konstanten in den Quelltext einbringe. Es spart mir unter Umständen wieder einiges an Speicherplatz bei dem später übersetzten Programm. “wochenTage“ ist eine sogenannte typisierte Konstante. Ich kann auf die Elemente wie in einem normalen Array zugreifen. Hier habe ich mir die Strings für die Wochentage abgelegt. Den Wert, den ich später bei der Datumsabfrage zurückbekomme, | Die drei nun folgenden Konstantendeklarationen sind einfach nur eine Hilfe, um im Programm selber einige Wertzuweisungen zu sparen. Es ist ohnehin besser, wenn ich Daten, die sich nie verändern werden als Konstanten in den Quelltext einbringe. Es spart mir unter Umständen wieder einiges an Speicherplatz bei dem später übersetzten Programm. “wochenTage“ ist eine sogenannte typisierte Konstante. Ich kann auf die Elemente wie in einem normalen Array zugreifen. Hier habe ich mir die Strings für die Wochentage abgelegt. Den Wert, den ich später bei der Datumsabfrage zurückbekomme, | ||
| - | Nun die globalen Variablen. Die Namen sind so gewählt, daß sie eigentlich aussagekräftig genug sein müßten. In den beiden Strings “datum“ und “uhrzeit“ wird der darzustellende String zusammengestückelt. “puffer“ ist für meine Sicherungskopie des Bildschirms und in “auswahl“ bekomme ich die Wahl des Users zurückgeliefert. | + | Nun die globalen Variablen. Die Namen sind so gewählt, daß sie eigentlich aussagekräftig genug sein müßten. In den beiden Strings |
| - | Machen wir uns an das Hauptprogramm. Zuerst einmal bin ich selber dafür verantwortlich, | + | Machen wir uns an das Hauptprogramm. Zuerst einmal bin ich selber dafür verantwortlich, |
| - | Diese Sicherheit hätte ich also. Nun kann ich ganz beruhigt zur Arbeit schreiten. Die beiden Prozeduren “GetDate“ und “GetTime“ sind Standardroutinen aus der PASCAL-Unit : DOS. Mit ihrer Hilfe kann ich das aktuelle Datum und die Uhrzeit erfragen. “GetDate“ gibt mir nicht nur das Datum zurück, sondern meldet mir auch gleichzeitig noch, welchen Wochentag wir haben. Nett ... Wenn ich es schon so leicht gemacht bekomme, dann will ich das auch nutzen und den Wochentag in meinem Datumsstring mit anzeigen. Nun muß man wissen, in welcher Form mir die Prozedur den Wochentag zurückgibt. Der Wochentag ist eine Zahl zwischen 0 und 6. Null bedeutet dabei Sonntag und sechs bedeutet Samstag. Das erklärt, warum die Indizes meiner Wochentagskonstante von 0 bis 6 gehen und warum der Inhalt dann mit “So“ anfängt und nicht mit Montag. Ich kann jetzt, wie oben schon mal erwähnt, den Rückgabewert von “GetDate“ direkt als Index für das Array benutzen. | + | Diese Sicherheit hätte ich also. Nun kann ich ganz beruhigt zur Arbeit schreiten. Die beiden Prozeduren |
| - | Es ist natürlich eine Geschmacksfrage wie mein Datum dargestellt wird. Ich mag es, wenn ich zum Beispiel bei der Jahreszahl | + | Nun muß man wissen, in welcher Form mir die Prozedur den Wochentag zurückgibt. Der Wochentag ist eine Zahl zwischen 0 und 6. Null bedeutet dabei Sonntag und sechs bedeutet Samstag. Das erklärt, warum die Indizes meiner Wochentagskonstante von 0 bis 6 gehen und warum der Inhalt |
| - | Die “GetTime“ Funktion brauche ich nun ja wohl nicht mehr zu erklären. Die Funktion würde mir auch noch die Sekunden und die hundertstel Sekunden zurückgeben. Aber so genau wollte ich die Uhrzeit ja nicht haben. Also kann ich für diese beiden Werte eine Hilfsvariable einsetzen, deren Inhalt nicht beachtet | + | Es ist natürlich |
| - | Gut ... meine Ausgabestrings sind also erstellt und formatiert. Nun geht es darum, den User zu fragen, was er denn bitte schön eingefügt haben möchte. Wir lassen ihm ja die Wahl, ob er nur das Datum, nur die Uhrzeit oder vielleicht beides haben möchte. Für diese Auswahl zeigen wir ihm ein Menü. Ein Portfolio-Menü. Die Menüfunktion stammt auch wieder aus der PortAES-Unit und ihre Parameter und ihre Benutzung sind im zweiten Kursteil beschrieben. Nur kurz ... mit den ersten beiden Zahlenparametern lege ich die linke-obere Ecke des Menüs fest, ich positioniere es also. Wichtig ist, daß die Funktion einen Wahrheitswert zurück liefert, ob eine gültige Auswahl getroffen wurde oder nicht. Sollte der User das Menü mit Escape abgebrochen haben, dann würde die IF-Anweisung und die daraus folgende CASE-Anweisung gar nicht ausgeführt werden und es würde gar nichts passieren. Unser Programm beendet sich sang und klanglos und löst sich nach der Wiederherstellung des Bildschirms in Luft auf. Sollte aber einer der Menüpunkte angewählt werden, dann war das Starten des Programms nicht sinnlos und wir können zeigen, was in uns steckt. Bei drei Menüpunkten kann unser Rückgabewert in “auswahl“ zwischen 0 und 2 liegen. Je nachdem was nun in “auswahl“ steht, sende ich mit Hilfe der EDHO_INS Funktion den passenden String an die Textverarbeitung. | + | Der Rechenausdruck MOD (Modulo) führt eine ganzzahlige Division durch und gibt als Ergebnis den ganzzahligen Rest zurück. Bei **“1996 MOD 100“** wäre 100 also 19 mal in 1996 enthalten. **“19 mal 100 gleich 1900“** Der ganzzahlige Rest ist also 96. Und schon habe ich nur noch die letzten beiden Ziffern. Dieses Ergebnis schreibe ich mir dann wieder in die Variable **“jahr“** zurück, weil ich ja nur das haben will. Die nun folgende vierzeilige Anweisung ist natürlich nur eine Anweisung, die ich der Übersichtlichkeit halber aber in vier Zeilen verteile. Es handelt sich hierbei um eine ganz normale Stringverkettung. Zwischen den einzelnen Angaben werden noch Leerzeichen und Punkte eingebaut. Die Formatierung von einer Zahl in einen String übernimmt die Funktion “Expandieren“. Außerdem wird der String auch noch um die führende Null erweitert falls erforderlich. |
| + | |||
| + | Die **“GetTime“** Funktion brauche ich nun ja wohl nicht mehr zu erklären. Die Funktion würde mir auch noch die Sekunden und die hundertstel Sekunden zurückgeben. Aber so genau wollte ich die Uhrzeit ja nicht haben. Also kann ich für diese beiden Werte eine Hilfsvariable einsetzen, deren Inhalt nicht beachtet wird. An dieser Stelle noch ein kleiner Hinweis. Es gibt doch einen Unterschied, | ||
| + | |||
| + | Gut ... meine Ausgabestrings sind also erstellt und formatiert. Nun geht es darum, den User zu fragen, was er denn bitte schön eingefügt haben möchte. Wir lassen ihm ja die Wahl, ob er nur das Datum, nur die Uhrzeit oder vielleicht beides haben möchte. Für diese Auswahl zeigen wir ihm ein Menü. | ||
| + | |||
| + | **Ein Portfolio-Menü.** | ||
| + | |||
| + | Die Menüfunktion stammt auch wieder aus der PortAES-Unit und ihre Parameter und ihre Benutzung sind im zweiten Kursteil beschrieben. Nur kurz ... mit den ersten beiden Zahlenparametern lege ich die linke-obere Ecke des Menüs fest, ich positioniere es also. Wichtig ist, daß die Funktion einen Wahrheitswert zurück liefert, ob eine gültige Auswahl getroffen wurde oder nicht. Sollte der User das Menü mit Escape abgebrochen haben, dann würde die IF-Anweisung und die daraus folgende CASE-Anweisung gar nicht ausgeführt werden und es würde gar nichts passieren. Unser Programm beendet sich sang und klanglos und löst sich nach der Wiederherstellung des Bildschirms in Luft auf. Sollte aber einer der Menüpunkte angewählt werden, dann war das Starten des Programms nicht sinnlos und wir können zeigen, was in uns steckt. Bei drei Menüpunkten kann unser Rückgabewert in **“auswahl“** zwischen 0 und 2 liegen. Je nachdem was nun in “auswahl“ steht, sende ich mit Hilfe der EDHO_INS Funktion den passenden String an die Textverarbeitung. | ||
| Zum Schluß muß noch der Bildschirm restauriert werden, um die Hinterlassenschaften unseres Menüs zu beseitigen und unser Hook-File wird beendet. Sobald die Textverarbeitung wieder die Kontrolle hat, erscheint wie von Zauberhand unser String. | Zum Schluß muß noch der Bildschirm restauriert werden, um die Hinterlassenschaften unseres Menüs zu beseitigen und unser Hook-File wird beendet. Sobald die Textverarbeitung wieder die Kontrolle hat, erscheint wie von Zauberhand unser String. | ||
| Zeile 349: | Zeile 395: | ||
| Noch etwas zur Benutzung dieses Hook-Files. Ab einer bestimmten Größe, die das Programm im geladenen Zustand im Speicher einnimmt, kann es sein, daß das Hook-File nicht mehr geladen wird. Das tritt meist dann auf, wenn noch ein anderes Programm im Hintergrund schlummert. Viele benutzen vielleicht den File-Manager FM.EXE. Starte ich die Textverarbeitung über dieses Programm, dann schafft es die Textverarbeitung nicht mehr das Hook-File nachzuladen. Es ist also meistens besser, wenn man die Textverarbeitung von der DOS-Oberfläche startet. Damit sind diese Schwierigkeiten weitgehend behoben. | Noch etwas zur Benutzung dieses Hook-Files. Ab einer bestimmten Größe, die das Programm im geladenen Zustand im Speicher einnimmt, kann es sein, daß das Hook-File nicht mehr geladen wird. Das tritt meist dann auf, wenn noch ein anderes Programm im Hintergrund schlummert. Viele benutzen vielleicht den File-Manager FM.EXE. Starte ich die Textverarbeitung über dieses Programm, dann schafft es die Textverarbeitung nicht mehr das Hook-File nachzuladen. Es ist also meistens besser, wenn man die Textverarbeitung von der DOS-Oberfläche startet. Damit sind diese Schwierigkeiten weitgehend behoben. | ||
| - | So, ich hoffe, daß das nicht zu schwer war. Eigentlich nicht. Man sieht, wie man mit einfachen Mitteln eine Erweiterung für die Textverarbeitung verfassen kann. Denkbar wäre jetzt zum Beispiel mit der EDHO_INS Funktion eine Art von Textbaustein-Verwaltung. Häufig gebrauchte Textinhalte können so auf Knopfdruck einfach eingefügt werden. Vielleicht hat ja der eine oder andere Geschmack daran gefunden, kleine PASCAL-Programme zu erstellen. Vielleicht haben ja auch einige Nichtprogrammierer die den Text gelesen haben, Lust bekommen, selber zu programmieren. Ich kann nur sagen, es ist ein tolles Gefühl, wenn man weiß, daß ein kleines Wunderwerk, das gerade auf dem Bildschirm zu sehen ist, von einem selbst stammt. Jeder Programmierer, | + | So, ich hoffe, daß das nicht zu schwer war. Eigentlich nicht. Man sieht, wie man mit einfachen Mitteln eine Erweiterung für die Textverarbeitung verfassen kann. Denkbar wäre jetzt zum Beispiel mit der EDHO_INS Funktion eine Art von Textbaustein-Verwaltung. Häufig gebrauchte Textinhalte können so auf Knopfdruck einfach eingefügt werden. Vielleicht hat ja der eine oder andere Geschmack daran gefunden, kleine PASCAL-Programme zu erstellen. Vielleicht haben ja auch einige Nichtprogrammierer die den Text gelesen haben, Lust bekommen, selber zu programmieren. Ich kann nur sagen, es ist ein tolles Gefühl, wenn man weiß, daß ein kleines Wunderwerk, das gerade auf dem Bildschirm zu sehen ist, von einem selbst stammt. Jeder Programmierer, |
| + | |||
| + | Falls jemand also anfangen will zu programmieren : Es gibt viele gute Bücher, um den Einstieg selber zu finden und ein Turbo PASCAL 6.0 kann ich schon auf CD gebrannt in jeder Computerabteilung der Kaufhäuser für nicht mal 40,-DM bekommen. | ||
| + | Programmierkurs für PASCAL 4 | ||
| + | |||
| + | ====== Teil 4 ====== | ||
| + | |||
| + | ==== Die Grafikprogrammierung ==== | ||
| + | |||
| + | So ... Freunde. Wir haben es nun fast geschafft, die Grundlagen der Pascalprogrammierung auf dem Portfolio zusammenzutragen. Ein Bereich fehlt aber noch. Etwas, daß eigentlich immer dazugehört und was für die meisten Leute das absolut Höchste ist. | ||
| + | |||
| + | Es geht um die Grafikprogrammierung. Ich muß zugeben, daß es mir lieber gewesen wäre, wenn ich mir dieses Thema hätte sparen können. Ich bin zwar auch fasziniert von den Möglichkeiten, | ||
| + | |||
| + | Es gibt zwei mittelgroße Probleme, die uns erst einmal beschäftigen müssen. Ziel eines jeden Kurses war es bisher, am Ende eine Art Prototyp zu haben, auf dem man dann seine eigenen Programme aufbauen kann. Meistens waren das Pascal-Units, | ||
| + | |||
| + | Leider haben wir aber noch ein viel größeres Problem. Grafikprogrammierung ist grundsätzlich immer eine zeitkritische Angelegenheit. Zwar sind für die meisten Funktionen, wie das Setzen eines Pixels auf dem Bildschirm, keine großen Programmierkünste erforderlich. Wenn Sie aber einen Kreis zeichnen wollen, müssen Sie schon Berechnungen anstellen, an welchen Koordinaten auf dem Bildschirm die Pixel gesetzt werden sollen. Hier liegt nun das große Problem, was auf einem 4,5MHz getakteten Portfolio noch viel schwerer ins Gewicht fällt als auf anderen Systemen. Der Kleine ist nun mal nicht der schnellste Rechner unter der Sonne. Wenn ich nun noch eine Hochsprache wie Pascal verwende, die mit der Anzahl der Befehle, die für eine Funktion ausgeführt werden müssen, sehr hoch ist, bin ich in Bezug auf die Grafik schon auf der Verliererseite. Die einzige Möglichkeit, | ||
| + | |||
| + | Damit wird klar, daß die Grafikunit, die zu diesem Teil des Kurses gehört, größten Teils in Maschinensprache verfaßt ist. Maschinensprache ist aber ja eigentlich nicht unser Thema. Deshalb wird es die meisten von euch nicht interessieren, | ||
| + | |||
| + | Ich persönlich bin auch kein großer Assembler Programmierer, | ||
| + | |||
| + | Folgende Befehle sind in der Unit implementiert : | ||
| + | ^ ^ ^ | ||
| + | | Arc | Bar | | ||
| + | | Bar3D | Box | | ||
| + | | Circle | ClearDevice | | ||
| + | | CloseGraph | DrawPoly | | ||
| + | | Ellipse | FillEllipse | | ||
| + | | FillPoly | FloodFill | | ||
| + | | GetColor | GetPixel | | ||
| + | | InitGraph | Line | | ||
| + | | LineRel | LineTo | | ||
| + | | MoveRel | MoveTo | | ||
| + | | OutTextXY | PieSlice | | ||
| + | | PutPixwl | | ||
| + | | Sector | ||
| + | | SetFillStyle | SetTextStyle | | ||
| + | | TextPixel | | | ||
| + | |||
| + | Welche Parameter diese Routinen benötigen, und wie ich sie verwende, kann ich aufgrund der Kompatibilität in der Pascal Hilfe nachlesen. Bei den zwei folgenden Befehlen sind jedoch Abweichungen zu denen der Original Unit notwendig geworden : | ||
| + | |||
| + | Bei OutTextXY können nur ASCII-Codes bis einschließlich 127 ausgegeben werden. | ||
| + | |||
| + | Und der Befehl SetTextStyle besitzt eine etwas andere Aufrufsyntax: | ||
| + | |||
| + | |||
| + | ^ Nummer ^ Erklärung ^ | ||
| + | | 1 | Zeichen nicht gekippt, keine Kursivschrift | | ||
| + | | 2 | Zeichen nicht gekippt, Kursivschrift nach links | | ||
| + | | 3 | Zeichen nicht gekippt, Kursivschrift nach rechts | | ||
| + | | 4 | Zeichen um 180° gekippt, keine Kursivschrift | | ||
| + | | 5 | Zeichen um 180° gekippt, Kursivschrift nach links | | ||
| + | | 6 | Zeichen um 180° gekippt, Kursivschrift nach rechts | | ||
| + | | 7 | Zeichen um 90° gekippt, keine Kursivschrift | | ||
| + | | 8 | Zeichen um 90° gekippt, Kursivschrift nach links | | ||
| + | | 9 | Zeichen um 90° gekippt, Kursivschrift nach rechts | | ||
| + | | 10 | Zeichen um 270° gekippt, keine Kursivschrift | | ||
| + | | 11 | Zeichen um 270° gekippt, Kursivschrift nach links | | ||
| + | | 12 | Zeichen um 270° gekippt, Kursivschrift nach rechts | | ||
| + | Statt eines weiteren Parameters bei der Original-Unit, | ||
| + | |||
| + | Aus Kompatibilitätsgründen wurde die Routine InitGraph original übernommen. Das bedeutet, daß ein Pfadname für einen Grafiktreiber angegeben werden muß. Da die Portfolio-Unit aber gar keinen Treiber benötigt, können Sie eingeben, was Sie wollen. | ||
| + | |||
| + | Mit den oben genannten Grafikbefehlen sollten die meisten Leute schon etwas anfangen können. Einige Befehle, um zum Beispiel Grafiken im PGC Format einzulesen oder abzuspeichern, | ||
| + | |||
| + | Mit dieser Unit haben wir nun Grundlagen für die Grafikprogrammierung auf dem Portfolio geschaffen, die für jemanden ausreichen sollten, der keine Lust und Zeit hat, sich mit der langwierigen Assembler-Umsetzung auseinanderzusetzen. Den Quelltext der Unit abzudrucken ist leider nicht möglich, da ihr Umfang so groß ist, daß es den Rahmen der PofoInfo bei weitem sprengen würde. Das bedeutet nicht, daß man die Unit nicht bekommen kann. Denn der Pascal Kurs ist zusammen mit den Units, die in diesem Rahmen entstanden sind, beim PCD erhältlich. | ||
| + | |||
| + | Auch wenn dieser Teil des Kurses qualitativ und quantitativ nicht mit den vorherigen Teilen mithalten kann, soll er doch den Abschluß des Kurses bilden. Abschluß des Kurses bedeutet, daß mit diesem vierten Teil sämtliche Grundlagen besprochen sein sollten. Das heißt natürlich nicht, daß dies das Ende von Artikeln zum Thema Pascal Progammierung auf dem Portfolio ist, jedoch sollte nun jeder in der Lage sein, diese Grundkenntnisse in eigene Programme umzusetzen. Spezielle Probleme und Sonderfunktionen könnten Inhalt weiterer Artikel sein. | ||
| + | An dieser Stelle deshalb noch einmal ein letzter Aufruf an alle, die glauben ,daß wichtige Bereiche nicht besprochen wurden. Eine Email genügt und ich werde mich mit der gewünschten Thematik auseinandersetzen (es sei denn, es betrifft den Bereich Grafik...: | ||
| - | Wer Fragen und Anregungen zu diesem Artikel hat, kann sich wie immer gerne an mich wenden. Die Adresse und Telefonnummer stehen im Anhang. Ich habe auch ein Archiv in der Pofo-Mailbox, | ||
| ===== Anhang ===== | ===== Anhang ===== | ||
| Zeile 362: | Zeile 476: | ||
| * DIP | * DIP | ||
| * Diverse Texte aus diversen Mailboxen | * Diverse Texte aus diversen Mailboxen | ||
| + | |||
| + | ===== Download ===== | ||
| + | |||
| + | [[pwfd> | ||
| + | [[pwfd> | ||
| + | [[pwfd> | ||
| + | [[pwfd> | ||
software/diy/pascal/pascalk.1138390955.txt.gz · Zuletzt geändert: (Externe Bearbeitung)
