8086 Microprozessor Emulator



  • Ich habe mir gestern den 8086 Microprozessor Emulator heruntergeladen und finde dieses Programm sehr hilfreich.
    (zu finden http://www.emu8086.com)
    Allerdings muss nicht jeder mit mir einer Meinung sein.
    Nun habe ich ihn natürlich gleichmal getestet und habe folgenden Code
    eingegeben um ganz einfachmal zu schauen was in diesen Registern vor sich geht.
    Problem ist wer kann mir bitte erlären was im Einzelnen passiert.
    Ich kann diese Veränderungen nicht nachvollziehen

    Ich habe folgenden Code eingegeben:
    #MAKE_COM#
    ORG 100h
    MOV AL, var1
    MOV BX, var2
    RET ; stops the program.

    Wie erwartet waren viele Dinge für mich unklar. Wenn ich diesen Code compiliere
    und mit der Option "Single Step " quasi debugge tut sich folgendes.
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ORG 100h ;nach dieser Zeile haben die Register folgende Werte:

    Offsetwert 0100 Hex A0 Warum?? Dec 160 Mov AL,[00108h]Warum??
    CS = 0B56 Warum??
    IP = 100
    SS = 0B56 Warum??
    SP = FFFE Warum??
    BP = 0000
    SI = 0000
    DI = 0000
    DS = 0B56 Warum??
    ES = 0B56 Warum??
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    MOV AL, var1 ; nach dieser Zeile haben die Register folgende Werte:
    springt automatisch auf
    Offsetwert 0103 Warum?? Hex 8B Warum?? Dec 139 Mov AL,[00108h]
    Mov BX,[00109h]

    AL = 07 Warum??
    BX = 00 Warum??
    CL = 0B Warum??
    DX = 00 Warum??
    CS = 0B56 Warum??
    IP = 103
    SS = 0B56 Warum??
    SP = FFFE Warum??
    BP = 0000
    SI = 0000
    DI = 0000
    DS = 0B56
    ES = 0B56

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    MOV BX, var2 ; nach dieser Zeile haben die Register folgende Werte:
    springt automatisch auf
    Offsetwert 0107 Warum?? Hex C3 Warum?? Dec 195 Mov AL,[00108h]
    Mov BX,[00109h]

    AL = 07 Warum??
    BH = 12 Warum??
    BL = 34 Warum??
    CX = 0B Warum??
    DX = 00
    CS = 0B56 Warum??
    IP = 107
    SS = 0B56 Warum??
    SP = FFFE Warum??
    BP = 0000
    SI = 0000
    DI = 0000
    DS = 0B56
    ES = 0B56



  • Tauboga schrieb:

    Wie erwartet waren viele Dinge für mich unklar. Wenn ich diesen Code compiliere
    und mit der Option "Single Step " quasi debugge tut sich folgendes.
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ORG 100h ;nach dieser Zeile haben die Register folgende Werte:

    Offsetwert 0100 Hex A0 Warum?? Dec 160 Mov AL,[00108h]Warum??
    CS = 0B56 Warum??
    IP = 100
    SS = 0B56 Warum??
    SP = FFFE Warum??
    BP = 0000
    SI = 0000
    DI = 0000
    DS = 0B56 Warum??
    ES = 0B56 Warum??
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    Dein Rechner besteht Speichermäßig aus vielen kleinen Segmenten. Diese sind 64KB groß (16 Bit Adressraum)
    Um nun einen Speicher von mehr als 64KB anzusprechen, gibt es ein Segmentregister, in dem eingestellt wird, welches Speichersegment aktuell angesprochen werden soll.
    Du hast eine .com - Datei assembliert, was bedeutet, dass sich der gesammte Programmcode in einem Segment befindet. In diesem Fall heißt es eben 0B56.

    Ich könnte dir jetzt alles erklären, aber vielleicht ist es günstiger, du kaufst dir ein Assemblerbuch und liest es dir selber durch ..



  • DocJunioR ich wäre dir sehr dankbar wenn du mir doch alles erklären
    könntest. Ich besitze bereits 2 Assemblerbücher(Assembler Grundlagen der Programmierung v. Marcus Roming)+(Programmiersprache Assembler v Reiner Backer). Glaube mir auf diese Fragen habe ich auch in diesen Bücher keine
    Antwort gefunden.

    Grüße



  • Tauboga schrieb:

    RET ; stops the program.

    RET ist ein rücksprung von einem unterprogramm. 'stops the program' würde eigentlich HLT heissen. in deinem fall wird von deinem benutzer-programm wieder in das monitor-programm zurückgesprungen.

    Tauboga schrieb:

    SP = FFFE Warum??

    der stack 'wächst' von oben nach unten d.h. je kleiner dieser wert desto mehr einträge hat der stack. in deinem fall sieht es so aus, als würde sich nur eine adresse (die rücksprungadresse für den RET befehl) auf dem stack befinden.



  • ich glaub er hat schon ein buch... aber es scheint nicht so gut zu sein.
    besorg dir andere bücher! am besten mehrere. geh am besten in die bücherei, die haben da bestimmt was.

    übrigens wenn du die zahlen in den registern anguckst, dann stellst du fest, dass manche denselben inhalt haben:
    cs = ss = ds = es

    CS sagt welches segment du gerade benutzt für deinen code. wie der doc schon erklärt hat, hat der 8086 1MB speicher zur verfügung. wenn dein programm gestartet wird dann sucht das betriebssystem für dein programm speicher, weil deinem programm 65536 bytes zustehen (64kilobyte). nun findet das betriebssystem an der stelle 0B56h (das entspricht dezimal 2902) genügend speicher für dein programm und schreibt diesen wert in das CS register.
    alle .com programme beginnen an der stelle 100h, deshalb wird in das IP register die zahl 100h reingeschrieben und deshalb musst du auch "org 100h" in deinen quelltext reinschreiben, damit auch die befehle an dieser adresse in deinem code beginnen.
    somit beginnt dein programm an CS:IP 0B56h:100h im speicher.
    da dieses programm ein .com (compact) ist, teilt das codesegment, sich den speicher mit dem datensegment und dem stapelsegment. deshalb haben DS und SS auch den gleichen wert wie CS.

    SP = FFFEh
    SS:SP geben an wo sich dein stapelsegment befindet, da ss wie oben schon besprochen 0B56h enthält und SP = FFFEh enthält, beginnt dein stapelsegment also an, 0B56h:FFFEh. es teilt sich dasselbe segment mit dem codesegment, weil CS = SS ist, aber es beginnt in diesem segment an FFFEh (dezimal 65534), also ganz am ende des codesegments. der speicher sieht nun so aus:

    |~~~~~~~~~~~~~~~~~~~~~~~~~~|<= 0B56h:0000
    |                          |
    |                          |
    |mov al,var1               |<= 0B56h:100h (CS:IP)
    |mov bx,var2               |
    |ret                       |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |<= 0B56h:FFFEh (SS:SP)
    |__________________________|
    

    wenn du jetzt zB in das register AX FFFFh reinschreibst:
    mov AX,0FFFFh
    und dieses nun auf den stapel schiebst:
    push AX
    so sieht das ganze so aus:

    |~~~~~~~~~~~~~~~~~~~~~~~~~~|<= 0B56h:0000
    |                          |
    |                          |
    |mov ax,0FFFFh             |<= 0B56h:100h 
    |push ax                   |
    |ret                       |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |FFFFh                     |<= 0B56h:FFFCh (SS:SP)
    |                          |
    |__________________________|
    

    dabei wird SP nun mit 2 subtrahiert hat also jetzt den wert FFFCh (dezimal 65532). nun wird der wert der in AX steht (FFFFh) an diese adresse geschrieben, also 0B56h:FFFCh (SS:SP). wenn du nun ax nochmal auf den stack schieben würdest, sieht das ganze so aus:

    |~~~~~~~~~~~~~~~~~~~~~~~~~~|<= 0B56h:0000
    |                          |
    |                          |
    |mov ax,0FFFFh             |<= 0B56h:100h
    |push ax                   |
    |push ax                   |
    |ret                       |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    |FFFFh                     |<= 0B56h:FFFAh (SS:SP)
    |FFFFh                     |
    |                          |
    |__________________________|
    

    SP wird nocheinmal mit 2 subtrahiert, hat also den wert FFFAh (dezimal 65530). und diesmal wird der wert, der in AX steht in 0B56h:FFFAh geschrieben.
    wie man vielleicht schon erkennen kann, nähert sich die adresse des stapels an die des codes, weil die zahl in SP immer subtrahiert wird.
    wenn du das gaze also zu oft machst, so dass SP so klein wird, dass es auf die stelle zeigt wo dein programmcode steht, dann wirst du beim schreiben auf den stack, dein eigenes programm mit FFFFh überschreiben. dein programm wird also nicht mehr ausführbar werden und wird irgendwann abstürzen, oder einfach anhalten.
    auch das datensegment teilt sich denselben bereich wie der code. normalerweise schreibt man die daten an das ende des codes.

    org 100h
    mov ax,FFFFh
    ret
    string db 'hallo welt!$'
    

    genauso sieht das auch im speicher aus. der string, wird direkt nach dem code eingetragen. auch deine .com datei sieht genauso aus, wenn du diese mal mit dem editor öffnest, dann wirst du sehen, dass am anfang komische ascii zeichen kommen und ganz am ende steht: hallo welt!$

    kommen wir zum IP register.
    CS:IP zeigt immer auf den nächsten befehl der ausgeführt werden soll. wenn dein programm startet dann hat IP den wert 100h (dezimal 256). der befehl der als erstes ausgeführt wird wenn dein programm startet ist der, der an der stelle 0B56h:0100h steht. bei dir also "mov al,var1". dieser befehl ist 3 byte gross, deshalb wird zu IP die 3 dazuaddiert damit es auf den nächsten befehl zeigt. bei dir ist es "mov bx,var2". dieser befehl ist 4 byte gross, deshalb wird die 4 zu IP addiert, wenn der befehl ausgeführt wird, damit IP auf den nächsten befehl zeigt, in deinem fall "ret". hier endet dein programm.

    mov al,var1
    diese zeile besagt, dass der wert der in der speicherstelle "var1" steht, in das register AL kopiert wird, also die 07h (dezimal 7). anscheinend sieht dein programm vollständig also so aus:

    org 100h
    
    main:
    	mov	al,var1
    	mov	bx,var2
    	ret
    
    var1 db 7h
    var2 dw 1234h
    

    bei der zweiten zeile ist es ähnlich. "mov bx,var2" sagt dem prozessor, dass die zahl, die in der speicherstelle var2 steht, in das register BX, hineingeschrieben wird. in diesem fall also die 1234h (dezimal 4660).
    dabei kommt die 34h in das register BL. das sind die ersten 8bit des registers BX und die zahl 12h kommt in das register BH. das sind die oberen 8bit des registers BX (bits 8 bis 15).

    wenn du das programm emulierst dann siehst du deinen code aber diesmal ohne die speicherstellen var1 und var2. diese werden vom assembler beim assemblieren durch deren adressen ersetzt.
    dein programm beginnt also an der stelle 100h. dort befindet sich der erste befehl "mov al,var1". da dieser befehl 3 byte gross ist steht der nächste befehl an der stelle 103h (100h+3h=103h). an dieser stelle steht der befehl "mov bx,var2". dieser befehl ist 4 byte gross. deshalb steht der nächste befehl an der stelle 107h (103h+4h=107h). das ist der befehl "RET". dieser befehl ist 1 byte gross. nach diesem befehl beginnen die variablen. diese beginnen an der stelle 108h (107h+1h=108h). die erste variable ist var1, da diese nur ein byte gross ist, beginnt die zweite variable var2 an der stelle 109h (108+1h=109h).
    deshalb ersetzt der assembler beim assemblieren diese beiden variablen mit 108h und 109h.

    alle register die eine 0000h (dezimal 0) als inhalt haben. wurden vom betriebssystem so gesetzt. daran kannst du nichts machen.
    vorsicht! wenn du programme für MS-DOS schreibst, haben nicht alle register, dieselben zahlen am programmanfang. verlasse dich nie darauf, dass das betriebssystem die register vorher auf 0000h setzt.



  • Assembler is ansich die "einfachste" aller sprachen, weil hier wirklich noch alles logisch und nochvollziehbar ist.
    par example:
    djnz - decrease and jump when not zero, einfacher gehts nicht mehr
    cjne - compare and jump when not equal

    schon alleine mit den 2 befehlen kannst sämtliche schleifen if und switch anweisungen aus c vergessen.

    z.b. inc a - den accumulator erhöhen ; mit dem und cjne kannst dir schon ne for,while, do while schleife baun. mit djnz kannst da ähnliches machen

    mit cjno kannst dir eine if baun

    also wennst da mal einen grundstock an unterprogrammen gebaut hast, programmiert sichs mit assembler beinahe so leicht wie mit c, auch wenns unterschiedliche anwendungsbereiche hat

    mov - irgendwas cvon irgendwo irgendwohin verschieben; z.b. aus dem accumulator in ein register usw...

    besorg dir einfach eine assembler befehlsliste, mit der is schon beinahe alles erklärt, weil neben den befehlen stehen kurze prägnante erklärungen, dies auf den punkt bringen.
    ein buch ansich is auch nicht verkehrt, da aber vielleicht eins, das erklärt wie ein assembler arbeitet, weil wennst das prinzip mal verstanden hast, kannst mit etwas zeitaufwand das ganze eigenhändig ohne compiler in maschinensprache übersetzen!

    hab das jahrelang in der schule machen müssen

    mfg Shark T


Log in to reply