繁体   English   中英

麻烦了解寄存器x86

[英]Trouble understanding registers x86

我一直在试图教自己如何完成某些组装任务。

现在,我正在努力检测回文。 我知道我可以使用堆栈,也可以使用Irvine的库比较字符串,但是我正在尝试通过寄存器来实现。

问题是,在使用寄存器时,我感到很困惑。

可以编译以下内容,但是当我进入CMP行时,程序中断并显示以下消息:

Project.exe中0x004033FC的未处理异常:0xC0000005:访问冲突读取位置0x0000000F。

我假设它与设置寄存器的方式有关,但是即使在调试时使用寄存器也无济于事。

任何帮助,将不胜感激。

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

CPU寄存器是直接位于CPU内核内部的一块计算机内存。 一块计算机内存意味着一定数量的位(0/1),对于64b x86 CPU,通用寄存器为64位“宽”,名称为rax, rcx, rdx, rbx, ..

ecxrcx的下32b部分( rcx特殊名称访问上32b的部分,只能通过使用rcx指令进行rcx )。 下部16b部分可通过cx访问,该部分由两个8b部分ch (上部)和cl (下部)组成。

因此,当您使用ecx ,可以将32位设置为0或1。可以将其解释为0到2 32 -1之间的无符号数(以hexa 0 .. 0xFFFFFFFF ),或者将其解释为-2 31到+ 2 31 -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位)编码。 eax适用于UTF-32编码。


要检查回文,您可能想加载第一个字符的地址的一个寄存器(“ r1”),最后一个字符的地址(!)的一个寄存器(“ r2”),然后执行以下循环:

  • if(r2 <= r1)->以true退出(比较所有重要字符)(检查地址为无符号数字)
  • 现在地址r1 <r2->现在比较字符
  • if(字节[r1]!=字节[r2])退出false
  • ++ r1,--r2->调整地址以指向秒/秒的最后一个字符
  • 循环到开始

这将为presetWord生成“ false”,如'S' != 's' ,因此您可能想对if (byte [r1]...部分,不区分大小写,但是我首先要使其不起作用。


调试时,您应该能够识别寄存器中某些数字的“类”。 如果将大小加载到寄存器中,则很有可能会是一些小数字,例如0000000F (15)。 地址很可能会像8040506E这样的大量数字。 作为单个字符用ASCII字符时,应该引起类似20 - 7F中常见的情况,但如果这样做mov al,... ,调试器仍显示整个eax ,所以上三个字节将保持它的前值,为例如读取空格字符到eax集作为12345678将的值更改eax12345620 (空间' ' == 0x20在ASCII)。

您也可以使用内存视图来检查内存中特定地址的内容。 例如,如果您将cmp更改为cmp [esi+ecx],eax并在内存视图中检查该地址,您会看到它会在第二次迭代中再次指向最后一个字符,而不是最后一个字符。

这一切都是可见的,可以在调试器中检入,有时有些乏味,然后通常比要求SO或仅考虑源代码要容易得多,特别是如果您停留时间较长。


最后...为什么还要注册? 因为计算机内存是单独的芯片。 它可能看起来很无辜,但是诸如mov al,[presetWord]类的指令实际上可能会停滞数百个CPU周期,而CPU芯片将等待内存芯片读取内存内容,并通过总线将其发送到CPU芯片。 。 尽管alecx直接位于CPU内部,但在CPU需要时可以在同一周期内对其进行访问。

因此,您可能希望将值存储到寄存器中(如果在计算中经常使用它们),以免减慢内存的速度(尽管一旦内存内容被L0 / 1/2/3缓存缓存,“数百”个循环就变成了合理的数量,有时甚至0个周期,缓存级别直接在CPU芯片上)。 但是,您希望以可预测的模式访问内存(以便高速缓存可以预读),并且访问量合理(高速缓存通常以16-32B的大小(按其级别最高可达4-8k)工作)。 如果您访问了16条不同的8k内存页之类的指令,则可能会耗尽可用的缓存行,然后将至少有一个具有完全停顿状态的访问,等待实际内存的读取。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM