MASM nach NASM übersetzen ...



  • Hallo,

    ich habe einen 16-Bit Code in dem es mir schwer fällt die Implementation einer Jump Table für Routinen / Variablen umzusetzen.

    Damit dass hier nicht ausufert habe ich das ganze Problem mal auf eine sehr einfache 32-Bit Funktion abstrahiert.

    Erst mal der von mir gewählte MASM-Code:

    TITLE jump_table.asm - beispielcode
    
    		.686P
    		.MODEL FLAT
    
    EXTERN _gets:PROC
    EXTERN _printf:PROC
    
    PUBLIC _TestFunc
    
    .DATA
    	buffer   dq 8 dup(0)
    	message1 db 'Message 1', 0Dh, 0Ah, 0
    	message2 db 'Message 2', 0Dh, 0Ah, 0
    	message3 db 'Message 3', 0Dh, 0Ah, 0
    	message4 db 'Message 4', 0Dh, 0Ah, 0
    	message5 db 'Message 5', 0Dh, 0Ah, 0
    	msgError db 'unknown command', 0Dh, 0Ah, 0
    
    	JumpTable LABEL DWORD
    		dd message1, message2, message3, message4, message5
    
    .CODE
    	_TestFunc PROC
    		push ebp
    		mov  ebp, esp
    
    		push OFFSET buffer
    		call _gets
    		add  esp, 4
    		movzx eax, BYTE PTR [buffer]
    		cmp  al, 48
    		jb	 error
    		cmp  al, 52
    		ja   error
    
    		sub  al, 48
    		mov  edx, DWORD PTR [JumpTable + (eax*4)]
    		push edx
    		jmp  done
    
    error:
    		push OFFSET msgError
    done:
    		call _printf
    		add  esp, 4	
    
    		pop  ebp
    		ret		
    	_TestFunc ENDP
    
    END
    

    Funktioniert wunderbar. Der User kann in der Kommandozeilen die Werte 0-4 eingeben und erhält danach die Ausgabe "Message n" bzw. eine Fehlermeldung wenn der Range nicht getroffen wurde.
    Ich setze hier nur für die MessageVariablen die Jump Table (letzt endlich ein Array von Pointern auf die entsprechende Nachricht) ein. Ist aber auch egal, bei mehreren addiert sich die Problematik halt.

    Hier mein, erfolgloser, Versuch das ganze mit NASM zu realisieren:

    [BITS 32]
    
    EXTERN _gets
    EXTERN _printf
    
    GLOBAL _TestFunc
    
    SEGMENT .DATA
    	buffer   times 8 dq 0
    	message1 db 'Message 1', 0x0D, 0x0A, 0
    	message2 db 'Message 2', 0x0D, 0x0A, 0
    	message3 db 'Message 3', 0x0D, 0x0A, 0
    	message4 db 'Message 4', 0x0D, 0x0A, 0
    	message5 db 'Message 5', 0x0D, 0x0A, 0
    	msgError db 'unknown command', 0x0D, 0x0A, 0
    
    	JumpTable dd message1, message2, message3, message4, message5
    
    SEGMENT .CODE	
    	_TestFunc:
    		push ebp
    		mov  ebp, esp
    
    		lea  eax, [buffer]
    		push eax
    
    		call _gets
    		add  esp, 4
    		xor  eax, eax
    		mov  al, [buffer]
    		cmp  al, 48
    		jb	 error
    		cmp  al, 52
    		ja   error
    
    		sub  al, 48
    		mov  edx, [JumpTable + (eax*4)]
    		push edx
    		jmp  done
    
    error:
    		lea  eax, [msgError]
    		push eax
    done:
    		call _printf
    		add  esp, 4	
    
    		pop  ebp
    		ret
    

    Kompilieren tut es in dieser Version. Jedoch liefert die C-Funktion char* gets(char*) schon den ersten Fehler. Da als einzige Manipulation der Übergabeparameter erfolgt, gehe ich davon aus, dass eine inkorrekte Adresse übergeben wird; insbesondere da der "Fehler" eine Access Violation ist.

    Beim 16-Bit Code haben sich zusätzliche Probleme aufgetan. Und zwar mag NASM folgenden Code nicht:

    [BITS 16]
    
    SEGMENT .DATA
    	MyVarA db 0
    	MyVarB db 1
    	MyVarC db 2
    
    	JumpTable dw MyVarA, MyVarB, MyVarC
    
    SEGMENT .CODE
    	TestFunc:
    		mov cx, 2
    		mov di, [JumpTable + (cx*2)]
    
    		ret
    

    Wenn ich die Zeile:

    mov di, [JumpTable + (cx*2)]
    

    durch:

    movzx edi, [JumpTable + (ecx*2)]
    

    ersetze,
    dann kompiliert er korrekt. Dass ist aber nicht wirklich im Sinne des Erfinders und kann die Lösung nicht sein.
    Der genaue Compiler-Error ist "invalid effective address", was ich in Angesicht eines 16 Bit Codes nicht nachvollziehen kann.

    Parameter sind "nasm source.asm -f aout -o source.o"

    Hoffe sehr, dass ihr mir bei diesem offensichtlichen Adress-Problem helfen könnt.



  • hi,

    FrEEzE2046 schrieb:

    mov di, [JumpTable + (cx*2)]
    

    durch:

    movzx edi, [JumpTable + (ecx*2)]
    

    ersetze,
    dann kompiliert er korrekt. Dass ist aber nicht wirklich im Sinne des Erfinders und kann die Lösung nicht sein.
    Der genaue Compiler-Error ist "invalid effective address", was ich in Angesicht eines 16 Bit Codes nicht nachvollziehen kann.

    die 16 und 32 Bit Addressierung unterscheidete sich - In diesem Bild (Wikipedia) werden die Möglichkeiten der 16Bit Addressierung gezeigt.



  • masm schrieb:

    die 16 und 32 Bit Addressierung unterscheidete sich

    Hi,

    ich kenne den Unterschied zwischen der Addressierung im Real und im Protected Mode. Dennoch tritt der Fehler halt auch bei expliziter Segment Angabe auf.



  • 'mov di, [JumpTable + (cx*2)]' geht aus 2 Gründen nicht:
    1. es gibt keine Skalierung
    2. CX kann nicht zur addressierung verwendet werden: nur BX,BP,DI,SI
    (sieh Bild für mögliche Kombinationen)

    Du musst die Berechnung 'von Hand' machen:

    z.B.:

    mov di,cx
    shl di,1
    mov di,[di+JumpTable]
    


  • Ack. Das liegt auch nicht am NASM.

    Was ansonsten die Schmutzverletzung angeht, solltest du vielleicht mal genau untersuchen, was nach dem Linken beim Aufruf der externen Funktionen eigentlich an Code generiert wird. Ich hatte da zumindest mit verschiedenen Linkern zT. recht eigenartige Resultate, wie Aufrufe in extra generierte Jump-Tables.

    BTW:
    Das Offset einer Variable holst du dir in NASM mit

    Var db 00
    
    ;...
    
    mov di, Var
    


  • masm schrieb:

    z.B.:

    mov di,cx
    shl di,1
    mov di,[di+JumpTable]
    

    Mir ist die Adressierung über Segment:Offset durchaus bekannt. Dein Code kann aber so nicht funktionieren, würde ich sagen.

    Angenommen:
    Segment = 0x1000
    Offset = 0x0040

    Dann wäre die physikalische Adresse:
    0x1000 << 1 + 0x0040 = 0x10000 + 0x0040 = 0x10040

    Du würdest hier aber die "1" von 0x1000 aus dem Register "rausschieben" (shl di, 1)

    @Nobuo T
    Du sprichst wahrscheinlich das wegen dem "lea" an oder? Hab ich nur gemacht, weil ich den Grund für Access Violation gesucht habe. Der springt bei den Aufrufen von _gets und _printf an vollkmommen falsche Stellen (stellenweiße im Bereich von _main)



  • FrEEzE2046 schrieb:

    Mir ist die Adressierung über Segment:Offset durchaus bekannt. Dein Code kann aber so nicht funktionieren, würde ich sagen.
    [......]

    Oha. Da geht aber einiges ordentlich daneben. Vielleicht willst du dein Verstaendnis von logischen Operationen und der "Adressierung über Segment:Offset" nochmal ueberdenken?

    FrEEzE2046 schrieb:

    Der springt bei den Aufrufen von _gets und _printf an vollkmommen falsche Stellen (stellenweiße im Bereich von _main)

    Jo, das mit dem offset habe ich wegen deinem lea erwaehnt. Benutzt man ansonsten so eher nicht.
    Die Umsetzung des Codes sieht vom Prinzip sonst aber IMHO korrekt aus. Ich wuerde also darauf tippen, dass dein Problem irgendwo bei deiner Wahl des Ausgabeformats von NASM, der korrekten Einbettung deines Codes darin (Korrekte Segmentanordnung, Orgs, usw.) oder beim Linken liegt.



  • @Nobuo T

    Okay, mein Beispiel wegen dem Bitshifting war quatsch, aber dennoch sehe ich die Problematik.

    Kannst du mir das vielleicht näher erläutern, was bei mir "gründlich daneben geht"?
    Wenn du ein 16 Bit Register einem bitshift nach links um 1 vollziehst, dann fällt der Wert von Bit 0 komplett weg nnd die Wertigkeit der anderen Bits verändert sich um x * 2^(n-1)

    Was ist daran falsch?



  • Ok, dann also Stueck fuer Stueck:

    Im Prinzip hast du recht, dass es beim shiften (wie beim Multiplizieren, Dividieren, Addieren, usw.) zu einem Ueberlauf kommen kann.
    Das ist zB. bei einem Linksshift dann der Fall, wenn das MSB (most significant bit = Bit mit hoechster Wertigkeit = idR. bit n-1 -> bei n=16 Bit also Bit 15) 1 ist und dann quasi hinten rausfaellt.
    Sollte hier aber keine grosse Rolle spielen, da du zum einen das selbe Problem auch haettest, wenn du so etwas schreiben koenntest wie "mov ax, [di*2]" und
    man idR. diesen Fall eh abfangen sollte, indem man vor der Berechnung der Adresse in ein Array prueft, ob der Index die Arraygrenzen (oder eben Adressierungsgrenzen) ueberschreitet.

    Dann nochmal zur Adressierung im RM in deinem Beispiel:

    Angenommen:
    Segment = 0x1000
    Offset = 0x0040

    Dann wäre die physikalische Adresse:
    0x1000 << 1 + 0x0040 = 0x10000 + 0x0040 = 0x10040

    Das shifting selbst ist falsch, wie du schon festgestellt hast.
    Dann kommt hinzu, dass du das offset skalieren/shiften solltest, nicht den Segmentanteil (den laesst man falls moeglich immer konstant).
    Ausserdem wird eine physikalische Adresse aus Segment:Offset im RM wie folgt gebildet:
    pAddr = Segment * 16 + Offset

    Dh. dein Beispiel muesste korrekt so lauten:

    pAddr = (0x0040 << 1) + (0x1000 << 4) = 0x10080



  • Sorry,

    dass:

    0x1000 << 1 + 0x0040 = 0x10000 + 0x0040 = 0x10040
    

    soll natürlich:

    0x1000 << 4 + 0x0040 = 0x10000 + 0x0040 = 0x10040
    

    heißen. Wie du sieht, hatte ich aber trotzdem korrekt gerechnet; war nur ein Tippfehler.
    Wir reden etwas aneinander vorbei. Was ich hier kritisiere ist, dass du das Offset um 1 Bit nach links verschiebst, weil ich mir die Frage nach dem "warum" stelle.

    EDIT:
    Ich gehe davon aus, dass ihr das "shl di, 1" auf meine Multiplikation "* 2" bezieht. Ist jedoch Quatsch. Ich habe das gemacht, da ich ein Pointer-Array habe, wobei jeder Pointer im 16-Bit Mode 2 Byte groß ist. In "cx" lag bei mir der Index dieses Arrays, daher die Multiplikation.

    Warum ihr dass jetzt als Faustregel in die normale Berechnung der physikalischen Adresse im Real Mode reingehauen habt, verstehe ich nicht.

    phys. (lineare) Adresse = Segment * 16 + Offset = Segment << 4 + Offset
    


  • Hier mal wie sich eine Adresse zusammensetzt (20Bit)

    [Segmentselektor: CS,DS,SS,...] 
       |15        |11                |0
    
                  [Offset: Register und/oder imm16]
    +             |15                             |0
    --------------------------------------------------
       [             20 Bit Adresse               ]
       |20                                        |0
    

    Wenn man also die echte Adresse ausrechnen will, muss man den Inhalt des Segmentregisters um 4 nach links shiften und dann das Offset dazu addieren:
    p = ([CS,DS,SS...] << 4) + Offset

    Das Offset kann sowohl durch ein Register/Registerkombination und/oder einem 16Bit immediate (Zahlenwert)gegeben sein.
    Hieraus ergibt sich im Übrigen auch, dass man Ein und die Selbe Adresse durch verschieden paare von Segmentregister und Offset Bilden kann.



  • @masm
    Nicht böse gemeint, aber habe ich dem in irgendeiner Art und Weise widersprochen?



  • da hab ich deinen letzten Beitrag falsch verstanden 🙂



  • FrEEzE2046 schrieb:

    phys. (lineare) Adresse = Segment * 16 + Offset = Segment << 4 + Offset
    

    Macht für mich halt nur keinen Sinn, warum in den Beispielen von euch immer das Offset shl 1 gemacht wurde.
    Aber da habt ihr euch wohl von meiner Umrechnung vom Index auf die 16-Bit Adresse aus meinem Beispeil irre machen lassen.



  • Ok, ich sehe ein es war ein Fehler, dein Beispiel aufzugreifen, da es offenbar nur noch mehr Verwirrung gestiftet hat.

    Die Formel zur Berechnung der physikalischen Adresse im RM hast du da jetzt korrekt gepostet.

    Zu der Sache mit dem shift bleibt sonst nur nochmal festzuhalten, dass es in 16Bit-Adressen keine Registerskalierung (*2, *4, etc.) oder -summanden gibt und du deshalb in so einem Fall entsprechende Berechnungen extra ausfuehren musst:

    mov ax, [di * 2] ; Skalierung funktioniert mit 16Bit-Adressen nicht!
    
    ;stattdessen aequivalent:
    shl di, 1
    mov ax, [di]
    

    Das hat dir "masm" bereits ganz zu Anfang geschrieben, worauf dein eigenartiges Beispiel folgte...



  • Nobuo T schrieb:

    worauf dein eigenartiges Beispiel folgte...

    Okay, ich habe ein falsches Beispiel gepostet (bzgl shl 1, statt 4) und du hast die Berechnung im Real Mode falsch beschrieben. Es ist schließlich nicht vorgesehen den Offset immer * 2 (shl 1) zu rechnen. Dass hatte im übrigen nichts mit meinem Beispiel von oben zu tun, wo ich den Index auf die Adresse (16-Bit) anpassen musste.
    Irgendwo haben wir doch eh das selbe gemeint, aber hatten Schwierigkeiten es auch so auszudrücken. Thema erledigt.



  • FrEEzE2046 schrieb:

    du hast die Berechnung im Real Mode falsch beschrieben.

    Wo denn? 😕



  • Nobuo T schrieb:

    Wo denn? 😕

    Hier:

    Nobuo T schrieb:

    Dh. dein Beispiel muesste korrekt so lauten:
    pAddr = (0x0040 << 1) + (0x1000 << 4) = 0x10080

    Mein Beispiel war:

    FrEEzE2046 schrieb:

    Angenommen:
    Segment = 0x1000
    Offset = 0x0040



  • FrEEzE2046 schrieb:

    Nobuo T schrieb:

    Wo denn? 😕

    Hier:

    Nobuo T schrieb:

    Dh. dein Beispiel muesste korrekt so lauten:
    pAddr = (0x0040 << 1) + (0x1000 << 4) = 0x10080

    Mein Beispiel war:

    FrEEzE2046 schrieb:

    Angenommen:
    Segment = 0x1000
    Offset = 0x0040

    Da hast du wohl was falsch verstanden.
    Das ist, wie im zitierten Beitrag steht, meine Interpretation deines Beispiels (warum postest du eigentlich ueberhaupt nur diesen kleinen nichtssagenden Ausschnitt desselben?)?
    Moeglich, dass ich deine Frage mit dem Beispiel falsch interpraetiert habe, allerdings habe ich nirgendwo geschrieben, dass der Teil, den du hier zitierst, die allgemeine Formel fuer die physikalische Adressberechnung im RM sei - die steht naemlich direkt darueber.



  • @ Nobuo T

    Du hast einfach die beiden Beispiele zusammen "gewurschtelt". Daher auch die Missverständnise.
    Die Formel für die Berechnung der phys. Adresse hast du darüber richtig gepostet, dass stimmt natürlich.


Anmelden zum Antworten