Код (Text): .386 .model flat, stdcall option casemap:none include C:\masm32\include\windows.inc include C:\masm32\include\kernel32.inc includelib c:\masm32\lib\kernel32.lib .code start: mov EAX, 1000h mov ECX, 10h div CX invoke ExitProcess, 0 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 использовать (этого полно), а именно программу маленькую, но ничего не нашёл. Вернее нашёл какой-то один пример, но он мне не помог. Спасибо, кто откликнется и наставит на путь истиный.
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 недавно подобную проблему обсуждали здесь
amvoz Если возникают непонятки с какими-то командами, неплохо почитать описание этих команд в интеловском (или АМДшном) мануале. Например, в данном случае Вы бы сразу увидели, что делимое находится в регистровой паре ДХ:АХ, а не в одном регистре.
amvoz mov EAX, 1000h mov ECX, 10h div CX Ну и чего непонятно ? Делишь на word - значит надо проинитить DX:AX. AX - вижу, в норме. А если DX в момент деления будет иметь значение 10h или более - переполнение тебе обеспечено. Не забывай - делимое при делении на ворд надо поместить в DX:AX.
Хорошо. Я понимаю всё, что вы говорите (или мне кажется, что понимаю), но не понимаю следующего обстоятельства: ...Вот этот участок кода выполняется Код (Text): mov EAX, 2559; mov edx, 10; div DL А этот нет Код (Text): mov EAX, 2560; mov edx, 10; div DL ...Перед выполнением div DL имеем состояние регистров (смотрено в OllyDbg) Впервом случае EAX 000009FF EDX 0000000A Во втором EAX 00000A00 EDX 0000000A ...В общем, просто не могу увидеть качественной разницы между регистрами EAX, обуславливающие деление в первом случае и неделение во втором. Хотя бы делимые разную длину имели, что ли, так нет же, и в том и в другом случае заполнено по 3 младших полубайта. Покажите уж.
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)
Угу. То есть смотрим частное и остаток, чтобы они влазили куда надо. В этом случае куда они должны влазить, Вы сказали, а если буду делить переменную типа DWORD на WORD, то необходимо будет следить за тем, чтобы частное влазило в AX, а остаток деления в DX. Благодаря Вам я это понял (ещё во втором случае слежу за тем, чтобы делимое было в dx:ax). А если на двойное слово делим, то тоже ищем в Google данные по этому действию. Это понятно. Один вопрос отстался. ...Вот, допустим по моему варианту- как я должен знать, влезет частное в al или нет? Я должен найти частное на калькуляторе и определить, влезет оно в al или нет. Так ведь? А если я его нашёл на калькуляторе- какой смысл мне реализовывать операции деления на ассемблере? То есть: если при написании программ на ассемблере, где я ищу некотрое частное, я всякий раз вынужден буду предварительно искать его на калькуляторе, чтобы проверить, влезет оно в предназначенное место или нет... Ну, вы все поняли нелепость, я думаю... Что-то я не понимаю, как мне деление на ассемблере реализовывать, минуя калькулятор. Честно.
Здесь у Вас всё верно? В моём случае остаток (ah) не был больше либо равен cl Он был меньше cl (остаток был 0, а cl равнялось 10). И получилось переполнение.
В общем, действовать, наверное, надо с размахом. То есть использовать на полную катушку регистры EAX и EDX- только так возможно максимально себя обезопасить от переполнения (полностью его исключить, конечно, нельзя). То есть код в моём случае надо так переписать: Код (Text): mov EAX, 2560 mov ECX, 10 mov EDX, 0 ; Это чтобы содерджание "обобщённого" регистра равнялось 2560 div ECX Вот так. Табличку Вашу я переписываю себе, а всё-таки условие переполнения Вы пересмотрите. Спасибо.
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 - тогда ошибок не будет
Разобрался с формулами. Насчёт остатка. Вношу ясность в формулировки: коль скоро остаток по определению меньше частного, а заносим мы их в подрегистры одинаковые по размеру, то достаточно проверить, влезает ли частное в отведённый ему подрегистр. Если влезает- значит, влезает и остаток и нет нужды о нём беспокоиться. Не влезает частное- тем более нет нужды. Спасибо.
amvoz я тебе еще в #2 ссылку давал, там был код, на который ты не обратил внимание Код (Text): ;слово на байт CMP AH,DIVBYTE JNB переполнение DIV DIVBYTE . . . ;учетверенное слово на двойное слово cmp edx,DIVDWORD jnb переполнение DIV DIVDWORD будь внимательнее!
amvoz ...Перед выполнением div DL имеем состояние регистров (смотрено в OllyDbg) Впервом случае EAX 000009FF EDX 0000000A Во втором EAX 00000A00 EDX 0000000A http://www.wasm.ru/forum/viewtopic.php?pid=291605#p291605 - читай мой пост #10.
Только, что пришло в голову -- получается, что 0xF7F2 (div rdx/edx/dx), 0xF7FA (idiv rdx/edx/dx), 0xF6F4 (div ah), 0xF6FC (idiv ah) -- коды "запрещенных команд" , которые использовать для операции деления нельзя -- так как они вызывут переполнение при любом значении rdx/edx/dx/ah -- стало быть их нужно использовать для кодирования чего-нибудь полезного