我用 x86 汇编语言编写了一个小程序,将 2 个分数相加。 这是结构:
struct FRACTION
numer dd ?
denom dd ?
ends
这是两个分数相加的函数:
;===================================
; add_fractions(frac_addr_1, frac_addr_2)
;
add_fractions:
.frac_addr_1 = 8h
.frac_addr_2 = 0ch
push ebp
mov ebp, esp
sub esp, sizeof.FRACTION
.sum_numer_offset = -8h
.sum_denom_offset = -4h
push esi
push edi
push edx
push ecx
push ebx
mov esi, dword [ebp + .frac_addr_1]
mov edi, dword [ebp + .frac_addr_2]
;=================================
; Get product of both denominator
;=================================
mov eax, dword [esi + FRACTION.denom]
mov ebx, dword [edi + FRACTION.denom]
mul ebx
mov ecx, eax ; ecx = product
;===============================
; Get GCD of both denominator
;===============================
mov eax, dword [esi + FRACTION.denom]
mov ebx, dword [edi + FRACTION.denom]
push ebx
push eax
call stein
add esp, 4*2 ; eax has GCD
;===============================
; Get the LCM = product / GCD
;===============================
mov ebx, eax ; swap eax and ecx
mov eax, ecx ; eax is not product
mov ecx, ebx ; ecx is GCD to divide
div ecx ; eax is now LCM after division
;=========================================
; store LCM as the denominator of the sum
;=========================================
mov dword [ebp + .sum_denom_offset], eax
;=========================================
; compute for the numerator of the sum
;=========================================
mov eax, dword [ebp + .sum_denom_offset]
mov ebx, dword [esi + FRACTION.denom]
div ebx
mov ebx, dword [esi + FRACTION.numer]
mul ebx
mov dword [ebp + .sum_numer_offset], eax
mov eax, dword [ebp + .sum_denom_offset]
mov ebx, dword [edi + FRACTION.denom]
div ebx
mov ebx, dword [edi + FRACTION.numer]
mul ebx
add eax, dword [ebp + .sum_numer_offset]
mov dword [ebp + .sum_numer_offset], eax
lea eax, dword [ebp - sizeof.FRACTION]
.end_func:
pop ebx
pop ecx
pop edx
pop edi
pop esi
add esp, sizeof.FRACTION
pop ebp
ret
add_fractions 子例程中的 LEA 指令工作正常,它将正确的地址返回到具有 sum_numer 和 sum_denom 的本地变量。
这是调用程序中调用 add_fractions 的代码片段:
mov esi, frac_1
mov edi, frac_2
push edi
push esi
call add_fractions
add esp, 4*2
lea ebx, dword [eax]
lea ecx, dword [eax + 4]
mov eax, [ebx]
call print_eax
mov eax, [ecx]
call print_eax
在上面的代码片段中,frac_1 是 1/2,frac_2 是 1/3。调用后,我将分子的地址存储在 ebx 中,将分母的地址存储在 ecx 中,然后打印这些地址中的值。它应该打印出来 (5/6) 但实际结果很奇怪:
5
dff5c
不是6,而是dff5c,这是我用来存储分母的堆栈地址。但是当我直接把lea改为mov时。效果很好。
mov esi, frac_1
mov edi, frac_2
push edi
push esi
call add_fractions
add esp, 4*2
mov ebx, dword [eax]
mov ecx, dword [eax + 4]
mov eax, ebx
call print_eax
mov eax, ecx
call print_eax
这次我将枚举数和分母的值存储到ebx、ecx中,而不是地址中。并且打印正确
5
6
问题是“lea ebx, dword [eax]”是否会影响堆栈并更改[eax + 4]中的值,因此它会打印出dff5c而不是6?
我在这段代码中看到(至少)三个问题:
在
div r32
之前您需要清除EDX寄存器!您有 div ecx
和两次 div ebx
。
您在 EAX 中返回的地址指向本地区域,在 add_fractions 返回到其调用者之后,该地址不再有效。
lea eax, dword [ebp - sizeof.FRACTION] ... add esp, sizeof.FRACTION
add esp, sizeof.FRACTION
使信息无效。
一个简单的解决方案可能是覆盖堆叠的参数。因此,您不是存储在
[ebp - 8]
和 [ebp - 4]
,而是存储在 [ebp + 8]
和 [ebp + 12]
。到那时,原始参数已经转移到 ESI 和 EDI。
push edi push esi call add_fractions add esp, 4*2 lea ebx, dword [eax] lea ecx, dword [eax + 4] mov eax, [ebx] call print_eax mov eax, [ecx] call print_eax
在
[eax]
和 [eax + 4]
,您通常会得到 .sum_numer 和 .sum_denom。这些是实际数字,而不是指向这些值的指针。您不需要取消引用。push edi
push esi
call add_fractions
pop ebx ; -> EBX is sum_nomer
pop ecx ; -> ECX is sum_denom
mov eax, ebx
call print_eax
mov eax, ecx
call print_eax