assembly 无法理解x86寄存器

igetnqfo  于 4个月前  发布在  其他
关注(0)|答案(1)|浏览(62)

我一直在自学如何完成装配中的某些任务。
现在,我正在尝试检测回文。我知道我可以使用堆栈,或者可能使用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

字符串

20jt8wwn

20jt8wwn1#

CPU寄存器是直接位于CPU核心内的计算机内存。计算机内存意味着一定数量的位(0/1),在64 b x86 CPU的情况下,通用寄存器是64位宽,名为rax, rcx, rdx, rbx, ..
ecxrcx的下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寄存器中的位值。

; EBX as memory address:
mov   ebx,OFFSET presetWord  ; some address into memory (32b unsigned number)
; ECX as numeric value ("unsigned long" in C++)
mov   ecx,SIZEOF presetWord - 1  ; 15
; AL as ASCII character (extended 8 bit)
mov   al,[ebx]      ; also shows how memory is referenced by address
; AL == 83 == 'S' => value of memory at address "presetWord"

字符串
在你的例子中,执行cmp [ecx],eax意味着在地址15处引用内存,幸运的是,这对你来说是非法的,所以它确实崩溃了。如果你偶然为你的进程使用了一些合法的地址(但不是你真正想要使用的地址),它将默默地继续下去,并继续下去,带来意想不到的结果。
你可能确实想做cmp [esi+ecx],eax,这意味着引用地址为presetWord+15(字符串的最后一个字符)的内存,但这只适用于第一次迭代。然后你做inc esi,它将指向presetWord+1地址(第二个字符)。
您可能只想比较字符,所以您应该将eax更改为al,以便一次只获取/比较单个字节,因为字符串是以ASCII编码(每个字符8位)编码的。
为了检查回文,你可能想用第一个字符的地址加载一个寄存器(“r1”),用最后一个字符的地址(!)加载一个寄存器(“r2”),然后执行这个循环:

  • if(r2 <= r1)-> exit with true(比较所有重要字符)(检查地址是否为无符号数)
  • 这里地址r1 < r2 ->现在比较字符
  • if(byte [r1]!= byte [r2])exit false
  • ++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芯片。而alecx直接位于CPU内部,当CPU需要时,可以在同一周期内访问它。
因此,如果您在计算中经常使用值,则可能需要将值存储到寄存器中,以免内存变慢(虽然一旦内存内容被L0/1/2/3缓存,“数百”周期就变成了合理的数量,有时甚至是直接在CPU芯片上缓存级别的0周期)。(因此缓存可以预读),并且数量合理(缓存通常以16- 32 B到4- 8 k的大小工作)。如果你访问两个指令,比如16个不同的8 k内存页面,你可能会用完可用的缓存行,然后至少会有一个访问以完全停止为特征,等待读取真实的内存。

相关问题