PrettyOS um Rechner erweitern



  • nochmal:
    addition:

    mov ergebnis,50
    add ergebnis,20
    ;ergebnis = 70
    

    subtraktion:

    mov ergebnis,30
    sub ergebnis,10
    ;ergebnis = 20
    

    multiplikation:

    mov zahl1,3
    mov zahl2,5
    imul ergebnis, zahl1, zahl2
    ;ergebnis = 15
    

    division:

    mov zahl1,30
    mov zahl2,10
    idiv ergebnis, zahl1, zahl2
    ;ergebnis = 3
    

    alles soweit richtig?
    quelle: http://de.wikibooks.org/wiki/Assembler_(80x86_Prozessor)-Programmierung:_Rechnen_mit_dem_Assembler
    korrigiert



  • lmb, mach besser einen neuen Thread hier im Assembler-Forum auf, in diesem hier geht's um etwas anderes 😉



  • Will ich meinen. Ich versuche mal, die betroffenen Beitraege abzutrennen, hoffe die "Kollateralschaeden" sind gering. 😃



  • Gut, Danke 😉
    Stimmt das denn alles?
    Ich bin mir vorallendingen bei der Division nicht sicher...

    Echt klasse Forum!



  • tiny und co. werden aufgrund regelmaessigen Missbrauchs zensiert. Wenn du was verlinken willst, tue es direkt.

    Zu deinem Code:
    imul akzeptiert keine 3 Speicherzugriffe als Operanden (die wenigsten Instruktionen koennen uebrigens nur mit Speicherzugriffen als Operanden arbeiten).
    Selbes gilt also fuer idiv. Das akzeptiert sogar nur einen Operanden, den Divisor.
    In Befehlsreferenzen kannst du nachschauen, wie Befehle funktionieren. zB. hier.

    Allgemein solltest du dir auch mal Gedanken darueber machen, wann es sinnvoll ist Variablen zu verwenden und wann Register reichen. In laufenden Rechnungen arbeitet man sinnvollerweise zB. eher auf Registern: Arbeit mit Variablen braucht einmal Platz im RAM (klar) und verhaeltnismaessig viel Zeit zum Lesen/Schreiben im RAM.



  • das mit den registern habe ich leider noch nie richtig geschnallt.
    deswegen verstehe ich wohl auch deinen link nicht richtig, aber trotzdem danke!



  • In den FAQ gibt es Einsteigertutorials zu Asm. Der Umgang mit Registern und ein grobes Verstaendnis der Programmierumgebung der Ziel-CPU gehoert zu den grundlegensten Dingen beim Programmieren in Assembler.
    So, fuerchte ich, ist diese Art von Frickelei leider recht fruchtlos.



  • ich versuche es so lange, bis ich es geschafft habe.

    mein jetztiger code:

    mov eax,50
        add eax,20
        mov ergebnis, eax
        mov si, ergebnis
        call print_string
        jmp loop_start  
    
    .
    .
    .
    .
    
    ergebnis db 0
    

    Allerdings scheint in dieser zeile:
    mov ergebnis, eax
    etwas falsch zu sein.
    denn diese fehlermeldung kommt: invalid combination of opcode and operands
    aber das müsste eigentlich richtig sein!
    ich kopiere den inhalt von eax nach ergebnis
    falsch?
    oder kann ich auch direkt:
    mov si, eax

    PS: ich bin lernbereit für ALLES!



  • lmb schrieb:

    ich versuche es so lange, bis ich es geschafft habe.

    Bewundernswerte Einstellung. Waere aber sicher effizienter, wenn du dir Methoden aneignest, deinen Code selbst zu verifizieren und zu testen. 🙂

    Zu deinem Code:
    In NASM meint

    mov eax, ergebnis
    

    idR. in etwa "kopiere die Offset-Adresse des labels 'ergebnis' nach eax".
    Also nicht das, was du hier willst und andersherum funktioniert das natuerlich gar nicht, da das offset eines labels eine konstante, beim Assemblieren bestimmte Zahl ist und in konstante Zahlen kannst du nichts reinschreiben. (vgl. mov 15, eax).
    Um in Intel-Syntax Speicherzugriffe zu machen, packt man idR. eckige Klammern um ein Label oder eine Adresse.
    zB.

    mov eax, [ergebnis]
    

    liest vom Speicher (bzw. der "Variable") beim Label "ergebnis".

    Die Prozedur PrintString erwartet nun in si einen Zeiger (hier Offset-Adresse) auf einen 0-terminierten String. Was du da nach der Addition in "ergebnis" kopieren willst, ist jedoch weder 0-terminiert noch ein String, sondern einfach ein integer - kannst du so also nicht ausgeben. Vorher musst du deinen integer in einen string konvertieren. Dazu bietet dir Erhards code im RealMode AFAIK aber keine Funktionen. ... Die muesstest du dir also auch erstmal selbst schreiben. Zum Thema wie das funktioniert gab es vor nicht all zu langer Zeit 3 Themen, die sich eigentlich noch irgendwo unter den ersten 5 Foren-Seiten hier finden sollten.



  • Nobuo T schrieb:

    lmb schrieb:

    ich versuche es so lange, bis ich es geschafft habe.

    Bewundernswerte Einstellung. Waere aber sicher effizienter, wenn du dir Methoden aneignest, deinen Code selbst zu verifizieren und zu testen. 🙂

    Ja, ich versuche was ich kann.

    Nobuo T schrieb:

    Zu deinem Code:
    In NASM meint

    mov eax, ergebnis
    

    idR. in etwa "kopiere die Offset-Adresse des labels 'ergebnis' nach eax".
    Also nicht das, was du hier willst und andersherum funktioniert das natuerlich gar nicht, da das offset eines labels eine konstante, beim Assemblieren bestimmte Zahl ist und in konstante Zahlen kannst du nichts reinschreiben. (vgl. mov 15, eax).

    Ich hatte es doch genau andersherum, also:

    mov ergebnis, eax
    

    Nach meinem Verständniss müsste das heissen:
    Kopiere den Inhalt von eax nach ergebnis
    Aber nach diesen Aussagen:

    Nobuo T schrieb:

    Um in Intel-Syntax Speicherzugriffe zu machen, packt man idR. eckige Klammern um ein Label oder eine Adresse.
    zB.

    mov eax, [ergebnis]
    

    liest vom Speicher (bzw. der "Variable") beim Label "ergebnis".

    müsste es

    mov [ergebnis], eax
    

    heissen.

    Nobuo T schrieb:

    Die Prozedur PrintString erwartet nun in si einen Zeiger (hier Offset-Adresse) auf einen 0-terminierten String. Was du da nach der Addition in "ergebnis" kopieren willst, ist jedoch weder 0-terminiert noch ein String, sondern einfach ein integer - kannst du so also nicht ausgeben. Vorher musst du deinen integer in einen string konvertieren. Dazu bietet dir Erhards code im RealMode AFAIK aber keine Funktionen. ... Die muesstest du dir also auch erstmal selbst schreiben. Zum Thema wie das funktioniert gab es vor nicht all zu langer Zeit 3 Themen, die sich eigentlich noch irgendwo unter den ersten 5 Foren-Seiten hier finden sollten.

    Gut, danach werde ich jetzt gleich mal suchen 😉 (ich meine, als ich eben gestöbert habe, habe ich das thema gesehen)
    Aber erstmal der jetzige Code:

    mov eax,50
        add eax,20
        mov [ergebnis], eax
        ;integer in string umwandeln
        mov si, ergebnis
        call print_string
        jmp loop_start  
    
    .
    .
    .
    .
    
    ergebnis db 0
    

    EDIT:
    Oh, das sieht komplizierter aus.
    Ich habe aber auch noch das hier gefunden:
    http://www.softgames.de/forum/viewtopic.php?t=3308
    was sagt ihr dazu?
    sieht schwer nach nasm aus, heisst aber pure asm



  • Deinen String kannst du natuerlich nicht in das eine Byte bei "ergebnis" stopfen.
    Dafuer musst du dir einen anderen, ausreichend grossen Speicherbereich anlegen. Entweder auf dem Stack (waere wahrscheinlich am praktischsten) oder irgendwo im Speicher wo sicher nichts anderes steht.

    Zu dem verlinkten Code: Das ist zwar Intel-Syntax, sieht NASM deshalb aehnlich, verwendet aber sonst keine NASM-Typische Syntax.
    Besonders schoen ist der Code zudem auch nicht. Mach dir besser erstmal das Prinzip hinter der Umwandlung klar und versuche dann dir deinen eigenen Code zu basteln. c&p wuerde ich diesen Code nicht.



  • so, ich bin jetzt soweit, dass überprüft wird, ob eax = 0 ist (nach deiner anleitung)
    ich weiss aber auch nicht, durch welche basis es geteilt werden soll

    schleife:
    
                                    cmp eax,0
                                    jne ungleich
                    gleich:
                                    jmp weiter
                    ungleich:
                                    ; eax durch basis teilen; doch welche basis?
                                    jmp schleife
                    weiter:         jmp ende_schleife
    
            loop schleife
            ende_schleife:
            ; hier sollte es weitergehen
    

    und dann komm ich nicht ganz klar mit der anleitung.
    ich soll ax, ax:dx oder eax:adx (eins der drei wird automatisch ausgewählt) durch den divisor teilen
    aber durch welchen divisor?



  • Kannst du den Thread nochmal verlinken?

    Ok, dir ist aber schon so weit ueberhaupt klar, was du dort weshalb auf 0 pruefst?
    Dividieren tust du dann natuerlich durch die Basis des Zahlensystems, in dem du deine Zahl in den String schreiben willst. Das Dezimalsystem hat die Basis 10, hexadezimal die Basis 16, Binaer die Basis 2, usw. Ein System erkennbar? 😉

    Deine Schleife ist uebrigens noch ziemlich verbesserungsfaehig:
    eax pruefst du sinnvollerweise erst nach der Division am Ende der Schreife.
    Wozu soll der doppelte Sprung ueber "weiter" gut sein?
    Wozu das "loop schleife"? Weist du ueberhaupt, was der loop-Befehl tut?

    lmb schrieb:

    und dann komm ich nicht ganz klar mit der anleitung.

    Bin um aufklaerung und Verbesserung bemueht, dann taugt der Text vielleicht auch irgendwann mal fuer die FAQ. 🙂

    lmb schrieb:

    ich soll ax, ax:dx oder eax:adx (eins der drei wird automatisch ausgewählt) durch den divisor teilen
    aber durch welchen divisor?

    Naja, welche Register als Dividend genommen werden, haengt davon ab, wie breit der Divisor (Operand von div fuer unsigned, bzw. idiv fuer signed) ist. Fuer Division durch 8 brauchst du 16Bit Eingang (ax), fuer 16Bit-Divisor 32Bit Dividend (dx:ax), usw.
    Der Divisor ist die oben erwaehnte Basis.



  • http://www.c-plusplus.net/forum/viewtopic-var-t-is-236735-and-start-is-0-and-postdays-is-0-and-postorder-is-asc-and-highlight-is-integer.html

    nein ich weiss das nicht.
    das stand nirgends.
    mein integer ist eine stinknormale rationale zahl, also dezimalsystem.
    also ist meine basis 10

    eax pruefst du sinnvollerweise erst nach der Division am Ende der Schreife.

    Ja aber dann kann ich doch nicht vergleichen, oder soll ich 2mal?

    Wozu soll der doppelte Sprung ueber "weiter" gut sein?

    wie meinst du das und wo?

    Wozu das "loop schleife"? Weist du ueberhaupt, was der loop-Befehl tut?

    hab ich übersehen, sorry



  • lmb schrieb:

    nein ich weiss das nicht.
    das stand nirgends.

    Was du auf 0 pruefen sollst, steht doch klar im 2. Beitrag: Den Quotient, und den bekommst du wie der Name schon sagt erst als Ergebnis der Division. 😉
    Warum erschliesst sich dir vielleicht, wenn du darueber nachdenkst, dass das wiederholte Dividieren von 0 immer nur 0 ergeben wird und damit keine weiteren brauchbaren Ziffern deiner Zahl. Also eine nahe liegende Abbruchbedingung.
    Ka, wie man das sonst klarer formulieren sollte...

    Hm, vielleicht wird an einem kleinen Beispiel deutlicher, wie diese Schleife mit der Division funktionieren soll:

    Nehmen wir an, du hast die Zahl 123. Um die nun als String darstellen zu koennen, musst du irgendwie nacheinander an die einzelnen Ziffern herankommen.
    123 kannst du auch schreiben als 3 * 10^0 + 2 * 10^1 + 1 * 10^2 + 0 * 10^3 + ...
    (123 hat hier in dieser dezimalen Darstellung die Basis 10).

    Also was macht man, um die Ziffern zu bekommen? Division mit Rest (Modulo) durch 10!
    1. 123 mod 10 = 3; 123 / 10 = 12; 12 = 0? -> nein: weiter
    2. 12 mod 10 = 2; 12 / 10 = 1; 1 = 0? -> nein: weiter
    3. 1 mod 10 = 1; 1 / 10 = 0; 0 = 0? -> ja: fertig

    Der div-Befehl liefert dir nun wie gesagt sowohl Den Rest als auch den Quotient.

    lmb schrieb:

    mein integer ist eine stinknormale rationale zahl, also dezimalsystem.
    also ist meine basis 10

    Integer stellen nur ganze Zahlen dar, keine rationalen Zahlen.
    Wie du die Zahl darstellst (dezimal, hexadezimal, whatever) hat ansonsten nichts mit dem Zahlensystem zu tun.
    Du koenntest deinen Integer auch als Dualzahl, hex, oktal - eigentlich mit jeder beliebigen Basis darstellen.
    Dezimal 123 von oben ist hexadezimal zB. 7B (= (B = 12) * 16^0 + 7 * 16^1).

    lmb schrieb:

    eax pruefst du sinnvollerweise erst nach der Division am Ende der Schreife.

    Ja aber dann kann ich doch nicht vergleichen, oder soll ich 2mal?

    Weshalb kannst du den Quotient nach der Division nicht mit 0 vergleichen?

    lmb schrieb:

    Wozu soll der doppelte Sprung ueber "weiter" gut sein?

    wie meinst du das und wo?

    jmp weiter    ; z.6
    ;...
                    weiter:         jmp ende_schleife    ; z.10
    

    Erscheint mir wenig sinnvoll. 😃



  • Nobuo T schrieb:

    lmb schrieb:

    nein ich weiss das nicht.
    das stand nirgends.

    Was du auf 0 pruefen sollst, steht doch klar im 2. Beitrag: Den Quotient, und den bekommst du wie der Name schon sagt erst als Ergebnis der Division. 😉

    Was ich auf 0 prüfen soll, ist mir klar, aber:

    Nobuo T schrieb:

    Warum erschliesst sich dir vielleicht, wenn du darueber nachdenkst, dass das wiederholte Dividieren von 0 immer nur 0 ergeben wird und damit keine weiteren brauchbaren Ziffern deiner Zahl. Also eine nahe liegende Abbruchbedingung.
    Ka, wie man das sonst klarer formulieren sollte...

    das hier ist mir erst jetzt klar geworden :D. Naja, vielleicht war ich zu abgelenkt

    Nobuo T schrieb:

    Hm, vielleicht wird an einem kleinen Beispiel deutlicher, wie diese Schleife mit der Division funktionieren soll:

    Nehmen wir an, du hast die Zahl 123. Um die nun als String darstellen zu koennen, musst du irgendwie nacheinander an die einzelnen Ziffern herankommen.
    123 kannst du auch schreiben als 3 * 10^0 + 2 * 10^1 + 1 * 10^2 + 0 * 10^3 + ...
    (123 hat hier in dieser dezimalen Darstellung die Basis 10).

    Also was macht man, um die Ziffern zu bekommen? Division mit Rest (Modulo) durch 10!
    1. 123 mod 10 = 3; 123 / 10 = 12; 12 = 0? -> nein: weiter
    2. 12 mod 10 = 2; 12 / 10 = 1; 1 = 0? -> nein: weiter
    3. 1 mod 10 = 1; 1 / 10 = 0; 0 = 0? -> ja: fertig

    Der div-Befehl liefert dir nun wie gesagt sowohl Den Rest als auch den Quotient.

    genau und zwar wird der Rest in dx und der Quotient in ax gespeichert.

    Nobuo T schrieb:

    Weshalb kannst du den Quotient nach der Division nicht mit 0 vergleichen?

    Naja, es wirt eax auf 0 geprüft, wenn das nicht so ist, springt er zu ungleich, ansonsten geht er weiter zu gleich.
    in gleich wird über ungleich auf weiter gesprungen. in ungleich soll geteilt werden und dann wieder geprüft werden.
    also muss ich theoretisch 2mal prüfen, 1mal am anfang, einmal bei ungleich nach dem dividieren.

    Nobuo T schrieb:

    jmp weiter    ; z.6
    ;...
                    weiter:         jmp ende_schleife    ; z.10
    

    Erscheint mir wenig sinnvoll. 😃

    Da hast du wohl recht 🤡



  • lmb schrieb:

    Naja, es wirt eax auf 0 geprüft, wenn das nicht so ist, springt er zu ungleich, ansonsten geht er weiter zu gleich. in gleich wird über ungleich auf weiter gesprungen. in ungleich soll geteilt werden und dann wieder geprüft werden.
    also muss ich theoretisch 2mal prüfen, 1mal am anfang, einmal bei ungleich nach dem dividieren.

    Vielleicht entfernst du dich besser gedanklich erst nochmal von deiner bisher gebastelten Schleife...
    Es soll nicht eax auf 0 geprueft werden, sondern der Quotient. Der steht nach der Division nur gerade in eax, vor der Division hast du also gar nichts zum Pruefen.
    Eine einzige Ueberpruefung am Ende der Schleife (quasi do{ ... } while() statt while() { }) und mit dieser einen Ueberpruefung auch nur ein bedingter Sprungbefehl, mehr brauchst du im Prinzip nicht, um diese Schleife zu basteln.
    Keine labels "gleich", "ungleich", "weiter" und auch keine tausend jmps.



  • dann meinst du bestimmt sowas hier:

    loop:
      div
      cmp eax,0
      jne loop
      ;hier gehts weiter
    


  • 👍



  • Jaaaaaaaa!
    Danke!
    Jetzt habe ich die ganze Geschichte verstanden!
    Es gibt in Assembler keine Kommazahlen, deswegen wird der Rest in dx gespeichert!
    Und ich speichere die Ziffern nacheinander in einer Variable.
    Diese wird am Ende umgedreht, sonst habe ich statt 123 halt 321.
    Jetzt schnall ich das!
    Danke!
    Und jetzt kann ich auch wieder vernünftig schreiben, habe gerade meinen Gips bis zur Schulter abbekommen!
    Ich versuch mich mal weiter dran.

    EDIT://
    Ich bin nirgendwo fündig geworden.
    Wie kann ich einem String einen weiteren String hintendran (oder besser vornedran) fügen?
    Unter dem Suchwort "String Manipulation" gab es nichts tolles...

    ;int eax in string umwandeln
            loop:
            div 10                                   ;eax/10  (Ergebnis wird in ax, Rest in dx gespeichert)
            cmp eax,0                                ;eax=0?
            jne loop                                 ;wenn eax != 0, gehe zu loop
    
            ;hier gehts weiter
    

Anmelden zum Antworten