Re:x86汇编语言 -
我有三个32位有符号数:n1,n2和n3。
我想通过n2来确定n1以获得64位签名结果。
然后我想通过n3想象64位结果。
问题是如果64位带符号的结果足够大和/或如果n3足够小,将导致溢出并且idiv将抛出#DE异常。
如果idiv只是在溢出时设置#DE,我可以检查确认 ((n1 * n2)/ n3)* n3 +((n1 * n2)mod n3)=(n1 * n2)。如果没有,会发生溢出,我可以相应地继续。
但#DE与其他人并不好玩。当它被提出时,它报告"程序已经停止工作"然后把你踢出去。
所以我需要找到一些方法来预先检查idiv是否会在我进行除法之前导致溢出,或者我需要在汇编语言中执行相当于try ... catch的操作。
我已经搜索过互联网(包括这里),但总的来说并没有找到相关信息。没什么特别有用的。
我已尝试在c ++中尝试内联代码尝试...无济于事 - 它仍然报告"程序已停止工作"然后把你踢出去。
例如,使用两个支持文件:
// TC.h
// Version 1.0.0
// MDJ 2016/05/06
extern "C" int q;
extern "C" int m;
和
// TC.s
// Version 1.0.0
// MDJ 2016/05/06
.globl _q
.globl _m
.bss
.align 4
_q:
.space 4
_m:
.space 4
此文件运行完成并生成正确的结果:
// TryCatch.cpp
// Version 1.0.0
// MDJ 2016/05/06
#include <iostream>
#include "TC.h"
using namespace std;
int main() {
cout << endl;
try {
# AT&T syntax
asm(
"movl $34, %eax\n\t"
"movl $48, %edx\n\t"
"imull %edx\n\t"
"movl $13, %ecx\n\t"
"idivl %ecx\n\t"
"movl %eax, _q\n\t"
"movl %edx, _m\n\t"
);
}
catch(int e) {
cout << "Caught." << endl;
}
cout << "Reached the End." << endl;
cout << "q = " << q << endl;
cout << "m = " << m << endl;
cout << endl;
return 0;
}
但是,如果我像这样改变n1,n2和n3:
// TryCatch.cpp
// Version 1.0.0
// MDJ 2016/05/06
#include <iostream>
#include "TC.h"
using namespace std;
int main() {
cout << endl;
try {
# AT&T syntax
asm(
"movl $234567890, %eax\n\t"
"movl $123456789, %edx\n\t"
"imull %edx\n\t"
"movl $3, %ecx\n\t"
"idivl %ecx\n\t"
"movl %eax, _q\n\t"
"movl %edx, _m\n\t"
);
}
catch(int e) {
cout << "Caught." << endl;
}
cout << "Reached the End." << endl;
cout << "q = " << q << endl;
cout << "m = " << m << endl;
cout << endl;
return 0;
}
&#34; catch&#34;没有收到溢出,系统反而报告&#34;程序已停止工作&#34;然后把你踢出去。
任何帮助都将不胜感激。
答案 0 :(得分:1)
我突然想到我完全走错了轨道(作为模型铁路运输车,这是一个真正令人发指的罪行)双关语意图: - )。
但是,真的,我一直在努力解决这个问题。
相反,我应该采取简单的方法:我应该回到我1950年的文法学校和我长期分裂的第一次冒险。不要对EDX感到困惑:EAX被ECX分割,让我们想到一个两位数(无符号)数字除以一位数(无符号)数字。
现在,两位数字是被除数,它有一位数和十位数。所以它可以在0到99之间变化。
并且,一位数字是除数,它只有一位数。因此,它可以在1到9之间变化(因为不允许除以零)。
例如,考虑77除以2:
3 8
_____
2 | 7 7
6
_
1 7
1 6
___
1
因此,结果是:商为38,余数为1.
但是,在这里,与股息一样,我们允许商也有两位数:十位数和一位数。如果我们改为将商限制为只有一位数,会发生什么。
然后我们可以调用任何除法,这导致商数在十位数字段中具有除零之外的任何数字,AN OVERFLOW !!!
但是,那么,产生这样一个溢出所需的条件是什么:任何一个小于或等于股息数字的DIVISOR !!!
类似地,在EDX的划分:ECX的EAX中,如果ECX&lt; = EDX !!!
将发生溢出因此,这是我们对溢出的简单测试:
ECX <= EDX
适用于无符号分割。
预先检查有符号的除法溢出要复杂得多。我认为这会奏效,但我还在测试。
从EDX中的64位带符号被除数开始:EAX和ECX中的32位带符号除数。然后:
# Temporarily save the dividend
movl %edx, _dividendHigh # Most-significant 32b
movl %eax, _dividendLow # Least-significant 32b
# Check the divisor for zero
testl %ecx, %ecx # Is divisor = 0 ?
jz _DivideByZero # Go if Yes
# Check the divisor for +/- 1
cmpl $1, %ecx
je _dChkA # Go if divisor = 1
cmpl $-1, %ecx
je _dChkA # Go if divisor = -1
jmp _dChkC # Else continue
_dChkA:
# If dividendHigh < -1 or > 0 and divisor = +/- 1
# then overflow will occur.
cmpl $-1, %edx
jl _DivideOverflow # Go if divHigh < -1
cmpl $0, %edx
jg _DivideOverflow # Go if divHigh > 0
# If dividendHigh = -1 and bit 31 of dividendLow = 0
# and divisor = +/- 1 then overflow will occur.
cmpl $-1, %edx
jne _dChkB # Go if divHigh <> -1
bt $31, %eax
jnc _DivideOverflow # Go if divLow b31 = 0
_dChkB:
# If dividendHigh = 0 and bit 31 of dividendLow = 1
# and divisor = +/- 1 then overflow will occur.
cmpl $0, %edx
jne _dChkC # Go if divHigh <> 0
bt $31, %eax
jc _DivideOverflow # Go if divLow b31 = 1
# Check for non-unary overflow
# Overflow will occur if the
# most-significant 33b can be
# divided by the divisor. NOTE:
# that's 33 bits, not 32,
# because all numbers are signed.
# Do dividend shift and manual sign extension
# Check bit 63 to determine if dividend is positive or negative
_dChkC:
bt $31, %edx
jc _dChkD # Go if negative
# Do the 64-bit shift # Positive
# First, shift the Least-significant
# 32b left 1 bit (bit 32 --> CF).
shll $1, %eax
# Then, rotate the Most-significant
# 32b left, through the carry, 1 bit
# (CF --> bit 1 then bit 32 --> CF).
rcll $1, %edx
# Move it to %eax and manually positive-sign extend it
movl %edx, %eax
jmp _dChkE
_dChkD: # Negative
# Do the 64-bit shift
# First, shift the Least-significant
# 32b left 1 bit (bit 32 --> CF).
shll $1, %eax
# Then, rotate the Most-significant
# 32b left, through the carry, 1 bit
# (CF --> bit 1 then bit 32 --> CF).
rcll $1, %edx
# Move it to %eax and manually negative-sign extend it
movl %edx, %eax
movl $-1, %edx
# Do the Test Divide of the
# Most-Significant 33b
_dChkE:
idivl %ecx # EDX:EAX / ECX
# EAX = test quotient
# EDX = test remainder
testl %eax, %eax
jnz _DivideOverflow # Go if Quotient <> 0
# Get the full dividend
movl _dividendHigh, %edx # Most-significant 32b
movl _dividendLow, %eax # Least-significant 32b
# Perform the 64b by 32b division
idivl ecx # EDX:EAX / ECX
# EAX = quotient
# EDX = remainder
答案 1 :(得分:0)
你的DivideTester
太荒谬了。您只需要保留来电者%ebx
,%esi
,%edi
,%ebp
和%esp
。您似乎在同一个函数中保存/恢复大量寄存器,并在最后多次恢复相同的寄存器。
试试这个:
.globl _DivideTester
_DivideTester:
# extern "C" void DivideTester(void);
# clobbers eax and edx. The C compiler will assume this, because the standard calling convention says functions are allowed to clobber eax, ecx, and edx
# mov $0, %edx
# mov $6742542, %eax
# Instead, set the globals from C, and print before calling
mov _dividendHigh, %edx # load from globals
mov _dividendLow, %eax
# movl _divisor, %ecx
idivl _divisor # EDX:EAX / divisor
mov %eax, _quotient # EAX = Quotient
mov %edx, _remainder # EDX = Remainder
# print the results from C, or use a debugger to inspect them
ret
或者如果你坚持将常量硬编码到asm中,你仍然可以这样做。您仍然可以从C打印它们。
注意这个函数读起来有多容易?除了idiv
之外,基本上什么都不会出错。从asm获取函数调用是更多的工作,所以不要浪费你的时间。让编译器做它擅长的事情。你仍然可以通过反汇编/单步执行代码来确切地看到编译器做了什么,所以它不会因为将该部分留给C而失去调试能力。它更像是你避免整个一类错误(就像你最初那样)。
您只需要为mov $1234, _memory
之类的内容添加操作数大小,其中没有寄存器来暗示操作数大小。我宁愿省略它。如果不明确,as
会向您显示错误消息而不是选择默认消息,因此它始终是安全的。