我一直在自学如何完成装配中的某些任务。
现在,我正在尝试检测回文。我知道我可以使用堆栈,或者可能使用Irvine的库比较字符串,但我试图通过寄存器来完成。
问题是,当涉及到使用寄存器时,我有点困惑。
下面的代码可以编译,但是当我到达CMP行时,程序中断并给我以下消息:
Project.exe中0x004033FC处未处理的异常:0xC0000005:访问冲突阅读位置0x000000F。
我假设这与我如何设置寄存器有关,但即使在调试时使用寄存器也没有多大帮助。
如果你能帮忙的话,我将不胜感激。
INCLUDE Irvine32.inc
.data
enteredWord BYTE "Please enter the string to check: ", 0
presetWord BYTE "Step on no pets", 0
isAPalindrome BYTE "The word is a palindrome. ", 0
isNotAPalindrome BYTE "The word is not a palindrome. ", 0
.code
main proc
mov ecx, SIZEOF presetWord - 1
mov esi,OFFSET presetWord
checkWord:
MOV eax,[esi]
CMP [ecx],eax
JNE NOTPALIN
inc esi
dec ecx
loop checkWord
mov edx, offset isAPalindrome
call WriteString
jmp _exit
main endp
NOTPALIN PROC
mov edx, offset isNotAPalindrome
call WriteString
ret
NOTPALIN endp
_exit:
exit
end main
字符串
1条答案
按热度按时间20jt8wwn1#
CPU寄存器是直接位于CPU核心内的计算机内存。计算机内存意味着一定数量的位(0/1),在64 b x86 CPU的情况下,通用寄存器是64位宽,名为
rax, rcx, rdx, rbx, ..
。ecx
是rcx
的下32 b部分(上32 b部分不能用特殊名称访问,只能通过使用rcx
的指令访问)。下16 b部分可以通过cx
访问,cx
由两个8b部分ch
(上)和cl
(下)组成。因此,当你使用
ecx
时,你可以将32位设置为0或1。这可以解释为从0到232-1的无符号数(在十六进制0 .. 0xFFFFFFFF
中),或者从-231到+231-1的有符号数(0x80000000 .. 0x7FFFFFFF
)。或者你可以以任何你想要的方式解释这些位的含义,并编写代码。在你的代码中,你可以使用三种常见的方法来解释一些CPU寄存器中的位值。
字符串
在你的例子中,执行
cmp [ecx],eax
意味着在地址15处引用内存,幸运的是,这对你来说是非法的,所以它确实崩溃了。如果你偶然为你的进程使用了一些合法的地址(但不是你真正想要使用的地址),它将默默地继续下去,并继续下去,带来意想不到的结果。你可能确实想做
cmp [esi+ecx],eax
,这意味着引用地址为presetWord+15
(字符串的最后一个字符)的内存,但这只适用于第一次迭代。然后你做inc esi
,它将指向presetWord+1
地址(第二个字符)。您可能只想比较字符,所以您应该将
eax
更改为al
,以便一次只获取/比较单个字节,因为字符串是以ASCII编码(每个字符8位)编码的。为了检查回文,你可能想用第一个字符的地址加载一个寄存器(“r1”),用最后一个字符的地址(!)加载一个寄存器(“r2”),然后执行这个循环:
这将为presetWord生成“false”,如
'S' != 's'
,因此您可能希望对if (byte [r1]...
部分引入大小写不敏感性,但我首先会让它在没有大小写的情况下工作。在调试时,您应该能够识别寄存器中某些数字的“类”。如果您将大小加载到寄存器中,它很可能是一些小数字,例如
0000000F
(15)。地址很可能是像8040506E
这样的大数字。ASCII字符作为单个字符使用时应该导致像20
这样的东西-7F
,但是如果你执行mov al,...
,调试器仍然显示整个eax
,所以上面的三个字节将保持它以前的值,例如,阅读空格字符到eax
设置为12345678
将改变eax
的值为12345620
(ASCII中的空格' ' == 0x20
)。你也可以使用内存视图来检查内存中特定地址的内容。例如,如果你将
cmp
更改为cmp [esi+ecx],eax
,并在内存视图中检查该地址,你会看到它将再次指向第二次迭代的最后一个字符,而不是倒数第二个字符。这一切都是可见的,可以在调试器中检查,有时有点乏味,然后再一次往往比询问SO或只是思考源代码更容易,特别是如果你被卡住了更长的时间。
最后......为什么还要注册?因为计算机内存是独立的芯片。它可能看起来无辜,但像
mov al,[presetWord]
这样的指令实际上可能会停滞数百个CPU周期,而CPU芯片将等待内存芯片读取内存内容并通过总线将其发送到CPU芯片。而al
和ecx
直接位于CPU内部,当CPU需要时,可以在同一周期内访问它。因此,如果您在计算中经常使用值,则可能需要将值存储到寄存器中,以免内存变慢(虽然一旦内存内容被L0/1/2/3缓存,“数百”周期就变成了合理的数量,有时甚至是直接在CPU芯片上缓存级别的0周期)。(因此缓存可以预读),并且数量合理(缓存通常以16- 32 B到4- 8 k的大小工作)。如果你访问两个指令,比如16个不同的8 k内存页面,你可能会用完可用的缓存行,然后至少会有一个访问以完全停止为特征,等待读取真实的内存。