[MMX] Groß- in Kleinbuchstaben umwandeln



  • Ich soll mit Hilfe von MMX 16 chars von Groß- in Kleinbuchstaben umwandeln. Um nur einen Vergleich statt Zweien (c>='A' && c<='Z') zu benötigen soll folgende Äquivalenz angewendet werden (wir gratulieren zu obfuscated grade 3)...

    min('Z'+1+min_t-'A' > c+min_t-'A' ? 0xff : 0 , 'a'-'A')
    min_t := -128
    
    // ich wandle das mal möglichst fertig um:
    min(-102 > c+63 ? 0xff : 0, 32)
    
    -102 = 0x9A
    63 = 0x3F
    32 = 0x20
    

    Da es genau 16 chars (=128 bit) sind sollte das MMX ohne Schleife lösen können. Allerdings habe ich dazu noch ein paar Fragen:

    (1) die mmx-register sind 128 bit breit, wie schiebe ich da mit mov etwas rein? movq reicht ja nur für 64bit...habs trotzdem mal mit movq gemacht...

    # at&t-syntax (hoffe dass bei mmx dann auch src-dest gilt)
    
    movq %rdi, %xmm0 # Ist das notwendig, kann also mmx nur darauf arbeiten?
    # zu allen die 63 addieren
    movq $0x3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F %xmm8 # tut man das so?
    paddb %xmm8 %xmm0
    
    # jetzt die -102 irgendwo reinsetzen
    movq $0x9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A %xmm9 #sieht mir immer noch nach wtf-code aus
    
    # jetzt endlich spaßig vergleichen
    pcmpgtb %xmm0 %xmm9 # Vergleich durchführen
    
    # jetzt das minimum machen, also wieder zuerst was reinschreiben
    movq $0x20... %xmm10
    pminsb %xmm9 %xmm10
    
    # und jetzt steht eh schon der wert der zu addieren ist in xmm10
    paddb %xmm10 %xmm0
    
    # und wieder zurückgeben
    movq %xmm0 %rax #hier macht es ja boom in rax, wie kann ich denn das nun überhaupt wieder byteweise rauskopieren in den string...doch schleife??
    

    Außerdem ist der String der da reinkommt ja eine adresse...ich sollte die ja zuerst auflösen bevor ich sie da reinkopiere...

    Wie ihr seht habe ich an der Schnittstelle MMX<->Normales ASM noch Probleme. Außerdem interessiert es mich natürlich ob mein Ansatz überhaupt so funktionieren kann...

    Auf etwas Unterstützung hoffend...

    MfG SideWinder



  • hi,

    du meinst wohl SSEx(=16bit Reg.) und nicht MMX(64Bit Reg.). Glücklicherweise kann man aber sowohl bei den SSEx Befehlen als auch bei den MMX befehlen die xmm-Register verwenden (mmx Reg. gehen auch). Kannst dich ja mal bei Wiki über die Geschichte schlau machen 😉

    Bei der Verwendung von SIMD Befehlen ist das aligment sehr wichtig (Speicheroperanten müssen align 16 sein) – Wenn die Daten nicht align 16 sind, geht eine Menge an Geschwindigkeit verloren. Bei Strings kommt noch das Problem dazu, dass man eventuell Daten hinter dem String-Ende mit bearbeitet. Dies kann man zwar umgehen, macht die Sache aber etwas komplizierter (pcmpeqb, pmovmskb und bsf sind hier bei dann Hilfreich).
    Hier mal eine einfache Variante die von davon ausgeht das ECX die Anzahl der 16 Byte Blöcke enthält und ESI der Zeiger auf den String ist (Intel-Systax,masm):

    .data
            align 16
                string db "HALLO WELT WIE AbCDefgHiJ xYZ"
    
            align 16 ; zwingend notwendig!
            msk_A   OWORD 40404040404040404040404040404040h
            msk_Z   OWORD 5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5Ah
            msk_add OWORD 20202020202020202020202020202020h
        .code
    
        mov esi,OFFSET string
        mov ecx,2
    
        ; ESI enthält Adresse des Strings. ECX die Anzahl der XMMWORDs.
        ; Damit die Sache auch effizient ist, muss die Adresse align 16 sein!
        ; (Wenn dem nicht so wäre, müsste man movdqu (u=unaligned) statt movdqa nehmen)
    
        align 8 ; optional ;)
    @loop:
        movdqa xmm0,OWORD ptr [esi]     ; lade 16 byte nach xmm0 (alternativ movdqu)
        movdqa xmm1,xmm0                
        movdqa xmm2,xmm0
    
        pcmpgtb xmm1,msk_A              ; vergleich x >= 'A'
        pcmpgtb xmm2,msk_Z              ; vergleich x > 'Z'
        pandn xmm2,xmm1                 ; (~xmm2)&xmm1
    
        pand xmm2,msk_add               ; alle Bytes die Großbuchstaben enthielten,
                                        ; sind jetzt mit 0xff gefüllt -> maskieren mit 0x20
    
        paddb xmm0,xmm2                 ; addiere 0x20 zu Großbuchstaben 
    
        movdqa OWORD ptr [esi],xmm0     ; Ergebniss sichern (alternativ movdqu)
        lea esi,[esi+16]
        dec ecx
        jnz @loop
    


  • Danke movdqa ist schon mal ein guter Befehl.

    Bei mir sind diese 16 chars ein fixer Wert. Also ich habe da keinen Dinge hinter dem String auf die ich aufpassen müsste. Auch sollte ich deshalb keine Loop brauchen.

    Danke jedenfalls schonmal, auch für die Hinweise mit dem Konstanten definieren. Ich denke das bringt mich mal weiter 🙂

    Edit:
    .code ist nichts anderes als .text?

    MfG SideWinder



  • SideWinder schrieb:

    .code ist nichts anderes als .text?

    Würde ich mal von ausgehen. Ist einfach der wechsel ins code-Segment.



  • Also mit Debug geht so eine Umwandlung ganz einfach, mit z.B.:

    or Al,20

    d.h.:
    z.B.
    0100 0001
    or
    0010 0000

    0110 0001

    Aber ich bin auch verwirrt: ich dachte MMX benutzt die maximal 80Bit breiten
    FPU Register und beschneidet sie auf 64bit inclusive Saturation/Wrap Around
    während 128Bit-Breite und breiter erst mit SSE möglich ist.
    Tut mir leid, falls die Antwort zu voreilig ist, habe selber noch nicht so viel
    mit >16 Bit zu tun, und ich brüte derzeit frustriert über die Schnittstelle zur Soundausgabe
    (für ein einfaches Morsezeichenproggi) ... 😞



  • derSchüler schrieb:

    ich dachte MMX benutzt die maximal 80Bit breiten
    FPU Register und beschneidet sie auf 64bit inclusive Saturation/Wrap Around
    während 128Bit-Breite und breiter erst mit SSE möglich ist.

    MMX benutz die untern 64 Bit der Fpu-Register - da wird nichts von 80 Bit runter gebrochen. Als SSE1 eingeführt wurde, hat Intel den MMX-Befehlen den Zugriff auf XMM-Register ermöglicht, und umgekehrt auch erlaubt mit den neuen SSE1 Befehlen auf die MMX Register zuzugreifen.


Anmelden zum Antworten