[8051] Assambler - Flags



  • Hallo!

    In der Schule lernen wie gerade Assambler Mikrocontroller 8051 zu programmieren, aber derzeit nur auf den Zettel...

    Bsp:

    MOV A,#100dez
    ADD A,#40dez
    

    Aufgabenstellung: Ermittle den Inhalt des Akkus und der Flags(AC, C, OV) nach ADD.

    Naja die 100 wird in den A geschrieben und dann wird eben 40 zu 100 drauf gehählt --> im Akku steht am ende 140dez.

    Aber wie ermittelt man die Flags, wie Auxilliare Carry Flag, Carry Flag und Overflow Flag?

    Ich hab die flags gegoogelt, aber weis einfach nicht genau was die können etc. :(.

    Kann mir das einer bitte erklären?

    mfg gast



  • Eine gute Hilfe für solche Sachen ist übrigens das Windows/Dos Programm debug.

    Hm, und Internet...
    google "carryflag wiki" und so

    Es ist aber gut, wenn man binärrechnen kann, die Regeln sind einfach:

    0+0=0
    1+0 oder 0+1 = 1
    1+1=10
    1+1+1 = 11

    wieviel ist also
    11 + 1
    oder 11 * 11?

    in Debug:
    müsste man einmal AX mit FFFF laden bx mit 1 laden und mit add zusammenzählen und gucken was die Flags anzeigen, und danach AX nochmal mit FFFF laden und auch BX mit FFFF laden und beide multiplizieren über AX mit dem Befehl "mul BX" und gucken, was die Flags anzeigen.

    C:\Users\nachtfeuer>debug

    -a                      ;a wie assembly, debugs asm editor
    1A05:0100 mov ax,ffff
    1A05:0103 mov bx,1
    1A05:0106 add ax,bx
    1A05:0108 mov ax,ffff
    1A05:010B mov bx,ffff
    1A05:010E mul bx
    1A05:0110
    -t                      ;jetzt testen und Flags angucken mit "t"
    
    AX=FFFF  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
    DS=1A05  ES=1A05  SS=1A05  CS=1A05  IP=0103   NV UP EI NG NZ NA PE NC
    1A05:0103 BB0100        MOV     BX,0001
    -t
    
    AX=FFFF  BX=0001  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
    DS=1A05  ES=1A05  SS=1A05  CS=1A05  IP=0106   NV UP EI PL NZ NA PO NC
    1A05:0106 01D8          ADD     AX,BX
    -t
    
    AX=0000  BX=0001  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
    DS=1A05  ES=1A05  SS=1A05  CS=1A05  IP=0108   NV UP EI PL ZR AC PE CY
    1A05:0108 B8FFFF        MOV     AX,FFFF
    -t
    
    AX=FFFF  BX=0001  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
    DS=1A05  ES=1A05  SS=1A05  CS=1A05  IP=010B   NV UP EI PL ZR AC PE CY
    1A05:010B BBFFFF        MOV     BX,FFFF
    -t
    
    AX=FFFF  BX=FFFF  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
    DS=1A05  ES=1A05  SS=1A05  CS=1A05  IP=010E   NV UP EI PL ZR AC PE CY
    1A05:010E F7E3          MUL     BX
    -t
    
    AX=0001  BX=FFFF  CX=0000  DX=FFFE  SP=FFEE  BP=0000  SI=0000  DI=0000
    DS=1A05  ES=1A05  SS=1A05  CS=1A05  IP=0110   OV UP EI PL ZR AC PE CY
    1A05:0110 0000          ADD     [BX+SI],AL                         DS:FFFF=00
    


  • Die Flags zeigen an, dass bei der letzten Operation etwas Bestimmtes passiert ist. C, AC und OV teilen mit, dass beim ADD irgendwo ein Übertrag aufgetaucht ist.

    Beispiel:

    138 + 75 = 213

    Und nun grundschulmäßig auf Papier:

    Augend     138
    Addend      75
    Übertrag   11
    ==============
    Ergebnis   213
    

    Für den Mikrocontroller machst Du dasselbe, aber binar:

    Augend     10001010
    Addend     01001011
    Übertrag      1 1
    ===================
    Ergebnis   11010101
    

    Wenn Du die einzelnen Bits von rechts nach links mit 0 bis 7 durchnummerierst, dann siehst Du, dass bei Bit1 (1+1=10 -> 0, Übertrag 1) und Bit3 (1+1=10 -> 0, Übertrag 1) ein Übertrag passiert ist, der dann bei Bit2 und Bit4 eingetragen wird.

    Die nachfolgenden Ausführungen gelten 8051-Prozessoren. Für andere Prozessoren kann etwas anderes gelten.

    Das Carry-Flag (C) wird gesetzt, wenn bei Bit7 ein Übertrag passiert ist. Das ist das am häufigst benutzten Flags, weil es bei einer Addition anzeigt, dass das Ergebnis die Maximalgröße des Registers gesprengt hat, oder bei einer Subtraktion anzeigt, dass das Ergebnis kleiner als 0 ist.

    218 + 133 = 351 (passt nicht in ein 8-Bit-Register)

    Augend     11011010
    Addend     10000101
    Übertrag  1
    ===================
    Ergebnis   01011111
    

    Das AC-Flag wird gesetzt , wenn bei Bit3 ein Übertrag passiert ist. Das ist für 4-Bit-Operationen gedacht.

    11 + 12 = 23 (passt nicht in die unteren 4 Bit)

    Augend     00001011
    Addend     00001100
    Übertrag      1
    ===================
    Ergebnis   00010111
    

    Das OV-Flag wird gesetzt bei Übertrag von Bit7 aber nicht von Bit6 oder Übertrag von Bit6 aber nicht von Bit7. Das ist für vorzeichenbehaftete Zahlen gedacht, wenn sich durch die Operation unerwünscht das Vorzeichen (Bit7) geändert hat.

    (-38) + (-123) = (-161) (Ergebnis passt nicht in ein Register)

    Augend     11011010
    Addend     10000101
    Übertrag  1
    ===================
    Ergebnis   01011111
    

    viele grüße
    ralph

    PS: Es heißt "Assembler" (mit 'e'), nicht "Assambler"!



  • Und man kann spannenderweise mit dem Pentium bzw. Debug auch super 8bit compi spielen, zum Beispiel
    (debug.exe)

    -rax
    AX 0000
    :ff
    -rbx
    BX 0000
    :1
    -rdx
    DX 0000
    :64
    -rcx
    CX 0000
    :28
    -r
    AX=00FF  BX=0001  CX=0028  DX=0064  SP=E082  BP=0000  SI=0000  DI=0000
    DS=1E6D  ES=1E6D  SS=1E6D  CS=1E6D  IP=0100   NV UP EI PL NZ NA PO NC
    1E6D:0100 0000           ADD    [BX+SI],AL                         DS:0001=20
    -a
    1E6D:0100 add al,bl
    1E6D:0102 add dl,cl
    

    Und dann nur noch gucken, was die flags so treiben, wenn die Befehle ausgeführt werden. 😉

    Den Instruction Pointer kann man in debug beschreiben mit "rip":

    -rip
    IP 010A
    :100
    


  • Danke, ich verstehs jetzt!

    Nun habe ich ein Bsp:

    Ein Block im internen RAM(Startadresse 20hex, länge=10Bytes) soll gelöscht werden.

    In der Schule haben wir es so gemacht:

    MOV R1,#20
    MOV A,#10
    Loop: MOV @R1,#0
    INC R1
    DEC A
    JNZ Loop ;springt ja nur wenn A nicht 0 ist
    

    oder so:

    MOV R1,#20hex
    CLEAR: MOV @R1,#0hex
    INC R1
    CJNE R1,#2Ahex,CLEAR ;springe zu clear, wenn der inhalt des Registers R1 ungleich 2Ahex ist oder?
    

    1. Teil:
    Warum 2Ahex?
    Und könnt ihr mir bitte die Aufgabenstellung genau erklären... Aus was besteht ein Block was ist den damit genau gemeint?
    Die Startadresse ist 20hex und was steht in dieser drinnen? irgendein
    Binärwert?
    Die Länge des Block ist 10 Bytes, was heißt das nun genau?
    Erklärt mir bitte die beiden Codes.

    2. Teil:
    Diese JMP-Befehle springen halt irgendwo im Programm herum.

    Wenn man schreibt:

    SJMP rel ;rel ist eine relative 8 - Bit - Offset - Adressse(-128 bis +127) - Was ist eine 8-Bit-Offset-Adresse? Eien Offsetspannung ist jene Spannung die am Eingang angelegt werden muss das am Ausgang 0V rauskommt..., das weiß ich über offset, aber ich kanns net umsetzen. Die Gleiche Frage gilt für adr16 und adr11.
    LJMP adr16 ;mit LJMP kann überall herumspringen beim 8051er - adr16 ist eien 16bit Adresse
    AJMP adr11 ;mit AJMP halt nur im 2kbyte-bereich, also kann nur 2kbyte weit springen - adr11 ist eine 11bit adresse
    
    ;Bsp:
    "irgendein wort":.... ; ja und "irgendein wort" ist eine 11bit adresse? und man kann nur 11 bit weit springen? Ich verstehs net...
    ....
    AJMP "irgendein wort"
    ....
    

    Ja und das gleiche bsp mit LJMP und SJMP.

    Danke im voraus! 🙂

    mfg guest123



  • Du machst es schon richtig: Sich in Ruhe Fragen überlegen und fragen. Allerdings ist es besser, den Lehrer als uns zu löchern. Da braucht man zwar eine dicke Haut, weil fragen hierzulande als Beleidigung, Angriff, Verzögerung, Querulantentum etc. gilt, aber anders kommst Du nicht weiter.

    guest1234 schrieb:

    Ein Block im internen RAM(Startadresse 20hex, länge=10Bytes) soll gelöscht werden.

    Warum 2Ahex?
    Und könnt ihr mir bitte die Aufgabenstellung genau erklären... Aus was besteht ein Block was ist den damit genau gemeint?

    Der interne Speicher ist der Speicher, der im Mikrocontroller fest eingebaut ist. Externer Speicher kann bei bestimmten Mikrocontrollern dazugeschaltet werden.

    Ein Block ist ein zusammenhängendes Stück von irgendetwas (Blockschokolade, Wohnblock, Notizblock, Ostblock), in diesem Fall ein zusammenhängender Speicherbereich. Der Zusammenhang besteht darin, dass eine bestimmte Anzahl von Speicheradressen mit fortlaufenden Hausnummern adressiert werden und vom Programmierer für einen bestimmten Zweck benutzt werden. Der "bestimmte" Zweck besteht hier nur darin, sie auf Null zu setzen ("zu löschen = engl. clear"), da es sich nur um ein Beispiel handelt.

    Wg. 2Ah:
    Zähl mal 10 Zahlen ab 20: 20,21,22,23,24,25,26,27,28,29. Wenn Du bei 30 bist, bist Du eins zu weit.
    Und nun das Ganze hexadezimal ab 20h: 20h,21h,22h,23h,24h,25h,26h,27h,28h,29h. Wenn Du bei 2Ah bist, bist Du eins zu weit.

    Die Startadresse ist 20hex und was steht in dieser drinnen? irgendein
    Binärwert?

    Stell Dir das wie eine Straße mit Hausnummern vor: Machen Sie 10 Häuser der Goethestraße ab Hausnummer 20 platt! Was da vorher stand, ist egal, nachher ist alles platt ;).

    Die Länge des Block ist 10 Bytes, was heißt das nun genau?

    10 Häuser :). Genauer wäre: 10 Speicherzellen mit je einem Byte Größe.

    Erklärt mir bitte die beiden Codes.

    Der erste Code benutzt eine sogenannte Laufvariable (A). In BASIC wäre das gleichbedeutend mit FOR A=10 TO 1 STEP -1 . In C hieße es for (A=10;A>0;A--) .
    Der zweite Code benutzt eine Abbruchbedingung: Wenn die letzte Speicheradresse plus Eins gelöscht werden soll, wird die Schleife verlassen. Das entspricht in einer Hochsprache der WHILE-Schleife.

    SJMP rel ;rel ist eine relative 8 - Bit - Offset - Adressse(-128 bis +127) - Was ist eine 8-Bit-Offset-Adresse? Eien Offsetspannung ist jene Spannung die am Eingang angelegt werden muss das am Ausgang 0V rauskommt..., das weiß ich über offset, aber ich kanns net umsetzen. Die Gleiche Frage gilt für adr16 und adr11.

    Eine "relative Offset-Adresse" ist hier ein "weißer Schimmel". Der Prozessor nimmt die aktuelle Programm-Adresse, zählt 'rel' hinzu und springt dahin. Wenn 'rel' negativ ist, wird eben abgezogen. Wie Du siehst, kann er nicht weiter als 128 Bytes weit springen, dafür ist der Befehl kürzer und schneller als andere Sprungbefehle.

    LJMP adr16 ;mit LJMP kann überall herumspringen beim 8051er - adr16 ist eien 16bit Adresse
    AJMP adr11 ;mit AJMP halt nur im 2kbyte-bereich, also kann nur 2kbyte weit springen - adr11 ist eine 11bit adresse

    'adr..' steht für eine absolute Adresse. Dorthin wird gesprungen. Eine 11bit-Adresse steht für eine Adresse am Anfang des Adressenraums (2048 Bytes). Wenn Du einen Controller mit größerem Programmspeicher hast und eine Adresse hinten anspringen willt, musst Du LJMP (Long Jump) nehmen, da hier die Adresse 16 Bits groß sein darf. Dafür braucht der Befehl 1 Byte mehr an Speicherplatz, was - wenn Du wirklich einen großen Controller hast - nicht weiter tragisch ist. Es geht also nicht um "weit springen", sondern um "dahin springen".

    "irgendein wort":.... ; ja und "irgendein wort" ist eine 11bit adresse? und man kann nur 11 bit weit springen? Ich verstehs net...
    ....
    AJMP "irgendein wort"
    ....

    Fasse das Wort "Wort" in der Computerei nur mit spitzen Fingern an! Ich vermute mal, dass der Lehrer "Label" gemeint hat. Du musst die Einsprungstellen irgendwie bezeichnen ("Loop","CLEAR") und der Assembler berechnet die Adressen. Sollte die Einsprungstelle mit diesem Befehl nicht erreichbar sein (z.B. eine 16-Bit-Adresse soll mit AJMP angesprungen werden), bekommst Du eine Fehlermeldung.

    viele grüße
    ralph


Anmelden zum Antworten