2016-12-19 66 views
0

我一直在试图教自己如何在汇编中完成某些任务。理解寄存器的问题x86

现在,我正在努力检测回文。我知道我可以使用堆栈,或者可能使用Irvine的库比较字符串,但我试图通过寄存器来完成。

问题是,当涉及到使用寄存器时,我比较困惑。

下编译,但是当我到CMP线,程序中断,给我这个消息:0000005:

在0x004033FC在Project.exe未处理的异常访问 冲突读取位置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 
+4

您使用'ecx'作为指针,而是使用长度加载它。另外,您一次只能操作4个字节而不是1.使用'mov al,[esi]; cmp [presetWord + ecx],al'来代替。 – Jester

+0

您将'ECX'设置为*字符数*,然后将其用作指向内存的指针。它不会指向任何合理的东西,当然它会崩溃。你需要设置'ECX'来抵消+长度,而不仅仅是长度。 –

+0

@Jester所以使用它,我仍然必须做'mov ecx,OFFSET presetWord + SIZEOF presetWord - 1'? –

回答

0

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

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

因此,作为使用的是ecx,可以设置32位为0或1。哪个可以解释为无符号数从0到2 32 -1(在六0 .. 0xFFFFFFFF),或者作为符号数从-2 to +2 -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”)(!),然后执行这个循环:

  • 如果(R2 < = R1) - >与真(比所有重要的字符)出口(检查为无符号数的地址)
  • 这里解决R1 < R2 - >现在比较字符
  • 如果(字节[r1]!=字节[r2])退出假
  • ++ R1,--r2 - >调整地址指向第二/倒数第二个字符
  • 环路开始

这将产生presetWord“假”,为'S' != 's',所以你可能要为if (byte [r1]...部分引入不区分大小写,但我会先让它工作没有。


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

您还可以使用内存视图来查看特定地址的内容在内存中。如果你想例如改变cmpcmp [esi+ecx],eax,并检查内存认为地址,你会看到它会在第二次迭代中再次在最后一个字符,而不是第二个最后一个字符点。

这是所有可见和可能检查调试器,有时有点繁琐,然后再次往往比在SO上询问或只考虑源代码更容易,特别是如果你被困在更长的时间。


终于......为什么要登录?因为电脑内存是独立的芯片。它可能看起来无辜的,但像mov al,[presetWord]指令实际上可能拖延数百个CPU周期,而CPU芯片将等待内存芯片读取内存的内容并把它通过总线线连接到CPU芯片。而alecx直接位于CPU内部,可在CPU需要时在同一周期内访问。因此,如果您在计算中经常使用它们,您可能希望将值存储到寄存器中,以避免内存变慢(尽管一旦内存内容被L0/1/2/3高速缓存缓存,“数百“的周期变成合理的量,有时甚至在CPU芯片上直接存储高速缓存级的情况下为0个周期)。但是你希望以可预测的模式访问内存(所以缓存可以预读),并且数量合理(缓存通常以16-32B的大小在他们的级别上高达4-8k)。如果您访问16条不同的8k内存页面的指令,则可能会用尽可用的缓存行,然后至少有一个访问将全部停止,等待真正的内存读取。

+0

作为一个方面说明..在汇编中没有变量的“类型”,或任何东西。这是你的代码,它给了这些位的意义/结构。所以你必须始终知道你使用的数据的大小,就像ASCII char是8位一样。所以你写代码只读取单个字节,并用+ -1调整指针。如果你的数据超过16b,你需要读取单词,并用+ -2调整指针,或者在寻址中使用* 2,比如'mov ax,[esi + ecx * 2]'。因此,请确保您了解这些8/16/32位的含义,以及什么是字节/字/双字,并检查常见类型是如何在C++中编码的(如启发式)。 – Ped7g

+0

正如我在之前的评论中写的,我正在测试字符串“banab”,但它仍然失败。另外,这里是我的代码,因为它已被更新:http://imgur.com/a/pUJYa –

+0

另外,关于大小,我不能'mov ax,[esi + ecx * TYPE presetWord]'? –