Assembler-Beispiel 4 |
Ausgabe "Hello World" 10 mal auf den Bildschirm - verzögert
Nehmen wir einmal an, wir hätten ein großes Programm zu schreiben und in diesem
brauchen wir an mehreren Stellen Verzögerungen. Dann ist es sicher sinnvoll nicht
jedesmal diese Schleifen zu schreiben, sondern sich einfach einen Programmblock zu
schaffen, der die Verzögerung realisiert und der von allen notwendigen Stellen aus
genutzt werden kann.
Das genau leistet die Programmstruktur Unterprogramm!
Das Bild links soll das Prinzip verdeutlichen. Im Hauptprogramm (das aufrufende Programm) wird an der Adresse i der Befehl zum Start des Unterprogramms (gerufenes Programm) ab Adresse j gefunden. In gleicher Weise würde auch von Adresse k das Unterprogramm ab Adresse j gestartet werden. Die Bearbeitung wird somit ab Adresse j fortgesetzt. Irgendwann wird das Ende des Unterprogramms, die Adresse je erreicht. Nun gibt es ein Problem - woher weiß das Unterprogramm an welcher Stelle das Hauptprogramm fortgesetzt werden soll, an Adresse i+1 oder k+1? |
Das Problem läßt sich relativ einfach mit dem Stack lösen!
Der Befehl an Adresse i bzw. k ist bekannt und man kann sofort die Adresse des
nächsten Befehls berechnen (i+1 bzw k+1). Je nach Art des Unterprogramms wird nun
diese Adress
durch den Befehl in den Stack gespeichert und der Stack auch entsprechend verändert,
bzw. es wird auch noch CS gespeichert (es werden 2 oder 4 Byte in den Stack
gespeichert). Der Befehlszähler (IP) wird mit dem Wert, der im Befehl steht geladen. Der Aufruf eines weiteren UP aus einem UP heraus ist so nun auch ohne weiteres möglich. |
Noch ein paar grundsetzliche Bemerkungen zur Unterprogramm-Nutzung. Unterprogramme können gut wiederkehrende Aufgaben erledigen. Man sollte dabei jedoch bedenken, dass die ordnungsgemäße Weiterarbeit des Hauptprogramms aber nur dann funktioniert, wenn auch alle Register im Prozessor wieder die alten Werte haben - das organisiert das Unterprogramm nicht automatisch - dafür ist der Programmierer selbst zuständig!
Und so kann es funktionieren. |
Soviel zur Theorie der Unterprogramme
Nun zur praktischen Realisierung mit dem 8086-Assembler. Im Assembler werden sie
allgemein als "Prozeduren (Procedure)" bezeichnet und es gibt zwei Typen:
Im Bild links wird deutlich, dass mit dem Typ FAR nun die Möglichkeit besteht, den gesamten Speicher zu füllen. Es müssen nun aber zwei völlig getrennte Programme (oder auch mehr) bearbeitet und übersetzt werden, erst der Linker schafft die Verbindung. Zur fehlerfreien Arbeit des Assemblers muss ihm irgendwie mitgeteilt werden, dass das UP später folgt, die Kommunikation über die Segmentgrenzen hinaus muss organisiert werden. Das fällt natürlich beim Typ NEAR weg. Wir wollen mit dem NEAR-UP beginnen. Es bedarf nun natürlich einer besonderen Bezeichnung, damit der Assembler den Bereich des UP auch findet:
Der Aufruf des UPs aus dem dem HP erfolgt mit dem CALL-Befehl:
|
Und so sieht das Programm dazu aus!
Wie schon bekannt werden zuerst die Assembler-Direktiven ".MODEL SMALL",
".STACK 256", das ".DATA"-Segment mit dem Text
und dann das ".CODE"-Sement für HP und UP vereinbart.
Dann folgt mit der Marke "start:" das HP. |
|
Nun zur 2.Variante der Unterprogramme, dem
Links ist das Programm dargestellt und eigentlich ist der Unterschied zum
NEAR-Unterprogramm gar nicht so groß. Es beginnt wieder mit
Der Aufruf im Hauptprogramm ist mit:
Der entscheidende Unterschied ist, dass sowohl das Haupt- als auch das Unterprogramm
völlig eigenständige Programme sind, eigene Modellvereinbarung, eigene Segmente! Richtig ist nun, dass wir mit zwei Programmen arbeiten müssen, diese müssen jeweils editiert und assembliert werden. Danach muss vom Hauptprogramm (Objektdatei) aus die Objektdatei vom Unterprogramm dazu gelinkt werden - hier kommt nun unsere Bedienoberfläche wieder ins Spiel! |
Hier wurde in bekannter Weise das 1.Programm "H10UE41.asm", das Hauptprogramm,
vereinbart und bearbeitet. Hinter Quelle1 steht (Basis), das bedeutet, dass zu diesem Programm andere dazu gelinkt werden. Das Programm wurde editiert und mit <a> assembliert. Damit entsteht die Objektdatei "H10UE41.obj". Die Datei "H10UE41.asm" wird gleichfalls dem System eigenen Editor zugeordnet. |
Im nächsten Schritt wird nun mit <z> die zweite Quelle "H10UE42.asm" festgelegt und mit <2> übersetzt. Es entsteht als Link-Datei die Objektdatei "H10UE42.obj" |
Nun kann der Linker mit <l> gestartet werden. Intern kommt folgender Befehl zustande: TLINK H10UE41.obj H10UE42.obj Das Ergebnis wird sichtbar, da als Start-Datei nun "H10UE41.exe" eingtragen wird. Mit <r> kann nun das Programm gestartet werden.
Mit <d> könnte man für ein anderes Projekt die gesamte Liste aller
zu linkenden Dateien festlegen (sie wird durch Leerzeichen getrennt und die erste
Datei ist die Basis). |
. . . |
Links ist der Hex-Dump vom erzeugten Maschinen-Programm zu sehen. Das Programm (h10ue41) wird von Adresse 00200h bis 0021Hh in den Speicher geladen, das UP (h10ue42) von 00220h bis 0022Eh und die Daten (Text) von 00230h bis 0023Eh. Es funktioniert genau wie das Programm im Beispiel 3! |
Im Beispiel 5 sollen kurz Makros, eine weitere Leistung des Assemblers, beschrieben werden.
zurück zur Start-Seite (Beispiele) / weiter Beispiel 5 | ||
zurück zur Start-Seite |