我一直在努力教自己如何完成装配中的某些任务。
现在,我正在尝试检测回文。我知道我可以使用堆栈,或者可能使用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
答案 0 :(得分:0)
CPU寄存器是直接位于CPU内核内的一块计算机内存。一块计算机存储器意味着一些位(0/1),在64b x86 CPU的情况下,通用寄存器是64位"宽",在名称rax, rcx, rdx, rbx, ..
下。
ecx
是rcx
的低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") ,然后执行此循环:
这将产生" 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芯片。虽然al
和ecx
直接位于CPU内部,但在CPU需要时可在同一周期内访问。
因此,您可能希望将值存储到寄存器中,如果在计算中经常使用它们,则不要因内存而减慢(尽管一旦内存内容被L0 / 1/2/3高速缓存缓存,&#34 ;数百个周期变为合理数量,有时甚至是0个周期,直接在CPU芯片上具有高速缓存水平)。但是您希望以可预测的模式访问内存(因此缓存可以预读),并且数量合理(缓存通常使用16-32B的大小,最高级别为4-8k)。如果你使用16个不同的8k内存页面的几个指令访问,你可能会用尽可用的缓存行,然后将至少有一个访问具有完全停止,等待实际内存读取。