【问题标题】:Arithmetic operations with large numbers in assembly汇编中大数的算术运算
【发布时间】:2018-10-04 16:20:54
【问题描述】:

我的任务是编写一个可以从 C 中读取并声明如下的汇编例程:

extern int solve_equation(long int a, long  int b,long  int c, long int *x, long int *y);

找到方程的解

a * x + b * y = c

-2147483648 <x, y <2147483647 中检查所有选项。

如果找到解决方案,从例程返回的值将是 1,另一个是 0

您必须考虑到计算结果:a * x, b * y, a * x + b * y 可以超过 32 位。

.MODEL SMALL
.DATA
C DQ ?
SUM DQ 0
MUL1 DQ ?
MUL2 DQ ?
X DD ?
Y DD ?
.CODE
.386
PUBLIC _solve_equation
_solve_equation PROC NEAR
PUSH BP 
MOV BP,SP
PUSH SI
MOV X,-2147483648 
MOV Y,-2147483648 
MOV ECX,4294967295
FOR1:
        CMP ECX,0
        JE FALSE
        PUSH ECX
        MOV Y,-2147483648 
        MOV ECX,4294967295
FOR2:
        MOV SUM,0
        CMP ECX,0
        JE SET_FOR1

        MOV EAX,DWORD PTR [BP+4]
        IMUL X
        MOV DWORD PTR MUL1,EAX
        MOV DWORD PTR MUL1+4,EDX

        MOV EAX,DWORD PTR [BP+8]
        IMUL Y
        MOV DWORD PTR MUL2,EAX
        MOV DWORD PTR MUL2+4,EDX

        MOV EAX, DWORD PTR MUL1
        ADD DWORD PTR SUM,EAX
        MOV EAX, DWORD PTR MUL2
        ADD DWORD PTR SUM,EAX

        MOV EAX, DWORD PTR MUL1+4
        ADD DWORD PTR SUM+4,EAX
        MOV EAX, DWORD PTR MUL2+4
        ADD DWORD PTR SUM+4,EAX

        CMP SUM,-2147483648
        JL SET_FOR2
        CMP SUM,2147483647
        JG SET_FOR2
        MOV EAX,DWORD PTR [BP+12]
        CMP DWORD PTR SUM,EAX
        JE TRUE
SET_FOR2:       
            DEC ECX
            INC Y
            JMP FOR2
SET_FOR1:
            POP ECX
            DEC ECX
            JMP FOR1
FALSE:
            MOV AX,0
            JMP SOF
TRUE:
            MOV SI,WORD PTR [BP+16]
            MOV EAX,X
            MOV DWORD PTR [SI],EAX
            MOV SI,WORD PTR [BP+18]
            MOV EAX,Y   
            MOV DWORD PTR [SI],EAX
            MOV AX,1
SOF:
        POP SI
        POP BP
        RET
        _solve_equation ENDP
        END 

这是处理大数的正确方法吗?

当我尝试这样做时,我得到了操作或指令has illegal size 的参数:

MOV SUM,0
CMP SUM,-2147483648
CMP SUM,2147483647

主要代码:

int main() 
    {
     long int x, y, flag;

     flag = solve_equation(-5,4,2147483647,&x, &y);
    if (flag == 1)
      printf("%ld*%ld + %ld*%ld = %ld\n", -5L,x,4L,y,2147483647);
      return 0;

    } 

输出

-5*-2147483647 + 4*-2147483647 = 2147483647

我正在使用 dosbox 0.74 和 tcc

【问题讨论】:

  • 请注意,即使在 asm 中,这也可能需要一些时间才能运行。很长一段时间。
  • 看起来像一个混合源程序(TurboC 和 TASM?)。请添加适当的标签并描述您使用的版本。显示主程序。参数列表(int c! int*x ,int*y!)是老师确定的吗?
  • @rkhb 是的,老师确定的参数列表,我做了彼得说的改变,它起作用了。
  • 如果它有效,那很好。每次我的要求请考虑:附加标签(turbo-cturbo-c++tasmdosbox)、TurboC 版本的主程序和规范。这些信息有助于回答。

标签: c assembly x86


【解决方案1】:

您使用的是 16 位代码,因此 64 位操作数大小不可用。您的汇编器神奇地将大小与sum 关联起来,因为您使用sum dq 0 定义了它。

所以mov sum, 0 等价于mov qword ptr [sum], 0,当然不会在16 位或32 位模式下汇编;整数运算一次最多只能对 32 位进行运算。

(32 位操作数大小在 386 兼容 CPU 上的 16 位模式下可用,使用相同的机器编码,在 32 位模式下允许 16 位操作数大小。但 64 位操作数大小仅可用在 64 位模式下。与 386 不同,由于各种原因,AMD64 没有向先前存在的模式添加任何新前缀或任何内容。)


您可以使用 SSE 存储将整个 64 位 sum 归零,甚至可以与 SSE4.2 pcmpgtq 进行比较,但这可能不是您想要的。

您似乎想检查 64 位 sum 是否适合 32 位。 (即,如果它是符号扩展的 32 位整数)。

所以实际上你只需要检查所有 32 个高位是否相同并匹配低半部分的第 31 位。

mov  eax, dword ptr [sum]
cdq                              ; sign extend eax into edx:eax
                                 ; i.e. copy bit 31 of EAX to all bits of EDX
cmp  edx, dword ptr [sum+4]
je   small_sum

【讨论】:

  • DOS下可以使用SSE吗? IIRC 在使用 SSE 扩展之前,我们需要在控制寄存器中设置一些位。
  • @MargaretBloom:是的,您可以在实模式下使用 SSE,但不能使用 AVX。显然,16 位模式会禁用 VEX 前缀的解码,以避免将该编码用作陷阱。我假设 DOS 不会为您设置控制寄存器位,但您可以这样做 (wiki.osdev.org/SSE)。 (我在注意到它是 16 位代码之前写了这个答案,而不是我想的 32 位)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-17
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
相关资源
最近更新 更多