Rechnerarchitektur und Betriebssysteme (CS201): Asm-Programmierung, Stack, Compiler 11. November 2005 Prof. Dr. Christian Tschudin Departement Informatik, Universität Basel Wiederholung / Diskussion 1. Wie hängen Wort- und Adressgrösse zusammen? Bsp: Ein x86-prozessor hat 32-Bits Worte und 32-Bits-Adressen. Der AVR-µPRozessor hat 8-Bits-Worte: wieviele Adress-Bits? 2. Warum gibt es beim AVR-Prozessor keinen Befehl inc <memoryaddr>? 3. Ist Assembler eine Programmiersprache? c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 2/36
Relevanz von µkontrollern (µc) µc sind billig: 50 Rappen bis 5 Franken pro Chip µc- Marktvolumen: über 10 Milliarden USD pro Jahr! Vergleich: µprozessoren (PC etc): 25 30 Milliarden USD/a Digital Signal Processors (DSP): 4 Milliarden USD/a Populäre Geräte/Anwendungen: Auto: heute 60 µc und mehr pro Highend-Auto NASA Mars Sojourner Rover (8-bit Intel 80C85) Sonicare Plus Zahnbürste (8-bit Zilog Z8) c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 3/36 AVR Studio und WINE Die Simulationsumgebung AVR Studio 3.56 kann mit WINE auch unter Linux betrieben werden. WINE (Wine is Not an Emulator) implementiert die Window Programmier-Schnittstelle und bildet Aufrufe auf Linux ab. 3 Varianten, um Windows-Binaries auszuführen: avrstudio.exe avrstudio.exe Windows vmware o.ä. avrstudio.exe WINE Windows Linux Linux http://www.winehq.org/site/download http://www.atmel.com/dyn/products/tools card.asp?tool id=2724 c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 4/36
Implementierung von IF...ELSE in C: if (r1 < r2), if (r1 <= r2), if (r1 == r2) etc AVR hat ca ein Dutzend Vergleichsoperationen (Seite 10): BR{LT, GE} branch if less than, if greater or equal BR{EQ, NE} branch if equal, if not equal BRCS und BRCC branch if carry set, if carry clear BRLO branch if lower (unsigned) BRSH branch if same or higher (unsigned) BR{MI, PL} branch if minus, if plus BR{VC, VC} branch if overflow clear, if set sowie weitere Abfragen auf anderen Bits und Flags c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 5/36 Bedingte Ausführung von Code C: if (r1 < r2) { statement; } Realisierung in zwei Schritte: zuerst Register vergleichen, dann auf Grund von Nebeneffekten (Flags) bedingter Sprung Umsetzung: cp r1, r2 brge L1... statement... ; vergleiche (compare) r1 mit r2 L1: nop ; hier geht s weiter ; verzweige falls r1 >= r2 (mit Vorzeichen!) c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 6/36
Ausführung von Code-Alternativen C: if (r1 < r2) { stmt1; } else { stmt2; } Umsetzung: cp r1, r2 ; vergleiche (compare) r1 mit r2 brge L1 ; verzweige falls r1 >= r2 (mit Vorzeichen!)... stmt1... rjmp L2 L1:... stmt2... L2:... ; hier geht s weiter c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 7/36 WHILE Schleife C: while (r1 < r2) { statement; } Umsetzung: L1: cp r1, r2 ; vergleiche (compare) r1 mit r2 brge L2 ; springe hinaus falls r1 >= r2 (mit Vorzeichen!)... statement... rjmp L1 L2:... ; hier geht s weiter c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 8/36
REPEAT-UNTIL bzw DO-WHILE Schleife C: do { statement; } while (r1 < r2); Umsetzung: L1:... statement... cp r1, r2 ; vergleiche (compare) r1 mit r2 brle L1 ; springe zurück falls r1 < r2 (mit Vorzeichen!)... ; hier geht s weiter c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 9/36 FOR Schleife C: for (stmt1; r1 < r2; stmt2) { stmt3; } Umsetzung: stmt1 L1: cp r1, r2 ; vergleiche (compare) r1 mit r2 brge L2 ; springe hinaus falls r1 >= r2 (mit Vorzeichen!)... stmt3...... stmt2... rjmp L1 L2:... ; hier geht s weiter c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 10/36
Programmierbeispiel 1: Vorzeichen wechseln Input: in Register 16 Output: Register 16 ldi r16, 15 ; Konstante 15 an Register 16 ldi r17, 255 ; Konstante 255 an Register 17 eor r16, r17 inc r16 ; xor von r16 ; r16 um 1 erhöhen Zuerste müssen die Register geladen werden, dann das 2er-Komplement berechnen. Beachte die Bit-Negation mit XOR. c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 11/36 Programmierbeispiel 2: Reihe Zahlen von 1 bis N addieren. clr r0 ; Register auf 0 setzen ldi r16, 1 ; Konstante 1 (erste Zahl N) an Register 16 ldi r17, 4 ; Konstante 4 als Zähler-Variable in Register 17 L1: ; Label (logische Adresse) add r0, r16 ; addiere Inhalt von r16 zu r0 inc r16 ; N erhöhen dec r17 ; Zähler verkleinern (setzt Zero-Flag) brne L1 ; zurück zu L1, falls ungleich 0 end: nop ; no-operation BRNE = branch if not equal c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 12/36
Programmierbeispiel 3: Länge eines Strings berechnen ASCIIZ: String wird durch das Zeichen 0x00 abgeschlossen. (in C) Neue Befehle: rjmp relative jump.db Assembler-Anweisung für Datenablage cpi compare immediate, Vergleich mit einer Konstant Z Pseudoregister Zugriff auf Programmspeicherbereich (Z = R31:r30) adiw add immediate to word Bearbeitung von Z lpm load from program memory c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 13/36 Programmierbeispiel 3: Fortsetzung rjmp main str:.db "hello", 0 main: clr r16 ; Register auf 0 setzen ldi r30, LOW(str*2) ; Programmspeicher hat Wort-Adressen ldi r31, HIGH(str*2) L1: lpm r17, Z ; lese Byte cpi r17, 0 ; schon 0? breq L2 ; falls ja gehe zu L2 inc r16 ; erhöhe Zähler r16 adiw r30, 1 ; erhöhe Z Pointer rjmp L1 ; Schleife weiter bei L1 L2: c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 14/36
Speicheradressen des Datensegments (Memory Map) 0x0100 0x10ff Stack 4096 Hauptspeicher Zellen (SRAM) 0x0060 0x00ff 0x0020 0x005f 0x0000 0x001f 8 Bits 160 "extended IO" Registers 64 "Input/Output" Register 32 "general purpose" Register c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 15/36 Stack Stapel, Kellerspeicher etc Zwei Operationen: push (neues Element drauf -legen) pop (oberstes Element wegnehmen) Stack pointer (SP): zeigt auf erste freie Stelle Bei AVR (und meisten CPUs): Stack wächst nach unten d.h. ein POP verkleinert den Wert des Stack Pointer c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 16/36
Stack im AVR-Chip Stack liegt im Datensegment (SRAM) SP ist ein Spezialregister, 16-bit weit. Alle Register sind memory-mapped, auch SP: 0x5d (low byte) 0x5e (high byte) Initialisierung: ldi r16, LOW(4352-1) sts 0x005d, r16 ldi r16, HIGH(4352-1) sts 0x005e, r16 ; Bem: wäre kompakter zu kodieren mit port -Befehlen c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 17/36 Stack-Verwendung Dynamischer Speicher anlegen: Bytes ablegen (und holen) Gegensatz: statisch allozierte Variablen: üblicherweise im unteren Speicherbereich (0x0000...) (Konstanten: im code -Segment / read-only memory) Beispiel: Register temporär freimachen push r12 ; Inhalt von r12 auf Stack... ; beliebige Verwendung von r12 pop r12 ; r12 erhält wieder den alten Wert Wiederholung: Stackpointer zeigt auf erste freie Position c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 18/36
Stack-Maschinen Operationen meist nur im Register wegen Geschwindigkeit (Speicherzugriff) Stack wird aber als Zwischenablage verwendet Beispiel: 3 * ( fct(4) + fct(5) ) sei zu berechnen Umsetzung: push 3 call fct(4) (Resultat auf dem Stack) call fct(5) (Resultat auf dem Stack) pop r1 / pop r2 / add r1,r2 / pop r2 / mult r1,r2 Heutige CPUs sind keine reinen Stackmaschinen, aber beinahe c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 19/36 Infix, Postfix und Prefix-Notation Arithmetische Ausdrücke in verschiedenen Darstellungen: Normale (Infix-) Notation: 3 mult (4 plus 5) Postfix Notation: 3 4 5 plus mult reine Stackmaschine keine Register nötig keine Klammern auch bekannt als RPN reverse polnish notation Grundlage von PostScript, Forth Prefix Notation: (mult 3 (plus 4 5)) z.b. in LISP, Tcl c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 20/36
Exkurs: Postfix-Notation funktioniert auch für Code C: if ( test(a) ) { stmt1; } else { stmt2; } Umsetzung in Postfixnotation (PostScript) a % push a test % call: Funktion wird Resultat auf dem Stack lassen { stmt1 } % dies setzt einen Codeblock auf den Stack { stmt2 } % dies setzt einen Codeblock auf den Stack ifelse % erwartet 3 Argumente auf dem Stack, führt einen % der Codeblocks aus gemäss 3. Wert auf Stack Vorteil: keine Labels nötig c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 21/36 Postfix-Order abarbeiten (mit Register) infix: a + b * c + (d * e + f) * g postfix: a b c * + d e * f + g * + Initialisiere Stack DO Lese Postfix-Ausdruck Symbol für Symbol IF nächstes Symbol ein Operand THEN push Operand IF nächstes Symbol ein Operator THEN pop von zwei Operanden vom Stack (z.b. in Register) wende Operator an push des Resultats FI OD Schlussresultat auf dem Stack c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 22/36
Postfix-Order erzeugen Compiler-Arbeit infix: a + b * c + (d * e + f) * g postfix: a b c * + d e * f + g * + + Syntaxbaum des (Infix-) Ausdrucks erzeugen + + * Baum depth first traversieren (siehe Algo&Daten) * * a b c d e f g dabei Operator (eines Knotens) am Schluss ausgeben. c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 23/36 Subroutinen s1: call s1 nop ret Code an Adresse s1 soll mehrfach verwendet werden können. c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 24/36
(r)call und ret: Stack für Rücksprung-Adresse nutzen call: legt das Wort PC + 1 auf den Stack, SP wird um 2 verkleinert (post-decrement) setzt PC auf die Adresse der Subroutine ret: erhöhe SP um 2 (pre-increment) ersetzt PC mit oberstem Stackwort rcall S1... S1:... Code von S1... ret c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 25/36 Prozedur-Aufruf Beispiel mit max() C: void max() { if (r1 > r2) r3 = r1; else r3 = r2; } max: cp r2, r1 ; Prozedur max() brge L1 mov r3, r1 rjmp L2 L1: mov r3, r2 L2: ret... main: rcall max ; Prozeduraufruf c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 26/36
Prozedur-Aufruf ohne Stackpointer! Rücksprung-Adresse muss nicht im Stack abgelegt werden. Statt rcall max (mit impliziter Benutzung des Stacks) Rücksprungadresse in Reg (oder Hauptspeicher) ablegen. Beispiel mit Register Z (r30:r31) max: cp r2, r1... L2: ijmp ; indirect Jump: benutzt Inhalt von Reg Z main: ldi r30, LOW(next) ldi r31, HIGH(next) rjmp max next:... ; statt rcall c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 27/36 Prozedur-Aufruf ohne Stackpointer! (Forts) Problem: Nur 1 Register Z vorhanden: was, falls max() eine zweite Prozedur aufrufen muss? Für jeden Aufruf (!) einen separaten Speicherplatz, um Rücksprung abzulegen Damit ist aber keine Rekursion möglich. Man könnte die separaten Speicherplätze wie einen Stapel verwalten (Rekursion auch ohne CPU- Stackpointer ist möglich) c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 28/36
Parameter-Uebergabe für Prozeduren/Funktion C: int max(int a, int b) { return a > b? a : b } Aehnliche Diskussion wie bei Rücksprungadresse: Stapel, Register oder Hauptspeicher benutzen Praxis: a) einfache Param/Funktionswerte (int) via Register b) Liste von Eingabeparameter auf dem Stack übergeben Beispiel: ldi r24, 5 clr r25 rcall max mov r18, r24 ; Konvention: r24:r25 für 16-Bit Resultate c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 29/36 Parameter-Uebergabe Weitere Anforderungen Prozedur hat lokale Variablen Arbeitsregister müssen temporär gesichert werden Calling Convention : wer ist für die Reg-Sicherung zuständig? Stack Frame : Speicherauslegeordnung einer Prozedur Rücksprungadresse gesicherte Arbeitsregister lokale Variablen c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 30/36
Stack Frame (C-Stil) Framepointer Stackpointer Param N Param N 1... Param 1 Rücksprungadresse alter Framepointer lokale Variablen alte Reg Werte Zwischenwerte "callee frame" "caller frame" Stackpointer für: Zwischenwerte, Rücksprungadresse Framepointer (FP) FP erlaubt Zugriff auf Parameter, sowie lokale Variablen mit (bekanntem) relativem Offset c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 31/36 Historische Bemerkung: Pascal vs C-Parameterübergabe Pascal: PROCEDURE proc(a, b: INTEGER);... Aufruf:... proc(3, 4); C: proc(int a, int b,...);... Aufruf:... proc(3, 4, 5, 6); Das heisst: C erlaubt variable Parameterzahl, siehe printf( hello %s, world ); Dies bedingt, dass C die Parameter in umgekehrter Reihenfolge auf den Stack legt. Deshalb ist auch die Ausführordnung in C umbestimmt. c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 32/36
Funktionsaufruf und Parameter-Uebergabe Ablauf beim Caller : Arbeitsregister sichern soviel Parameter wie möglich in Register, sonst Stack Rücksprungadresse sichern, Aufruf Ergebniss aus dem r24:r25 Register lesen Ablauf beim Callee : Prolog: FP sicher, Param kopieren, neuer FP+SP, Reg sichern Code abarbeiten Resultat in richtige Register schieben Epilog: Reg restaurieren, FP zurücksetzen, Stack räumen c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 33/36 Beispiel für Framepointer-Verwendung C: fct() { char a;... } AVR-Konvention: Y-Register (r28:r29) als Frame Pointer AVR-Konvention: Framepointer zeigt auf unterste lokale Variable push r29 ; sichere alten Framepointer push r29 in r28, SP_L ; low Byte des Stackpointers in r29, SP_H ; high Byte sbiw r28, 1 ; erniedrige SP-Wert um 1 (für a ): FP-Wert!... ; Register-Param kopieren und SP neu setzen Bemerkung: SP wird durch eine IO-Operation gelesen, siehe IO c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 34/36
C-Compiler für AVR-CPU Crosscompiler Wird auf einem Host-Rechner ausgeführt (z.b. Intel x86, Linux) Erzeugt aber Code für eine andere Architektur, wie AVR gcc unterstützt auch AVR: spezielle Version des Kompilers, muss speziell installiert werden Aufruf (um Assembler-Code zu generieren, liegt in test.s vor): /opt/cdk4avr/bin/avr-gcc -S test.c c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 35/36 Beispiel main() { unsigned char a; a = 5; return 10 * a; } main: /* prologue: frame size=1 */ ldi r28,lo8( stack - 1) ldi r29,hi8( stack - 1) out SP_H,r29 out SP_L,r28 /* prologue end (size=4) */ ldi r24,lo8(5) std Y+1,r24 ldd r24,y+1 mov r18,r24 clr r19 mov r25,r19 mov r24,r18 ; --> Forsetzung rechte Spalte lsl r24 rol r25 lsl r24 rol r25 lsl r24 rol r25 add r24,r18 adc r25,r19 add r18,r24 adc r19,r25 mov r25,r19 mov r24,r18 /* epilogue: frame size=1 */ rjmp exit /* epilogue end (size=1) */ c Christian Tschudin CS201 Rechnerarchitektur und Betriebssysteme 2005-11-11, 36/36