【问题标题】:Why am I getting overflow? (NASM Assembler)为什么我会溢出? (NASM 汇编器)
【发布时间】:2016-02-09 06:58:48
【问题描述】:

为了这个程序的目的,我必须编写一个程序来计算一个 22/21*(r^2)*h 的圆锥体积。

因此,我开发的这段代码适用于在将变量相乘后不会导致 EDX 寄存器被填充的任何变量。我不能将寄存器移位或“或”在一起,也不能使用跳转或 cmp 语句。我还必须将 8 位寄存器用于高度和宽度变量,16 位用于半径^2 和半径^2*高度计算,然后用 32 位完成最后的除法计算。看一看。我不知道为什么它会溢出到 EDX 中,我不知道如何纠正它......

.data

radius          WORD ?                                      ;var for radius
height          WORD ?                                      ;var for height
numerator       WORD 22                                     ;Making a variable for numerator
denominator     DWORD 21                                    ;Making a variable for denominator
volume          DWORD ?                                     ;Making a variable to store volume main
decimal         DWORD ?                                     ;Making a variable to store volume remainder
prompt BYTE "Enter the radius: ",0                          ;val for radius prompt
promp2 BYTE "Enter the height: ",0                          ;val for height prompt
result BYTE "The volume of the cone is: ", 0                ;val for printing volume
decim  BYTE ".", 0                                          ;val for decimal place

                                                                ;volume of cone is 1/3(pi(r^2)h) or 22/21(r^2)h

.code

main PROC
    mov edx,OFFSET promp2                                       ;move prompt into edx for printing
    call WriteString                                            ;ask user for height

    mov eax,0
    mov edx,0
    mov ecx,0
    mov ebx,0                                                   ;move zero into all main registers

    call ReadDec                                                ;reading the height
    mov bl,al                                                   ;move height into storage

    mov edx,OFFSET prompt                                       ;move promp2 into edx for printing
    mov eax,0                                                   ;re-zero eax
    call WriteString                                            ;ask user for radius
    call ReadDec

    mul al                                                      ;obtain r^2
    mul bl                                                      ;multiply r^2 by height
    mov edx,0                                                   ;zero out edx
    mov dx,numerator                                            ;move numerator into dx
    mul dx                                                      ;multiply r^2*h by 22

    mov ebx,denominator                                         ;move 21 into ebx to divide
    div ebx                                                     ;divide by 21 to get total volume
    mov volume,eax                                              ;move volume into volume variable
    mov decimal,edx                                             ;move decimal remainder into decimal variable

    mov edx,OFFSET result                                       ;prepare string result for printing
    call WriteString                                            ;print result string
    mov eax,volume                                              ;move volume into eax to print     
    call WriteDec                                               ;print volume
    mov edx,OFFSET decim                                        ;move dec into print register
    call WriteString                                            ;print decimal
    mov eax,decimal                                             ;move decimal remainder into eax to print
    call WriteDec                                               ;print decimal value
    call CRLF                                                   ;carriage return and new line
    call WaitMsg                                                ;pause output

    exit
main ENDP

也许我可以为中间算术做不同的寄存器,但鉴于当前的 8-8-16-16-32 要求,我不知道如何实现。如果有人可以帮助我提供一个可行的解决方案并解释它为什么起作用,也许可以逐步完成它,这样我就可以理解为什么我的不适用于更大的整数(大多数超过 20 不起作用),这将是惊人的。提前感谢您能给我的任何帮助!

【问题讨论】:

  • 如果您的问题是在您进行除法时edx 不为零,那么解决方案是清除它:mov edx, 0(或xor edx, edx)。跨度>
  • edx 存储乘法运算产生的 16 到 32 位溢出以及除法运算的余数。在将“分子”移入其中以执行 AX 和 DX 之间的 16 位乘法之前,我清除了 edx。比如运行这个程序,分别插入18和15。将 AX 和 DX 相乘后,EAX 的前两个字节被填充,剩下的字节进入 DX,应该使用 shl EDX,16 然后或 EAX,EDX 将其移动到 EAX 的头部,但我不能这样做,因为它是对程序的限制。
  • div ebxedx:eax 中的64 位值除以ebx。您的乘法结果在dx:ax 中。您要么需要使用在相同寄存器对上操作的muldiv 的变体,要么通过在除法之前清除edx 来丢弃乘法溢出。
  • 但是如果我清除乘法溢出,它会给出一个不正确的答案,因为正确答案的十六进制值将会改变。我认为从 EBX 切换到 BX 可以解决它,它解决了溢出问题,但现在它只划分 AX 寄存器中的内容,而不是 DX:AX 的组合。
  • 那么为什么不乘以一个 32 位寄存器(例如 mul edx),这样乘积最终会出现在 edx:eax 中?

标签: assembly nasm integer-overflow irvine32


【解决方案1】:

让我们来看看整个计算:

mul   al               ;obtain r^2

这个mul alAX 寄存器中留下一个结果。所以你必须在下一行使用一个单词 multiply 否则你会丢失 r^2 的一部分值。

mov   bh, 0
mul   bx               ;multiply r^2 by height

现在结果在DX:AX。要将其乘以 22,您可以通过堆栈将其移动到 32 位寄存器 EAX

push  dx
push  ax
pop   eax
movzx edx, numerator   ;Why is this defined a word?
mul   edx

这次结果在EDX:EAX,为最后的除法做好准备。

div   denominator

最后将EAX 中的商和EDX 中的余数移动到各自的变量中。

mov   volume, eax      ;move volume into volume variable
mov   decimal, edx

【讨论】:

  • ew,这是组合两个 16 位值的可怕方式。 shl edx, 16/or edx, eax。 (或者如果 eax 的上半部分包含垃圾 shl edx, 16 / mov dx, ax。)或者通过简单地一直使用 32 位来避免 IvyBridge 之前的英特尔 CPU 的部分寄存器减速。 movzx eax, al 如有必要,其他 regs 以此类推,然后执行 imul eax, ebximul eax, edx 之类的操作。如果您不想要结果的高半部分,带有 2 或 3 个操作数的 imul 是更好的选择,因为它更快,而且您不会被 eax 作为目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 2020-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多