3 Programmierung Assembler Aufgaben: Übersetzt mnemotechnische Abkürzungen (z.b. move, add...) in die Maschinenbefehle des Prozessors Ermöglicht die Vergabe von Namen für Speicheradressen (Label) Berechnet relative Adressedistanz bei Branch-Befehlen Führt Direktiven aus (Kommandos direkt an den Assembler), die z. B. die Verwendung von Konstanten ode die Belegung von Speicherplätzen ermöglichen
Die wichtigsten Direktiven sind: ; Kommentare ein Kommentar beginnt mit einem Strichpunkt. Ein Kommentar kann am Beginn einer Zeile stehen oder nach einem Befehl ORG origin Setzt den Programmcounter auf einen Startwert und legt das folgende Programm ab dem Startwert in den Speicher Bsp: ORG $400 EQU equate definiert einen Namen unter dem ein Wert im Programm angesprochen werden kann. Entspricht der #define Anweisung in C. Die damit verwendeten Namen sollten in Großbuchstaben geschrieben sein. Bsp: LF EQU $0A ; Line Feed DC define constant Damit wird ein konstanter Wert im Speicher abgelegt. Durch ein Suffix.B.W oder.l kann die Datenbreite der Konstanten angegeben werden. Beispiel: DC.B 1, 2, 3, 4, 5, 6 DC.B Text, $0D, $0A, 0 DC.W 1296 DC.L $12345678 DS define storage DS reserviert Speicherplatz, der reservierte Platz wird mit 0 vorbelegt. Die Anzahl der reservierten Datenworte im Speicher wird direkt nach DS angegeben, die Breite eines Datenwortes wird mit.b,.w und.l festgelegt. Beispiel: DS.B 256 (reserviert 256 Byte) DS.W 1 (reserviert 1 Wort = 2 Byte) DS.L 4 (reserviert 4 Langworte = 16 Byte) END Ende des Assemblerlistings
Assemblerprogramm 1 ; Es werden alle Zahlen von einem Startwert bis zu einem Endwert addiert 2 org $1 ; Startadresse des Programms 3 STEP equ 1 ; Schrittweite wird als Konstante vereinbart 4 5 clr D0 ; Register D0 wird gelöscht 6 move FIRST,D1 ; Startwert ins Register D1 7 SCH add D1, D0 ; D1 wird zu D0 addiert, ; Ergebnis in D0 8 add #STEP,D1 ; D1 wird um Schrittweite inkrementiert 9 cmp LAST, D1 ; Vergleich von D1 mit dem Endwert 10 ble SCH ; Verzweigung zu SCH, falls Endwert ; noch nicht erreicht ist 11 move D0, SU ; Ergebnis wird in den Speicher kopiert 12 stop #0 13 14 FIRST dc.w 1 ; Speicherplatz für Startwert 15 LAST dc.w 100 ; und Endwert 16 SU ds.w 1 ; Speicherplatz für Ergebnis 17 end ; Ende des Programms
Maschinencode im Speicher in hexadezimaler Schreibweise Zeile Adresse OpCode Operandenadresse 5 1 4240 6 10002 3239 0001 0020 7 10008 D041 8 1000A D27C 0001 9 1000E B279 0001 0022 10 10014 6F00 FFF2 11 10018 33C1 0001 0024 12 1001E 4E72 13 14 10020 0001 15 10022 0064 16 10024
Der IDE68K Simulator von Peter J. Fondse Download von http://home.hetnet.nl/~pj.fondse/ide68k/
Einige Befehle (Auszug aus dem Hilfesystem des Simulators IDE68K) ADD Syntax: ADD <ea>, Dn ADD Dn,<ea> Size: byte, word, long Operation: destination := destination + source Description: Add the source operand to the destination operand, and store the result In the destination location. The size of the operation may be specified to be byte, word, or long. The mode of the instruction indicates which operand is the source and which is the destination as well as the operand size. Condition Codes: N Z V C X * * * * * Weitere Befehle werden anhand der Beispiele bm_einf_1-4 erläutert. ADD, SUB, MOVE, BRA, DBRA, CMP, Bcc, DIVU, SWAP,
Integrierte Funktionen des Simulators (System Calls) Der Simulator stellt eine Reihe von integrierten Funktionen zur Ein/Ausgabe von Zeichen und für Dateioperationen zur Verfügung. Solche Funktionen müssten bei einem realen System in einem Betriebssystemkern implementiert sein. Diese Funktionen werden über einen Trap erreicht, verwendet wird hier Trap #15. Welche Funktion ausgeführt werden soll wird durch einen festen Zahlenwert, der direkt nach dem Trap-Befehl im Speicher steht, angegeben. Einige Beispiele: Function 1 - PUTCH. Der Charakter in D0 wird auf dem Bildschirm ausgegeben Example. MOVE.B #'A',D0 TRAP #15 DC.W 1 Function 2 - GETCHE. Diese Funktion wartet auf einen Tastendruck und gibt den Charakter auf Bildschirm aus. Der Charakter wird in D0 zurückgegeben. Example. TRAP #15 DC.W 2 dem Function 7 PRTSTR. Register A0 muss auf einen mit NULL terminierten String zeigen. Der String wird auf dem Bildschirm ausgegeben. Im Beispiel wird Hello ausgegeben. Example. LEA STR,A0 TRAP #15 DC.W 7.. STR DC.B Hello,0 Die Beschreibung der weiteren Funktionen können aus dem Hilfesystem des Simulators entnommen werden.
Programmieraufgaben Blockmove: Erweitern Sie das in der Vorlesung vorgestellte Programm Blockmove so, dass auch Überlappungen der beiden Blockbereiche berücksichtigt werden. (siehe Vorlage blockmove0.asm) Search_Min: In einer gegebenen Zahlenreihe soll die kleinste Zahl ermittelt werden (siehe Vorlage min0.asm). Pattern_Block: ein Speicherblock soll mit dem Bitmuster $AA belegt werden. Gegeben werden die Startadresse (gerade) und die Anzahl Blocklänge in Bytes. Um das Beschreiben zu beschleunigen, werden statt Bytes Langworte geschrieben. Die restlichen 1-3 Byte werden byteweise beschrieben. (siehe Vorlage pattern0.asm). Hinweis: DIVU z.b. DIVU #4, D0 ; D0/4 => D0, Ergebnis in Lowword, Rest im Highword SWAP vertauscht Highword mit Lowword Dez_to_ASCII: Schreiben Sie ein Programm, das eine positive Integerzahl in eines ASCII-String umwandelt. Wichtige Befehle dabei sind DIVU und SWAP (siehe Vorlage dez2ascii0.asm). Hinweis: Zahl durch 10 dividieren Rest in einem Puffer speichern Wiederholen, bis Ergebnis = 0 Puffer in umgekehrter Reihenfolge ausgeben Aus Zahl ASCII-Ziffer erzeugen durch add.b #$30,D0 Der ASCII-String kann anschließend mit Systemcall 7 am Bildschirm ausgegeben werden.
Unterprogramm Ein Teilproblem wird entweder zur mehrmaligen Verwendung oder zur Programmstrukturierung als Unterprogramm codiert. Ein Unterprogramm wird von einem übergeordneten Programm (Hauptprogramm) aufgerufen. Nach Ablauf des Programms wird das aufrufende Programm fortgesetzt. Unterschied zwischen Verzweigung und Unterprogramm LB1 LB2 JMP LB2 STOP UP JSR UP JSR UP STOP Der Rücksprung aus dem Unterprogramm führt auf unterschiedliche Adressen, d.h. die Rücksprungadresse ist nicht wie bei der Verzweigung fest im Programm codiert, sondern wird erst beim Unterprogrammaufruf ermittelt und auf dem Stack abgespeichert JMP LB1 RTS
Befehle : JSR (Jump to Subroutine) BSR (Branch to Subroutine) absoluter Sprung zum Unterprogramm, Rückkehradresse wird auf dem Stack abgelegt relativer Sprung zum Unterprogramm, Rückkehradresse wird auf dem Stack abgelegt RTS (Return from Subroutine) Rückkehr aus dem Unterprogramm, Rückkehradresse wird vom Stack gelesen Im Befehlsatz des 68000 Prozessors gibt es (leider) keine bedingten Unterprogrammsprünge.
Stack (Stapel, Kellerspeicher) ein besonderer Speicherbereich innerhalb des normalen Arbeitsspeichers Arbeitsprinzip: Zuletzt auf den Stack geschriebener Wert wird als erstes wieder gelesen. LIFO (Last In First Out) Stackpointer Der Stackpointer ist ein Zeiger auf den zuletzt eingetragenen Wert im Stack. Spezielle Befehle Für Stackzugriffe werden typischerweise die Befehle push und pop verwendet. o push auf den Stack schreiben o pop vom Stack lesen Stack Stackpointer Pop Push Der Stackpointer wird dabei automatisch mitgeführt. Der Stack wird typischerweise von hohen Adressen zu tiefen Adressen beschrieben.
Stack beim 68000 Als Stackpointer wird das Register A7 verwendet. Der Stackbereich wird durch die Initialisierung des Registers A7 festgelegt. Diese Initialisierung muss vor der ersten Verwendung des Stacks erfolgen, also vor dem ersten Unterprogrammaufruf. Im Simulator wird der Stackpointer automatisch mit $1 belegt. Der Befehlsatz des 68000 kennt keinen Push und Pop- Befehl. Diese Befehle ergeben sich aus dem Move-Befehl kombiniert mit den Adressierungsarten Postinkrement und Pedekrement. Move.w D0, -(A7) => Push D0 Move.w (A7)+, D0 => Pop D0 Abhängig von der verwendetet Datenbreite wird A7 automatisch um 1,2 oder 4 inkrementiert oder dekrementiert.
Beispiel: BlockmoveUP1: PROG move.l #$1,A7 ;Stackpointer auf $1 bsr MOVE ; Sprung ins Unterprogramm ENDE stop #$2700 ;***************************************************** ; Unterprogramm MOVE MOVE... FERTIG RTS ; Rücksprung ;******************************************************* end PROG
BlockmoveUP2: Wird das Unterprogramm MOVE ein zweites mal aufgerufen und z.b. zu A1 ein Wert addiert, um ein neues Ziel zu erhalten, so wird das Programm fehlerhaft reagieren, wenn im Unterprogramm die Registerinhalte verändert wurden. Es müssen daher immer alle im Unterprogramm verwendeten Register am Beginn des Unterprogramms auf den Stack gerettet und am Ende des Unterprogramms wieder hergestellt werden. PROG move.l #$1,A7 ;Stackpointer auf $1 bsr MOVE ; Sprung ins Unterprogramm add.l #$20,A1 ; Block auf neue Adresse verschieben bsr MOVE ENDE stop #$2700 ;***************************************************** ; Unterprogramm MOVE MOVE move.l A0,-(A7) move.l A1,-(A7) move.w D2,-(A7) ;Register retten...... FERTIG move.w (A7)+,D2 ;Register zurückholen move.l (A7)+,A1 move.l (A7)+,A0 RTS ; Rücksprung ;******************************************************* end PROG
Das Retten und Zurückholen der Register muss in umgekehrter Reihenfolge erfolgen. Diese Aktion tritt sehr häufig auf. Daher steht dazu ein spezieller Befehl zur Verfügung. MOVEM (Move Multiple) Assembler-Syntax: MOVEM.X Registerliste, < ea > MOVEM.X < ea >, Registerliste MOVEM ermöglicht das Kopieren einer ganzen Liste von Registern, maximal 16 Register. Erlaubt sind Wort- oder Langwortoperationen (.W oder.l). Die Liste wird in folgender Syntax angegeben: Auflistung einzelner Register, getrenn mit / Registerbereich z.b. D0-D3 Die Register werden unabhängig von der Aufzählungsreihenfolge in einer festen, internen Reihenfolge abgelegt. Der Befehl MOVEM wird in erster Linie zum Retten von Registern auf den Stack verwendet. Beispiel: movem.l D0-D7/A0-A6, -(A7) movem.l (A7)+, D0-D7/A0-A6 ; alle Register bis auf Stackpointer ;auf den Stack retten ; alle Register vom Stack holen movem.l D0/D3/D7/A4-A6, -(A7) ; D0,D3,D7,A4,A5,A6 auf den Stack retten
Beispiel: BlockmoveUP3: Verschachtelte Unterprogrammaufrufe ;***************************************************** ; Unterprogramm MOVE MOVE move.w D2,-(A7);Register retten... V bsr VORW bra FERTIG R bsr RUECKW... FERTIG move.w (A7)+,D2;Register zurückholen RTS ;******************************************************* ; Unterprogramm VORW, Länge in D0 ;******************************************************* VORW movem.l D0/A0-A1,-(A7)... movem.l (A7)+,D0/A0-A1 rts ;******************************************************* ; Unterprogramm RUECKW, Länge in D0 ;******************************************************* RUECKW movem.l D0/A0-A1,-(A7)... movem.l (A7)+,D0/A0-A1 rts
Vor BSR MOVE Im UP MOVE Im UP RUECK 1 SP FFFE Rück Rück FFFC 041C adr 041C adr FFFA 10 D2 SP 10 D2 FFF8 Rück FFF6 0448 adr FFF4 FFF2 0520 A1 FFF0 FFEE 0500 A0 FFEC FFEA 0010 D0 SP
1 FFFE FFFC FFFA FFF8 FFF6 FFF4 FFF2 FFF0 FFEE FFEC FFEA Im UP RUECK Rück 041C adr 10 D2 Rück 0448 adr 0520 A1 0500 A0 SP 0010 D0 Im UP RUECK vor RTS Rück 041C adr 10 D2 Rück 0448 adr 0520 A1 0500 A0 0010 D0 SP Im UP MOVE Rück 041C adr 10 D2 Rück 0448 adr 0520 A1 0500 A0 0010 D0 SP Im Hauptprogramm Rück 041C adr 10 D2 Rück 0448 adr 0520 A1 0500 A0 0010 D0 SP
Parameterübergabe Parameter sind Daten, die an ein Unterprogramm übergeben werden oder vom Unterprogramm zurückgegeben werden. Folgende Varianten sind möglich: Parameter im Register Übliche Methode! Wichtig ist, dass Register, die Rückgabeparameter enthalten, nicht auf dem Stack gesichert werden dürfen. Beim Zurückschreiben der Register wird sonst das gerade berechnete Ergebnis überschrieben. Parameter im Speicher ( = globale Variable ) Parameter auf dem Stack Dabei werden die Parameter vor dem UP- Aufruf auf den Stack geschrieben. Für Rückgabeparameter müssen Platzhalter auf den Stack geschrieben werden. Im UP wird auf die Parameter mit einer Adressdistanz ausgehend vom aktuellen Stackpointer auf die Parameter zugegriffen. Nach der Rückkehr vom UP muss der Stackpointer korrigiert werden.
Beispiel : BlockmoveUP4 (Parameterübergabe über Stack) PROG move.l #BL1_END,D4 ; Laenge berechnen-> D4 sub.l #BL1_ANF,D4 move.l #BL1_ANF,-(A7) ; BL_ANFANG auf Stack move.l #BL2_ANF,-(A7) ; Ziel auf Stack move.w D4,-(A7) bsr MOVE ; Block verschieben add.l #10,A7 ; Stack korrigieren ENDE stop #2700 ;***************************************************** MOVE movem.l D2/A0-A1,-(A7) ;Register retten (12 Byte) move.w 16(A7),D2 ;Parameter vom Stack lesen move.l 18(A7),A1 move.l 22(A7),A0...... FERTIG movem.l (A7)+,D2/A0-A1 ;Register zurückholen RTS ;******************************************************* 1 FFFE FFFC FFFA FFF8 FFF6 FFF4 FFF2 FFF0 FFEE FFEC FFEA FFE8 FFE6 BL1_ 041C ANF BL2_ 041C ANF 10 Len Rück 0448 adr 0520 A1 0500 A0 0010 D2 SP 16 18 22