![]() |
![]() |
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 |
CP/M 68K Programmierkurs - Nützliche UnterprogrammeEs ist schon erstaunlich, wie viele Fähigkeiten bei reiner Assembler-Programmierung selbst umgesetzt werden müssen. Kein Vergleich zu Hochsprachen wie C, PASCAL, BASIC oder selbst dem Grundprogramm im ROM.An dieser Stelle muss ich also ein Kapitel einschieben, um einige nützliche Unterprogramme vorzustellen, die wir in den folgenden Beiträgen benötigen werden. Die Beispiele erheben nicht den Anspruch, die kürzeste oder schnellste Umsetzung darzustellen, sie sollen hingegen so leicht verständlich wie möglich sein. Die Unterprogramme sind so ausgerichtet, dass Inhalte der benutzten Register nach Abschluss wiederhergestellt werden, außer natürlich bei erwarteten Ergebnissen einer Funktion. Themen dieses Kapitels
Hexadezimale ZahlenausgabeMit der Routine PRINT8X kann ein hexadezimaler 32-Bit Wert in Register D0 auf der Konsole ausgegeben werden.Mit nur einer kleinen Änderung bei der Ausgabe in PRTNIB1 kann man die Routine so abändern, dass die Zahl nicht direkt auf der Konsole ausgegeben wird, sondern an einer bestimmten Stelle in einem Textpuffer abgelegt wird. Im Anschluss kann der Inhalt des Puffers mit der BDOS Funktion Print String (Funktionsnummer 9) gemeinsam mit Text ausgegeben werden. Die relevanten Änderungen sind fettgedruckt.CONOUT EQU 2 * BDOS Funktionsnummer Konsolausgabe START: * Zum Test des Unterprogrammes MOVE.L #$12345678, D0 * Testwert BSR PRINT8X * ausgeben RTS * Zurück zu CP/M * Ausgabe des Inhaltes von D0.L als 8 Hexadezimalziffern PRINT8X: MOVEM.L D0-D3, -(A7) * Registerinhalte auf Stack sichern MOVE.L D0, D2 * auszugebenden Wert kopieren MOVE #7, D3 * Anzahl der auszugebenden Stellen PRT8X1: * Schleife über 8 Stellen ROL.L #4, D2 * Mit MSB beginnen BSR PRTNIB * Eine Stelle ausgeben DBRA D3, PRT8X1 * Solange bis alle Stellen ausgegeben MOVEM.L (A7)+, D0-D3 * Registerinhalte wiederherstellen RTS * Rücksprung zum aufrufenden Programm PRTNIB: MOVE.B D2, D1 * Stelle für BDOS-Aufruf kopieren AND.B #$0F, D1 * untere 4 Bit maskieren CMP.B #$0A, D1 * Kleiner als 10 ? BLT PRTNIB1 * Ja, ausgeben ADD.B #7, D1 * ASCII Korrektur für A bis F PRTNIB1: ADD.B #’0’, D1 * ASCII Versatz addieren MOVE #CONOUT, D0 * BDOS Funktion TRAP #2 * CONOUT aufrufen RTS * Fertig Mit wenig Aufwand lässt sich die Ausgabe von Zahlen erweitern, so dass auch 16 Bit und 8 Bit Werte ausgegeben werden können. Dabei wird der größte Teil der Routine PRINT8X wiederverwendet.PRINTSTR EQU 9 * BDOS Funktionsnummer Print String TEXT * Start des Textsegments START: MOVE.L #$12345678, D0 * Testwert MOVE.L #VARIABLE, A0 * Adresse des Platzhalters BSR PRINT8X * in Platzhalter schreiben MOVE #PRINTSTR, D0 * Funktionsnummer BDOS MOVE.L #BUFFER, D1 * Adresse des auszugebenden Strings TRAP #2 * und aufrufen RTS * Zurück zu CP/M * Ausgabe des Inhaltes von D0.L als 8 Hexadezimalziffern PRINT8X: MOVEM.L D0-D3, -(A7) * Registerinhalte auf Stack sichern MOVE.L D0, D2 * auszugebenden Wert kopieren MOVE #7, D3 * Anzahl der auszugebenden Stellen PRT8X1: * Schleife über 8 Stellen ROL.L #4, D2 * Mit MSB beginnen BSR PRTNIB * Eine Stelle ausgeben DBRA D3, PRT8X1 * Solange bis alle Stellen ausgegeben MOVEM.L (A7)+, D0-D3 * Registerinhalte wiederherstellen RTS * Rücksprung zum aufrufenden Programm PRTNIB: MOVE.B D2, D1 * Stelle für BDOS-Aufruf kopieren AND.B #$0F, D1 * untere 4 Bit maskieren CMP.B #$0A, D1 * Kleiner als 10 ? BLT PRTNIB1 * Ja, ausgeben ADD.B #7, D1 * ASCII Korrektur für A bis F PRTNIB1: ADD.B #’0’, D1 * ASCII Versatz addieren MOVE.B D1,(A0)+ * Ablegen und nächste Adresse RTS * Fertig DATA * Start des Datensegements BUFFER: DC.B 'Inhalt des Registers D0: ' VARIABLE: DC.B '-------- $' END * Ausgabe des Inhaltes von D0.B als 2 Hexadezimalziffern PRINT2X: MOVEM.L D0-D3, -(A7) * Registerinhalte auf Stack sichern MOVE.L D0, D2 * auszugebenden Wert kopieren ROR.L #8, D2 * Byte nach vorne bringen MOVE #1, D3 * Anzahl der auszugebenden Stellen BRA PRT8X1 * dort einspringen * Ausgabe des Inhaltes von D0.W als 4 Hexadezimalziffern PRINT4X: MOVEM.L D0-D3, -(A7) * Registerinhalte auf Stack sichern MOVE.L D0, D2 * auszugebenden Wert kopieren SWAP D2 * Wort nach vorne bringen MOVE #3, D3 * Anzahl der auszugebenden Stellen BRA PRT8X1 * dort einspringen Dezimale ZahlenausgabeDie Ausgabe von Dezimalzahlen ist dagegen deutlich komplizierter, da alle Mikroprozessoren zunächst einmal im Binärsystem arbeiten. Die folgende Routine teilt die auszugebende Zahl jeweils durch 10, um dann den Rest bei den Divisionen auszugeben. Eine zusätzliche Schwierigkeit ist, dass der 68000 nicht direkt 32 Bit Divisionen unterstützt. Dazu ist eine weitere Routine notwendig.Die Routine gibt die Zahl im Register D0 in den Pufferspeicher ab der Adresse in A0 aus. Die Belegung der Register ist
Innerhalb von PRINTD wird die Routine DIVU32 aufgerufen, welche die erforderliche 32 Bit Division ausführt. Die Belegung der Register istPRINTD: TST.L D0 * Wenn Zahl 0 ist BEQ PRTD0 * dann sofort ausgeben und Ende MOVEM.L D1/D6/D7, -(A7) * REGS sichern MOVE.L #10, D1 * Konstante 10 MOVE.L #9, D7 * 10 Schleifendurchgänge CLR.B D6 * Flag löschen PRTD1: BSR DIVU32 * Division, Rest im Bereich 0-9 MOVE.B D2, -(A7) * Rest auf Stack DBRA D7, PRTD1 * wiederholen MOVE.L #9, D7 * 10 Schleifendurchgänge PRTD3: MOVE.B (A7)+, D1 * umgekehrte Reihenfolge BEQ PRTD4 * bei 0 Flag testen BRA PRTDX * Ziffer ausgeben PRTD2: DBRA D7, PRTD3 * Bis alle Stellen fertig MOVEM.L (A7)+, D1/D6/D7 * REGS wiederherstellen RTS * zurück zum Aufrufenden PRTD4: TST.B D6 * Flag testen BEQ PRTD2 * nicht ausgeben, solange Flag=0 PRTDX: ADDQ.B #1, D6 * Flag setzen ADD.B #$30, D1 * ASCII Korrektur MOVE.B D1, (A0)+ * Ziffer in Buffer BRA PRTD2 * weiter PRTD0: MOVE.B #$30, (A0)+ * '0' in Buffer RTS * sofort zurück
DIVU32: CLR.L D2 * Temp Register löschen MOVE.W D0, D2 * D0 LSW sichern CLR.W D0 * D0 LSW löschen SWAP D0 * MSW nach LSW DIVU D1, D0 * MSW zuerst dividieren MOVE.L D0, D3 * Ergebnis kopieren CLR.W D3 * Rest in D3 MSW SWAP D0 * Ergebnis nach MSW CLR.W D0 * Rest entfernen ADD.L D3, D2 * Rest zu LSW addieren DIVU D1, D2 * LSW dividieren MOVE.W D2, D0 * Ergebnis zusammensetzen SWAP D2 * Rest nach D2 LSW RTS HinweiseDie hier vorgestellten Unterprogramme werden in den kommenden Beispielen genutzt, aber dort nicht mehr erneut aufgelistet. Das bedeutet, dass die kommenden Beispiele nicht ohne die Ergänzung durch manche der hier wiedergegebenen Unterprogramme ablauffähig sind.Gegen Ende der Artikelserie werden wir die gesammelten Unterprogramme zu einem Modul zusammensetzen und als getrennte Objektdatei ablegen, die mit Hilfe des Linkers einfach zu einem Gesamtprogramm zusammengesetzt werden können.
|