Speicheradressierung 8051



  • Hallo zusammen.
    Ich habe eine Frage zur Speicheradressierung vom 8051.

    Der Stackpointer hat nach RESET den Wert 0x07
    MOV A, #0x12 //Inhalt 0x12 in A
    MOV B, #0x34 //Inhalt 0x34 in B
    MOV SP, #0x56 //Inhalt 0x56 in SP
    PUSH ACC //legt ACC im Stack ab
    PUSH B //legt B im Stack ab
    POP ACC //liest ACC vom Stack zurück
    POP B //liest B vom Stack zurück
    CALL Ende
    Ende: JMP $

    Inhalte?
    Akku: _____0x34______ B: ______0x12________ SP: _____0x58________
    0x56: ______00________ 0x57: _______12_____
    0x58: ______00________ 0x59: _______00_______

    Verstehe die Zuordnung nicht? Hat jemand eine gute Erklärung oder ein PDF zur erklärung?

    Gruss Andreas



  • Ich gehe davon aus, dass es sich um eine Hausaufgabe handelt. Du solltest deshalb alle benötigten Hilfsmittel eigentlich schon haben. Du brauchst zumindest eine Referenz der 8051-Befehle. Zu dem von dir benutzten Assembler (welchen?) brauchst du noch eine Assembler-Referenz. Zumindest musst du herauskriegen können, zu welchem 8051-Befehl die Assembler-Instruktion CALL und JMP umgesetzt werden. Ich habe mal ACALL und AJMP angenommen.

    Mit einem Debugger gehts einfach, manuell ist es ein bisschen schwieriger: Du musst die Speicheradressen des fertigen Programms herausfinden. Angenommen, das Programm beginnt bei Addresse 0, so sähe das Ganze so aus:

    //Adr                    was wohin            danach
    0000      MOV A, #0x12   // 0x12 nach A       | A = 12
    0002      MOV B, #0x34   // 0x34 nach B       | B = 34
    0005      MOV SP, #0x56  // 0x56 nach SP      | SP = 56
    0008      PUSH ACC       // ACC nach Stack    | SP = 57, 0x57 = ACC (0x12)
    000A      PUSH B         // B nach Stack      | SP = 58, 0x58 = B (0x34)
    000C      POP ACC        // Stack nach ACC    | SP = 57, ACC = 34 (ehemaliges B)
    000E      POP B          // Stack nach B      | SP = 56, B = 12 (ehemaliges ACC)
    0010      ACALL Ende                          | SP = 58, 0x57 = PCL (0x12), 0x58 = PCH (0x00)
    0012      Ende: AJMP $
    

    (Da ja alles hexadezimal ist, habe ich ab und zu das "0x" weggelassen)

    Der Scherz bei der Sache ist, dass der Stackpointer (SP) nach zwei PUSH und zwei POP wieder am Anfang ist. Durch das CALL werden die vorigen Werte überschrieben, und zwar mit der Adresse des Befehls, der nach dem CALL kommt. Dorthin würde ein RET hinspringen. Diese Adresse ist 0x12. 0x12 ist also nicht nur der Ausgangswert von A (bzw. ACC), sondern auch die Adresse von 'Ende:...'.

    Vor dem CALL steht SP auf 56. Das CALL inkrementiert SP um 1 (-> 57) und schreibt den niederwertigen Anteil der Rückkehradresse (0x12) auf den Stack, danach wird SP noch einmal inkrementiert (-> 58) und der höherwertige Anteil (0x00, weil sich das Programm ganz weit vorne befindet) auf den Stack. Diese Vorgehensweise wird in jeder Referenz beschrieben.

    A (auch Akku oder ACC genannt) und B wurden quasi ausgetauscht, weil sich PUSH-POP in LIFO-Manier verhalten. Der zuletzt gepushte Wert wird zuerst gepopt. Ein PUSH B - POP ACC bedeutet also, dass ACC jetzt den Wert von B hat. Das nächste POP holt sich den Wert, der davor gepusht worden ist.

    viele grüße
    ralph



  • Als Assembler dient Keil A51. Den "rechten" Teil habe ich soweit verstanden.

    Zu den Adressen:
    Nach dem Befehl MOV SP, #0x56 springt die Adresse zu 0008 da es die nächste Adresse ist nach dem Anfangswert 0x07 vom Stackpointer?

    Oder ist es so das der Befehl:
    MOV: 1 opcode byte und 2 mal 2-byte Adressen, eine für Quelle und eine für Ziel hat und somit in 3 Adressen schreibt.
    Pop und Push: 1 opcode byte und einmal 2-byte Adresse hat und somit in 2 Adressen schreibt?

    Danke und schöne Grüsse



  • MrMulugulu schrieb:

    Als Assembler dient Keil A51. Den "rechten" Teil habe ich soweit verstanden.

    Zu den Adressen:
    Nach dem Befehl MOV SP, #0x56 springt die Adresse zu 0008 da es die nächste Adresse ist nach dem Anfangswert 0x07 vom Stackpointer?

    Nein. Vergiss das ganz schnell! Die Aussage "Der Stackpointer hat nach RESET den Wert 0x07" hat überhaupt keine Bedeutung für diese Aufgabe. Es ist nämlich völlig egal, welchen Wert SP zu Beginn hat, da er mit "MOV SP, #0x56" überschrieben wird. Der alte Wert 0x07 geht verloren und SP hat nun den neuen Wert 0x56. Das hat auf den PC (program counter) keinerlei Auswirkung.

    Oder ist es so das der Befehl:
    MOV: 1 opcode byte und 2 mal 2-byte Adressen, eine für Quelle und eine für Ziel hat und somit in 3 Adressen schreibt.
    Pop und Push: 1 opcode byte und einmal 2-byte Adresse hat und somit in 2 Adressen schreibt?

    Überwiegend ja. Der Assembler bildet allerdings aus "MOV A,..." und "MOV B,..." unterschiedliche Maschinenbefehle. "MOV A, #0x12" wird zu "74 12" und "MOV B, #0x34" wird zu "75 F0 34". Das erste Mal braucht man also 2 Bytes und das zweite Mal 3 Bytes. Wenn 2 Bytes an die Adresse 0000 geschrieben wird, dann ist die nächste freie Adresse 2 Bytes weiter: 0000 + 2 = 0002. Wenn danach 3 Bytes an die Adresse 0002 geschrieben wird, dann ist die nächste freie Adresse 3 Bytes weiter: 0002 + 3 = 0005. Die angegebenen Adressen sind also die Startnummern der jeweiligen Befehle. Verbraucht der Befehl mehrere Bytes, dann ist die nächste Startnummer entsprechend höher. Disassembliert sieht dein Programm dann so aus:

    Adresse  Maschinenbefehle    Mnemonics
    0000     74 12               MOV A, #0x12 
    0002     75 F0 34            MOV B, #0x34 
    0005     75 81 56            MOV SP, #0x56
    0008     C0 E0               PUSH ACC     
    000A     C0 F0               PUSH B       
    000C     D0 E0               POP ACC      
    000E     D0 F0               POP B        
    0010     00 12               ACALL Ende    
    0012     00 00               Ende: AJMP $
    

    Du siehst, dass die PUSH- und POP-Befehle 2 Bytes verbrauchen, 1 Byte für opcode und nur 1 Byte für die RAM-Adresse der Register.

    viele grüße
    ralph



  • Wieso macht er denn aus den beiden mov Befehlen unterschiedliche Byte längen?.
    Danke erstmal für die Hilfe



  • SYSA schrieb:

    Wieso macht er denn aus den beiden mov Befehlen unterschiedliche Byte längen?

    Sonst wär's keine Banane mehr :).

    Das haben die Designer des 8051-Chips im Jahre 1980 so entschieden. Andere Prozessoren haben wiederum andere Eigenarten. Es gibt aber häufig "Kurzformen", wenn es um den Akkumulator geht.

    viele grüße
    ralph



  • Das war jetzt nicht die Frage woher weiß ich wann er was macht 😉


  • Mod

    SYSA schrieb:

    Das war jetzt nicht die Frage woher weiß ich wann er was macht 😉

    Ohne Kommas wird der Sinn nicht klar.



  • Das war jetzt nicht die Frage. Woher weiß ich, wann er wieviele Bytes belegt? 😃



  • SYSA schrieb:

    Woher weiß ich, wann er wieviele Bytes belegt?

    Für die Prozessoren gibt es Datenblätter, in denen unter anderem steht, welche Maschinenbefehle sie verstehen, wie die zugehörige Syntax lautet und was sie daraus machen. Der Assembler setzt nur diese* Syntax in Maschinenbefehle um. Manchmal gibt es aber mehrere Möglichkeiten, einen Assemblerbefehl in Maschinensprache umzusetzen, man denke an "mov ax, -1" beim 80386. Was der Assembler dann tatsächlich macht, lässt sich nur mit einem Disassembler erforschen. Allzuviele Freiheiten sollte sich ein Assembler nicht herausnehmen, sonst ist es kein Assembler mehr, sondern ein Compiler.

    viele grüße
    ralph

    * AT&T-Syntax mal außen vor gelassen.


  • Mod

    SYSA schrieb:

    Das war jetzt nicht die Frage. Woher weiß ich, wann er wieviele Bytes belegt? 😃

    Als Faustregel gilt: wenn mehrere Möglichkeiten der Kodierung existieren, wird die Kürzeste gewählt. Ausnahmen kann davon es bei Befehlen mit relativer Adressierung geben (typisch z.B. bei bedingten Sprüngen) - hier können sich auch Unterschiede in der Architektur des Assemblers manifestieren.


Log in to reply