Разделить положительное число на отрицательное в Ассемблер

Я хочу создать программу, которая может вычислять задачи деления. Проблема в том, что моя программа дала сбой, когда я попытался разделить на отрицательное число, хотя я создал ветвь под названием "DivNeg", которая должна была предотвратить сбой. У кого-нибудь есть идеи, как это исправить?

Вот мой ассемблерный код

    .386

.model flat

public _Divide

.code

_Divide proc
        mov eax, [esp + 4]  ; First address ; this is the dividend
        mov ebx, [esp + 8]  ; Second address ; this is the divisor

        cmp ebx, 0
        je  DivZero
        cmp ebx, 0
        jnae    DivNeg

        cdq
        idiv ebx            ; To divide by eax by ebx
        mov ebx, [esp + 12] ; Third address; this is the remainder
        jmp Done1

DivZero:
    mov     eax,-1          ; If user divides by zero, this will set the result to negative 1
    mov     edx, 0          ; If user divides by zero, this will set the remainder to 0
    mov     ebx,[esp +12]   ; Needed for the remainder if divided by 0
    cmp     ebx, 0
    je      Done2


Done1:
    mov     [ebx], edx
    je  Done1

DivNeg:
    cmp     ebx, 0
    jge     Done2
    mov     eax, -1
    neg     eax
    je      DivNeg


Done2:
        ret
_Divide endp

        end

person Jaquai    schedule 04.10.2017    source источник
comment
только на первый взгляд; у вас есть: je DivZero; je DivNeg один за другим; а также что должен делать DivNeg? почему есть ascii «-»?   -  person Paweł Łukasik    schedule 05.10.2017
comment
Я создал цикл DivNeg, чтобы отображались результат и остаток от деления на отрицательное число. Без него программа почему-то зависнет. Что касается части je DivNeg, я поставил ее на случай, если делитель меньше нуля.   -  person Jaquai    schedule 05.10.2017
comment
Я думаю, вам следует больше узнать об ассемблере; 'loop' не имеет смысла - это eax › 0, он будет зацикливаться вечно. Вся проблема немного «странна» для задачи, которую она должна выполнять.   -  person Paweł Łukasik    schedule 05.10.2017
comment
Второй прыжок, возможно, должен проверять другой флаг, кроме равного. Или, возможно, он вообще не должен проверять на отрицательный результат.   -  person Michael Dorgan    schedule 05.10.2017
comment
@MichaelDorgan Хорошо. Вместо этого я изменил je на jnge, но проблема все еще сохраняется. Я также избавился от части '-', но изменений все равно нет.   -  person Jaquai    schedule 05.10.2017
comment
@Jaquai Я действительно хочу тебе помочь, но просто подумай о DivNeg; вы ставите 0 в ebx и сразу после этого сравниваете с -1? это бесполезно - jmp никогда не произойдет. Непонимание того, что должен делать DivNeg, мешает оказанию помощи   -  person Paweł Łukasik    schedule 05.10.2017
comment
@PawełŁukasik Извините за часть mov ebx, 0. Этого не должно было быть в обновленном коде. Причина, по которой я создал цикл DivNeg, заключается в том, что у меня были проблемы со сбоем при делении на отрицательное число, когда у меня его не было. Я хотел создать этот цикл специально на тот случай, если кто-то будет делить на что-то меньшее нуля. Это был единственный способ для моей программы работать. Прошу прощения, если нечетко отвечаю на ваш вопрос.   -  person Jaquai    schedule 05.10.2017
comment
Я вызвал его с помощью (40, -7, &остальное) и работает для меня (возвращает -5 и 5 в остатке двойного слова). Тем не менее, этот код далек от правильного, я намеренно выбрал входные параметры, которые работают, просто чтобы троллить вас в той же степени, что и вы троллите нас, не предоставляя подробностей, как вы это называете, при каких значениях он дает сбой, на каком инструкции, это действительно дает сбой и т. д. все это поможет, например, я думаю, вы тестируете его с передачей nullptr для остатка, тогда он рухнет. Кстати, cmp ebx,0 jnae НИКОГДА не прыгнет. Вам нужно уделять больше внимания инструкции и использовать отладчик.   -  person Ped7g    schedule 05.10.2017
comment
Кстати, почему вы проверяете делитель на 0 и возвращаете какой-то неверный результат? Какова цель этого? Это не предотвратит его полный сбой, вы все равно можете сделать этот idiv сбой с -2147483648/-1 = +2147483648 (DE = ошибка деления из-за переполнения). Так что, если вам нужно какое-то неточное быстрое деление с защитой от сбоев, это не сработает для этого другого особого случая. Скорее правильно обрабатывайте исключение DE в коде, который нуждается в разделении.   -  person Ped7g    schedule 05.10.2017
comment
@ Ped7g Я не собирался троллить или обманывать людей. Я просто попытаюсь разобраться в этом сам. Спасибо за помощь. Извините, если я не был достаточно подробным для всех здесь.   -  person Jaquai    schedule 05.10.2017


Ответы (2)


cdq / idiv ebx вызовет #DE (исключение разделения) только в 2 случаях:

  • eax = что угодно, ebx = 0. Разделить на ноль.
  • eax = 0x80000000, ebx = -1. Это переполняется, потому что правильный ответ не помещается в eax. Самое отрицательное число (наибольшая величина) в дополнении до 2 со знаком не имеет обратного. -2^31 подходит для 32-битного целого числа со знаком, а +2^31 — нет. (В C именно поэтому INT_MIN / -1 является неопределенным поведением.) См. fpe">Почему целочисленное деление на -1 (отрицательное) приводит к FPE? подробнее.

У cdq/idiv ebx нет возможности ошибиться с положительным делимым и отрицательным делителем, потому что переполнение невозможно. Вы правильно используете cdq для расширения знака eax до edx:eax. (Без этого 64b / 32b => 32b деление легко может привести к переполнению результата.)

Если у вас не происходит сбой на самом idiv, значит, у вас другая ошибка, и вам следует выполнить пошаговый код в отладчике. Советы по отладка с помощью GDB или Visual Studio.

person Peter Cordes    schedule 04.10.2017
comment
Я немного обновил его: в разделе «je DivZero» я добавил «cmp ebx, 0», а затем «jnae DivNeg». В цикле «DivNeg:» обновлениями были «mov ebx, 0», «cmp ebx, -1», «jge Done2», «mov eax, -1», «neg eax», «je Done2». Когда я отлаживал программу, она показывала правильный результат/остаток, но затем она снова падала, указывая на «mov [ebx], edx» в цикле «Done1». - person Jaquai; 05.10.2017
comment
@Jaquai: посмотрите на значение в ebx и выясните, почему это недопустимый указатель. Выполняйте пошаговый код из более раннего кода программы и наблюдайте за изменением значений регистров, чтобы увидеть, как вы туда попали. - person Peter Cordes; 05.10.2017
comment
В цикле Done1 предполагается, что [ebx] будет результатом, а edx — остатком. Даже после отладки программы я до сих пор не могу понять, почему она зависла. Все остальное работает нормально (кроме деления на минусы). Кроме того, ничто не переходит к Done1 в цикле DivNeg. - person Jaquai; 05.10.2017

mov eax, [esp + 4]  ; First address ; this is the dividend
mov ebx, [esp + 8]  ; Second address ; this is the divisor
...
mov ebx, [esp + 12] ; Third address; this is the remainder

Эти комментарии указывают на то, что аргументами вашей функции являются адреса. Это означает, что вам нужно разыменовать, прежде чем вы сможете выполнять какую-либо работу с самими значениями. Вы сделали это правильно для 3-го аргумента, но не смогли с 1-м и 2-м аргументами!

mov  eax, [esp + 4]  ; First address
mov  eax, [eax]      ; this is the dividend
mov  ebx, [esp + 8]  ; Second address
mov  ebx, [ebx]      ; this is the divisor

cmp ebx, 0
je  DivZero
cmp ebx, 0
jnae    DivNeg

Вам не нужно повторять инструкцию cmp. Флаги остаются установленными для вашего второго условного перехода. Кроме того, поскольку условие эквалайзера было исключено, лучше всего использовать jna, а еще лучше использовать jl.

cmp  ebx, 0
je   DivZero
jl   DivNeg

Done1:
 mov [ebx], edx
 je  Done1

Очень проблематичный код! (Бесконечный цикл против нежелательного провала).
Лучше напишите:

Done1:
  mov  [ebx], edx   ;Return remainder
  jmp  Done2

На вашем месте я бы поместил метку Done1 на 3 строки выше, чтобы всегда выполнялась проверка на наличие нулевого указателя.

Done1:
  mov  ebx,[esp +12]   ; Needed for the remainder if divided by 0
  cmp  ebx, 0
  je   Done2
  mov  [ebx], edx
  jmp  Done2

Когда и делимое, и делитель положительны, вы можете безопасно использовать div вместо idiv. Когда делимое положительное, а делитель отрицательный, вы можете инвертировать делитель, используя div, как и раньше, но отрицая частное.

DivNeg:
  neg  ebx
  cdq
  div  ebx
  neg  eax
  jmp  Done1
person Fifoernik    schedule 07.10.2017