Assembly 代码在哪里可以更有效地检查输入字符是否为元音?
此部件项目读取按键并以特定颜色输出按键。当按下一个元音时,它会改变文本的颜色,直到按下另一个元音为止,直到按下ESC键为止。颜色是在一个特定的模式,这就是为什么我SUB 8时,它到达周期结束。我只是想让它更有效率。我试着把所有的比较语句排成一行,但没有成功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
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