麻烦了解寄存器x86

时间:2016-12-19 21:33:30

标签: assembly x86 irvine32

我一直在努力教自己如何完成装配中的某些任务。

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

问题是,当谈到使用寄存器时,我有点困惑。

以下编译,但是当我到达CMP线时,程序会中断并给我这样的信息:

  

Project.exe中0x004033FC处的未处理异常:0xC0000005:Access   违规读取位置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

1 个答案:

答案 0 :(得分:0)

CPU寄存器是直接位于CPU内核内的一块计算机内存。一块计算机存储器意味着一些位(0/1),在64b x86 CPU的情况下,通用寄存器是64位"宽",在名称rax, rcx, rdx, rbx, ..下。

ecxrcx的低32b部分(高级32b部分无法通过特殊名称访问,只能通过使用rcx的说明)。并且可以通过cx访问较低的16b部分,ch由两个8b部分cl(上部)和ecx(下部)组成。

因此,在使用0 .. 0xFFFFFFFF时,可以将32位设置为0或1.这可以解释为0到2之间的无符号数 32 -1(在hexa中{{ 1}}),或从-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(byte [r1]!= byte [r2])exit false
  • ++ r1, - r2 - >调整地址以指向第二个/第二个最后一个字符
  • 循环到开头

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

在调试时,您应该能够识别" class"其中一些数字在寄存器中。如果将大小加载到寄存器中,则很可能是一些小数字,例如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高速缓存缓存,&#34 ;数百个周期变为合理数量,有时甚至是0个周期,直接在CPU芯片上具有高速缓存水平)。但是您希望以可预测的模式访问内存(因此缓存可以预读),并且数量合理(缓存通常使用16-32B的大小,最高级别为4-8k)。如果你使用16个不同的8k内存页面的几个指令访问,你可能会用尽可用的缓存行,然后将至少有一个访问具有完全停止,等待实际内存读取。