Не выполняется примитивная операция деления. Почему?

Тема в разделе "WASM.BEGINNERS", создана пользователем amvoz, 29 янв 2009.

  1. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    Код (Text):
    1. .386
    2. .model flat, stdcall
    3. option casemap:none
    4.  
    5. include C:\masm32\include\windows.inc
    6. include C:\masm32\include\kernel32.inc
    7. includelib c:\masm32\lib\kernel32.lib
    8.  
    9. .code
    10. start:
    11.     mov EAX, 1000h
    12.     mov     ECX, 10h
    13.     div     CX          
    14. invoke ExitProcess, 0
    15.  
    16. end start
    То есть компилируется и на-гора выдаётся экзешник. Но при запуске ведёт себя издевательски прямо. Запускаю файл <имя файла>.exe и включается отладчик OllyDbg (он у меня по умолчанию JIT, правда, не понимаю, что это такое). И видно, что выполнение программы прерывается на строчке
    div CX
    ...Всё, хоть занажимайся F7, F8 или F9. Причина остановки тоже мне непонятна
    "Integer overflow- use Shift+ F7/ F8/ F9 to pass exception to program"
    Shift+ F7/ F8/ F9 уводит в дебри те ещё. На какой-то адрес 7С90EAF0


    В чём дело? Переписываю исходник, с таким вот изменением:
    ;mov ECX, 10h
    То есть закомментирываю строчку. Программа выполняется на "ура"

    Рассуждаь не берусь, вообще это выше моего понимания. Ладно бы там на ноль было деление или ещё что-нибудь- так нет же, самая безобидная операция. И числа красивые и на тебе. Я что мог сделать-

    1) использую в качестве частного содержимое регистра ECX, он не задействован Windows и он не задействован операцией DIV, то есть в нём не сохраняется ни остаток, ни частное

    2) Вычитал где-то, что по длине делитель должен быть в два раза больше частного. Использую поэтому регистр CX, но не ECX
    То есть с этих сторон подвоха быть не должно.

    Всю ночь пыхтел искал в Google пример программы (!) деления, не просто как операнд DIV использовать (этого полно), а именно программу маленькую, но ничего не нашёл. Вернее нашёл какой-то один пример, но он мне не помог. Спасибо, кто откликнется и наставит на путь истиный.
     
  2. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.914
    amvoz
    для начала, делимое должно располагаться при делении на двойное слово в edx:eax, при делении на слово в dx:ax, при делении на байт в ax. Если при делении на двойное слово делитель меньше или равен edx - возникнет переполнение о чем добросовестно OllyDbg и предупреждает "Integer overflow- use Shift+ F7/ F8/ F9 to pass exception to program". В твоем случае нужно было обнулить edx или, если eax<80000000h, ипользовать команду cdq. При делении на двойное слово/слово/байт остаток помещается в edx/dx/ah, частное в eax/ax/al. Вообще-то, при делении на степень двойки следовало воспользоваться сдвигом вправо -- и кода меньше, и выполняется быстрее, т.е. mov ECX,10h/div ECX эквивалентно shr EAX,4 недавно подобную проблему обсуждали здесь
     
  3. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
  4. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    Мда...
    Прочитав пару-тройку Таких вот...э... правил, я думал, что вопрос себя исчерпал.
    Спасибо.
     
  5. SII

    SII Воин против дзена

    Публикаций:
    0
    Регистрация:
    31 окт 2007
    Сообщения:
    1.483
    Адрес:
    Подмосковье
    amvoz
    Если возникают непонятки с какими-то командами, неплохо почитать описание этих команд в интеловском (или АМДшном) мануале. Например, в данном случае Вы бы сразу увидели, что делимое находится в регистровой паре ДХ:АХ, а не в одном регистре.
     
  6. Memphis

    Memphis New Member

    Публикаций:
    0
    Регистрация:
    23 окт 2008
    Сообщения:
    104
    amvoz
    mov EAX, 1000h
    mov ECX, 10h
    div CX

    Ну и чего непонятно ? Делишь на word - значит надо проинитить DX:AX. AX - вижу, в норме. А если DX в момент деления будет иметь значение 10h или более - переполнение тебе обеспечено. Не забывай - делимое при делении на ворд надо поместить в DX:AX.
     
  7. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    Хорошо. Я понимаю всё, что вы говорите (или мне кажется, что понимаю), но не понимаю следующего обстоятельства:

    ...Вот этот участок кода выполняется
    Код (Text):
    1.     mov     EAX, 2559;
    2.     mov     edx, 10;
    3.     div     DL
    А этот нет
    Код (Text):
    1.     mov     EAX, 2560;
    2.     mov     edx, 10;
    3.     div     DL
    ...Перед выполнением div DL имеем состояние регистров (смотрено в OllyDbg)
    Впервом случае
    EAX 000009FF
    EDX 0000000A

    Во втором
    EAX 00000A00
    EDX 0000000A

    ...В общем, просто не могу увидеть качественной разницы между регистрами EAX, обуславливающие деление в первом случае и неделение во втором. Хотя бы делимые разную длину имели, что ли, так нет же, и в том и в другом случае заполнено по 3 младших полубайта. Покажите уж.
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    amvoz
    Э-э, как бы это помягче...
    div dl делит ax=ah:al на dl и помещает результат в al. "Качественная разница" в том, что в первом случае получается число 255 (и 9 в остатке), которое умещается в al (0..255), а во втором случае получается 256 (и 0 в остатке), что и вызывает переполнение

    Заруби на носу:
    div ecx делит edx:eax на ecx и помещает рез-т в eax, остаток в edx --> переполнение при edx >= ecx
    div cx делит dx:ax на cx и помещает рез-т в ax, остаток в dx --> переполнение при dx >= cx
    div cl делит ax==ah:al на cl и помещает рез-т в al, остаток в ah --> переполнение при ah >= cl (как в твоем случае: 0A = 0A)
     
  9. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    Угу. То есть смотрим частное и остаток, чтобы они влазили куда надо. В этом случае куда они должны влазить, Вы сказали, а если буду делить переменную типа DWORD на WORD, то необходимо будет следить за тем, чтобы частное влазило в AX, а остаток деления в DX. Благодаря Вам я это понял (ещё во втором случае слежу за тем, чтобы делимое было в dx:ax). А если на двойное слово делим, то тоже ищем в Google данные по этому действию. Это понятно. Один вопрос отстался.
    ...Вот, допустим по моему варианту- как я должен знать, влезет частное в al или нет? Я должен найти частное на калькуляторе и определить, влезет оно в al или нет. Так ведь? А если я его нашёл на калькуляторе- какой смысл мне реализовывать операции деления на ассемблере?

    То есть: если при написании программ на ассемблере, где я ищу некотрое частное, я всякий раз вынужден буду предварительно искать его на калькуляторе, чтобы проверить, влезет оно в предназначенное место или нет... Ну, вы все поняли нелепость, я думаю... Что-то я не понимаю, как мне деление на ассемблере реализовывать, минуя калькулятор. Честно.
     
  10. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    Здесь у Вас всё верно? В моём случае остаток (ah) не был больше либо равен cl Он был меньше cl (остаток был 0, а cl равнялось 10). И получилось переполнение.
     
  11. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    В общем, действовать, наверное, надо с размахом. То есть использовать на полную катушку регистры EAX и EDX- только так возможно максимально себя обезопасить от переполнения (полностью его исключить, конечно, нельзя). То есть код в моём случае надо так переписать:
    Код (Text):
    1.    mov     EAX, 2560
    2.     mov     ECX, 10
    3.     mov     EDX, 0 ; Это чтобы содерджание "обобщённого" регистра равнялось 2560
    4.     div     ECX
    Вот так. Табличку Вашу я переписываю себе, а всё-таки условие переполнения Вы пересмотрите. Спасибо.
     
  12. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    amvoz
    Просто деление 64 бит чисел делается немного по другому, а именно в два прохода.
     
  13. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    amvoz
    У-уф, ну ты, батенька, даешь ?!! Как остаток может куда то не влезть, если он по определению меньше делителя ?!!
    Переполнение происходит, если не остаток, а старшая половина исходного делимого операнда, т.е. edx, dx или ah до выполнения div, больше или равна делителю, т.е. ecx, cx или cl соотв-но.

    Чего тут непонятного ? Запись делимого в виде edx:eax означает 64-битное число X=edx*2^32+eax, поэтому если edx < ecx, то после деления мы получим число Y=0*2^32+Z, где Z < 2^32, т.е. умещается в eax. Если же edx = ecx, то в итоге получится число Y=1*2^32+Z, т.е. больше 2^32 и возникнет ошибка переполнения. Аналогично и для dx:ax значение dx должно быть меньше cx, а для ah:al значение ah должно быть меньше cl - тогда ошибок не будет
     
  14. amvoz

    amvoz Member

    Публикаций:
    0
    Регистрация:
    12 ноя 2008
    Сообщения:
    653
    Разобрался с формулами. Насчёт остатка. Вношу ясность в формулировки: коль скоро остаток по определению меньше частного, а заносим мы их в подрегистры одинаковые по размеру, то достаточно проверить, влезает ли частное в отведённый ему подрегистр. Если влезает- значит, влезает и остаток и нет нужды о нём беспокоиться. Не влезает частное- тем более нет нужды. Спасибо.
     
  15. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.914
    amvoz я тебе еще в #2 ссылку давал, там был код, на который ты не обратил внимание
    Код (Text):
    1. ;слово на байт
    2. CMP AH,DIVBYTE
    3. JNB переполнение
    4. DIV DIVBYTE
    5. . . .
    6. ;учетверенное слово на двойное слово
    7. cmp edx,DIVDWORD
    8. jnb переполнение
    9. DIV DIVDWORD
    будь внимательнее!
     
  16. Memphis

    Memphis New Member

    Публикаций:
    0
    Регистрация:
    23 окт 2008
    Сообщения:
    104
    amvoz
    ...Перед выполнением div DL имеем состояние регистров (смотрено в OllyDbg)
    Впервом случае
    EAX 000009FF
    EDX 0000000A

    Во втором
    EAX 00000A00
    EDX 0000000A


    http://www.wasm.ru/forum/viewtopic.php?pid=291605#p291605 - читай мой пост #10.
     
  17. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.914
    Только, что пришло в голову -- получается, что 0xF7F2 (div rdx/edx/dx), 0xF7FA (idiv rdx/edx/dx), 0xF6F4 (div ah), 0xF6FC (idiv ah) -- коды "запрещенных команд" , которые использовать для операции деления нельзя -- так как они вызывут переполнение при любом значении rdx/edx/dx/ah -- стало быть их нужно использовать для кодирования чего-нибудь полезного :)