![]() |
![]() |
Startseite News   NDR-NKC ![]() ![]() ![]() NKC Emulator Z80 Section ![]() ![]() ![]() 68000 Section ![]() ![]() ![]() ![]() ![]() 8088 Section ![]() ![]() Bussysteme Stromversorgung Input / Output Grafikkarten Speicherkarten Massenspeicher Weitere Baugruppen Projekte Dokumentation Datenblätter Glossar Portraits Links Impressum |
Assembler-BibliothekenAnstelle von kleinen Routinen, die man mit dem Inline-Assembler umsetzt, lassen sich Bibliotheken für C-Programme auch direkt in Assembler codieren. Der Assembler-Quelltext wird getrennt vom C-Quelltext eingegeben. Mit dem Assembler AS68 wird aus diesem Quelltext eine Objektdatei erzeugt, die nach dem Übersetzen des C-Quelltextes mittels des Linkers zu dem finalen Programmcode zusammengesetzt wird. Das Verfahren hat den Vorteil, dass einmal getestete Routinen bereits als fertig übersetzte Objekt-Datei vorliegen und nicht immer wieder durch den C-Compiler übersetzt werden müssen. Falls Routinen in mehreren Programmen verwendet werden, sind Änderungen einfacher, da der Assembler-Code an einer einzigen Stelle korrigiert oder erweitert werden kann. Danach müssen nur noch alle Programme, welche die Bibliothek verwenden, neu übersetzt werden. In diesem Beitrag wird der erste Schritt zum Aufbau einer Sammlung von Funktionen und Methoden zur direkten Ansteuerung der Hardware der NKC gezeigt. Der Quelltext dieser Bibliothek wird unter dem Namen NKC.S gespeichert und kann später beliebig erweitert werden. Erstellen einer BibliothekAls Beispiel wird der Zugriff auf die Baugruppe IOE gezeigt. Mit der Bibliothek sollen beliebige Portadressen gelesen und beschrieben werden können. Dazu sind die zwei folgenden Funktionen umzusetzen, mit denen man theoretisch die gesamte Hardware des NKC steuern könnte.
Übergebene Variablen und Konstanten werden vom C-Compiler auf dem Stack abgelegt, bevor die Assembler-Routine aufgerufen wird. Der erste übergebene Wert liegt bei 4(A7), da der Aufruf der Routine zusätzlich die Rücksprungadresse auf den Stack legt. Es gibt keine Typ-Prüfung der übergebenen Werte, weder der C-Compiler noch der Assembler sind in der Lage, den Datentyp der Parameter oder des Rückgabewertes zu überprüfen. Die Namen der in Assembler definierten Methoden und Funktionen müssen mit einem Unterstrich beginnen und sich in den ersten sieben Buchstaben unterscheiden. Im Gegensatz zum Inline-Assembler müssen alle Routinen mit RTS abgeschlossen werden. TEXT ; Codeablage im Textsegment _setio: MOVE 4(A7),D1 ; 1. Parameter lesen OR.L #$FFFFFF00,D1 ; auf longword erweitern MOVE.L D1,A0 : in Adressregister kopieren MOVE 6(A7),D0 : 2. Parameter lesen MOVE.B D0,(A0) ; auf Portadresse schreiben RTS ; Rücksprungn ac C _getio: MOVE 4(A7),D1 ; Parameter lessen OR.L #$FFFFFF00,D1 : auf longword erweitern MOVE.L D1,A0 ; in Adressregister kopieren MOVE.B (A0),D0 ; Portadresse lesen RTS END Exportieren der öffentlichen SymboleDamit der Linker die Startadressen der bereitgestellten Funktionen erkennen kann, müssen diese exportiert werden. Dazu werden die Namen der zu exportierenden Funktionen direkt am Anfang des Quelltextes mittels GLOBL aufgelistet. GLOBL _setio GLOBL _getio Die Assembler-Direktive GLOBL darf an beliebiger Stelle im Assembler-Quelltext vorkommen, es empfiehlt sich jedoch, die Namen der zu exportierenden Funktionen am Anfang des Quelltextes aufzulisten, damit man sofort erkennt, welche Funktionen öffentlich zugänglich sind. Erstellen der Objekt-DateiJetzt kann aus dem Assembler-Quelltext die Objekt-Datei mittels des Assemblers AS68 erstellt werden. Dazu verwendet man den folgenden Aufruf des Assemblers. AS68 -L -U NKC.S Nach erfolgreicher Ausführung des Assemblers sollte auf der Diskette eine Datei mit dem Namen NKC.O vorliegen. Mit dem Programm NM68 kann man kontrollieren, ob die Funktionen tatsächlich exportiert werden. NM68 NKC.O Verwenden der RoutinenInnerhalb des C-Quelltextes kann man die exportierten Funktionen und Methoden aus der Objekt-Datei der Bibliothek ganz normal benutzen. Das Testprogramm wird unter dem Namen TEST.C gespeichert. #inclide "stdio.h" main() { setio(0x30, 0x55); setio(0x31, 'a'); printf("Port 0x30 = %02xn ", getio(0x30)); printf("Port 0x31 = %02xn ", getio(0x31)); } Die Funktion setio erwartet zwei Parameter vom Typ int. Da keine Typ-Prüfung stattfindet, ist die zweite Zeile dennoch möglich, da char - Werte ebenfalls als 2 Bytes auf dem Stack abgelegt werden, bevor die Funktion aufgerufen wird. Übersetzen des C-ProgrammsDer C-Quelltext kann wie gewohnt mittels der Submit-Dateien C.SUB oder CF.SUB übersetzt werden. Dabei ist darauf zu achten, dass die oben erstellte Objektdatei zusätzlich anzugeben ist. C TEST NKC Die Datei TEST.C wird unter Verwendung der Bibliothek NKC.O in das Programm TEST.68K bzw. TEST.REL übersetzt. Abschließend sollte natürlich die korrekte Funktion getestet werden. Zugriff auf lokale VariablenBei jeder C-Routine, in der lokale Variablen definiert werden, wird ein Stack-Frame aufgebaut, der den Zugriff auf die Inhalte der Variablen auch aus dem Assembler-Code erlaubt. Als Pointer wird das Adress-Register A6 genutzt. #inclide "stdio.h" main() { int addr = 0x30; int value = 0x55; int dummy = 0; test(addr, value); printf("dummy = %04xn", dummy); } Bei der Übersetzung dieses Codes werden die Variablen addr, value und dummy im durch A6 definierten Stack-Frame abgelegt. Die Reihenfolge entspricht der Reihenfolge der Definition im C-Programm. Daher ist es im Assembler-Code möglich, auf jede lokale Variable zuzugreifen, auch wenn diese nicht als Parameter an die Routine übergeben wurde. GLOBL _test _test: MOVE -2(A6),D1 ; addr nach D1.W holen MOVE -4(A6),D2 ; value nach D2.W holen MOVE #$77,-6(A6). ; 0x77 in Variable dummy ablegen RTS END Bei der Ausführung des obigen C-Programms wird der Wert der Variable dummy von 0x0000 auf 0x0077 geändert. |