Assembly 代码在哪里可以更有效地检查输入字符是否为元音?

Assembly 代码在哪里可以更有效地检查输入字符是否为元音?,assembly,x86,masm,irvine32,Assembly,X86,Masm,Irvine32,此部件项目读取按键并以特定颜色输出按键。当按下一个元音时,它会改变文本的颜色,直到按下另一个元音为止,直到按下ESC键为止。颜色是在一个特定的模式,这就是为什么我SUB 8时,它到达周期结束。我只是想让它更有效率。我试着把所有的比较语句排成一行,但没有成功 INCLUDE Macros.inc INCLUDE Irvine32.inc INCLUDELIB Irvine32.lib .386 .STACK 4096 ExitProcess PROTO, dwExitCod

此部件项目读取按键并以特定颜色输出按键。当按下一个元音时,它会改变文本的颜色,直到按下另一个元音为止,直到按下ESC键为止。颜色是在一个特定的模式,这就是为什么我SUB 8时,它到达周期结束。我只是想让它更有效率。我试着把所有的比较语句排成一行,但没有成功

INCLUDE        Macros.inc
INCLUDE     Irvine32.inc
INCLUDELIB  Irvine32.lib
.386
.STACK 4096
ExitProcess PROTO, dwExitCode:DWORD

.DATA
key       BYTE ?     
colorCode BYTE 5
max       BYTE 13

.CODE
main PROC

FindKey:
mov EAX, 50
call Delay

call ReadKey 
jz FindKey

MOV key, AL 
     cmp key, 75h
     JE UP
     CMP key, 6Fh
     JE UP
     CMP key, 69h
     JE UP
     CMP key, 65h
     JE UP
     CMP key, 61h
     JE UP
     CMP key, 55h
     JE UP
     CMP key, 4Fh
     JE UP
     CMP key, 49h
     JE UP
     CMP key, 45h
     JE UP
     CMP key, 41h
     JE UP
     CMP dx,VK_ESCAPE
     JE OVER

     COLOR:   
          MOVZX EAX, (black * 16) + colorCode
          CALL SetTextColor 
          MOV AL, key
          call WriteChar
          jmp FindKey

          UP: 
               CMP colorCode, 13
               JE RESET
               INC colorCode
               jmp COLOR

               RESET:
                    sub colorCode, 8
                    jmp COLOR    

     OVER:
     CALL Crlf
     INVOKE ExitProcess, 0

main ENDP
END main

如果您对高效的x86代码感兴趣,请参阅标记wiki中的链接。有很多好东西,尤其是阿格纳·福格的指南


AL
中有
key
,但
cmp
指令都使用内存操作数。对于
cmp-al,imm8
有一个特殊的操作码,因此
cmp-al,75h
只是一个2字节的指令。使用绝对位移来寻址
会产生更长的指令。而且,
cmp mem,imm
不能与条件跳转进行宏融合。每个insn都需要加载端口

代码的其余部分看起来似乎使用了太多内存操作数,并且出现了奇怪的缩进。(
UP
看起来像是
COLOR
块的一部分,但实际上在
COLOR
的末尾有一个无条件跳转,因此它不属于
UP


当然,由于所有的
je
目标都是相同的,所以一系列
cmp/je
远不是最佳的。您不需要找出实际匹配的键

您可以使用一种策略进行这样的检查,即查看
al
是否在正确的范围内,然后将其用作位图的索引。
编译器将此策略用于一个特定的应用程序。这就是为什么我们大多数时候使用编译器而不是手动编写asm:它们知道很多巧妙的技巧,并且可以在适用的地方应用它们。我们得到
1您对“高效”和其他情况的定义。例如,您可以使用一个查找表来确定它是否是元音。这将使代码更短,但更不可预测,当然会产生内存和缓存开销。您还可以使用SSE比较。首先使用位运算将字母转换为大写或小写也会减少比较。什么是SSE比较?使用SSE指令的矢量化比较。在这种情况下,
PCMPEQB
PTEST
很可能。我还没有听说过我班上的那些。。这是使用MASM和win32吗?@Patrick-SSE是一个x86指令集扩展,允许128位并行操作。是的,MASM和Windows支持SSE指令,几乎任何现代处理器都支持SSE指令。Jester意味着您可以通过并行执行来减少比较的数量。
    MOV   [key], AL    ; store for later use

    or    al,  20h     ; lowercase (assuming an alphabetic character)
    sub   al, 'a'      ; turn the ascii encoding into an index into the alphabet
    cmp   al, 'z'
    ja  .non_alphabetic

    mov   ecx, (1<<('a'-'a')) | (1<<('e'-a')) | (1<<('i'-a')) | (1<<('o'-a')) | (1<<('u'-a'))   ; might be good to pull this constant out and use an EQU to define it
    ; movzx eax, al    ; unneeded except for possible partial-register issues
    bt    ecx, eax      ; test for the letter being set in the bitmap
    jc  UP              ; jump iff al was a vowel
.non_alphabetic:
    CMP dx,VK_ESCAPE    ; this test could be first.
    JE OVER
    movzx ecx, al      ; avoid partial-reg stall or false dep on ecx that you could get with mov ecx,eax or mov cl,ca respectively
    mov   eax, 1
    shl   eax, cl      ; eax has a single set bit, at the index
    test  eax, 1<<('a'-'a') | 1<<('e'-a') | 1<<('i'-a') | 1<<('o'-a') | 1<<('u'-a')
    jnz  .vowel
    ; broadcast the key to all positions of an xmm vector, and do a packed-compare against a constant
    ; assuming  AL is already zero-extended into EAX
    imul    eax, eax, 0x01010101    ; broadcast AL to EAX
    movd    xmm0, eax
    pshufd  xmm0, xmm0, 0    ; broadcast the low 32b element to all four 32b elements
    pcmpeqb xmm0, [vowels]   ; byte elements where key matches the mask are set to -1, others to 0
    pmovmskb eax, xmm0
    test    eax,eax
    jnz   .vowel


section .rodata:
  align 16
  vowels: db 'a','A', 'e','E'
          db 'i','I', 'o','O'
          db 'u','U', 'a','a'
    times 4 db 'a'            ; filler out to 16 bytes avoiding false-positives