Страницы

Поиск по вопросам

пятница, 10 января 2020 г.

Не могу найти ошибку в АСМ модуле для С

#c #ассемблер


Пишу модуль для с-шной программы на nasm - сортировка пузырьком.
Программы почему-то вылетает при исполнении. Целые сутки пытался понять причину -
не вышло.
Пробовал разные реализации этого когда - бесполезно.
NASM ошибок не выдает, а модуль при исполнении вылетает.
Модуль АСМ:
global _bubblesort_asm
section .text

_bubblesort_asm:
    mov ebx, [esp+4] ; EBX = *m
    mov esi, [esp+8] ; ESI = size
    dec esi ; ESI = size-1 = I
    loop_i:
        xor edi, edi ; J = 0
        loop_j:
            mov ecx, [ebx+edi*4] ; ecx = value of m[J]
            mov edx, [ebx+edi*4+4] ; edx = value of m[j+1]
            cmp edx, ecx ; if(m[j] <= m[j+1])  goto skip
            jns skip
                mov [ebx+edi*4], edx ; mov value m[j+1] to m[j] adress
                mov [ebx+edi*4+4], ecx ; mov value m[j] to m[j+1] adress
            skip:
        inc edi ; j++
        cmp edi, esi ; if(j!=i) goto loop_j
        jnz loop_j
    dec esi ; I--
    cmp esi, 0 ; if(i>=0) goto loop_i
    jns loop_i
retn
;    for(i=size-1; i>=0; i--)
;        for(j=0; j m[j+1])
;                {temp = m[j]; m[j] = m[j+1]; m[j+1] = temp;}

main.c:
int main()
{
    srand(time(NULL));
    int i,size=100,m[size];
    for(i=0;i


Ответы

Ответ 1



Скомпилировал сишную сортировку с помощью mingw gcc с оптимизацей -O2, дизассемблировал, получилось вот что: .text:004013C5 mov edx, [ebp+m] .text:004013C8 mov esi, [ebp+size] .text:004013CB dec esi .text:004013CC js short loc_4013F5 .text:004013CE db 66h ; выравнивание .text:004013CE nop ; выравнивание .text:004013D0 .text:004013D0 loc_4013D0: ; CODE XREF: bubblesort+33j .text:004013D0 test esi, esi .text:004013D2 jle short loc_4013EF .text:004013D4 xor eax, eax .text:004013D6 db 66h ; выравнивание .text:004013D6 nop ; выравнивание .text:004013D8 .text:004013D8 loc_4013D8: ; CODE XREF: bubblesort+2Dj .text:004013D8 mov ecx, [edx+eax*4] .text:004013DB mov ebx, [edx+eax*4+4] .text:004013DF cmp ecx, ebx .text:004013E1 jle short loc_4013EA .text:004013E3 mov [edx+eax*4], ebx .text:004013E6 mov [edx+eax*4+4], ecx .text:004013EA .text:004013EA loc_4013EA: ; CODE XREF: bubblesort+21j .text:004013EA inc eax .text:004013EB cmp eax, esi .text:004013ED jnz short loc_4013D8 .text:004013EF .text:004013EF loc_4013EF: ; CODE XREF: bubblesort+12j .text:004013EF dec esi .text:004013F0 cmp esi, -1 .text:004013F3 jnz short loc_4013D0 .text:004013F5 .text:004013F5 loc_4013F5: ; CODE XREF: bubblesort+Cj Код почти идентичен тому что в вопросе, отличается некоторыми деталями, плюс gcc добавил выравнивание (66h - это префикс-модификатор размера операнда, перед nop он никаких функций не выполняет). Дополнение про различие js и jl. По сути cmp - это вычитание, не сохраняющее результат, но выставляющее флаги. Если сравнивать небольшие по модулю числа, то разницы в результатах не будет. Но если сравнивать числа, при вычитании дающие результат по модулю больший половины размерности переменной (регистра), то sf будет иметь не то значение, которое могло бы быть при сравнении меньших по модулю чисел. На примере величин в пределах байта. Сравним -10 и 10 внутри байтовых регистров: mov al, -10 mov cl, 10 cmp al, cl ; результат вычитания -20, флаг SF установлен ; и по js и по jl переход произойдет Теперь на примере чисел -100 и 100: mov al, -100 mov cl, 100 cmp al, cl ; результат вычитания "на бумаге" -200 ; но в пределах байта это уже не -200, а 56, флаг знака сброшен, ; поэтому по js переход не произойдет. ; Зато выставлен флаг переполнения - OF ; По jl, которое проверяет и SF, и OF, переход произойдет. Дополнение 2. Под половиной разрядности я имею в виду вот что: к примеру в байтовом регистре могут храниться знаковые числа от -128 до +127. В вордовом - от -2^15 до +2^15-1, в двордовом - от -2^31 до +2^31-1. Соответственно, если результат вычитания выходит за эти границы, то знак результата вычитания оказывается неверным (да и сам результат будет неверным, но в случае cmp он нас не интересует) и выставляется флаг переполнения. Поэтому чтобы определить, что одно число больше чем другое, нужно учитывать и флаг знака, и флаг переполнения, что и делают команды переходов jl (jnge), jnl (jge), jg (jnle), jng (jle). По поводу вылета при условии выхода из внешнего цикла cmp esi, 0 ; jns loop_i. Последний виток внешний цикл должен выполнить при esi==0. Тогда во внутреннем цикле произойдет вот что: edi обнулится при необходимости выполнится операция перестановки соседних элементов массива edi увеличится на 1 произойдет сравнение edi (1) с esi (0) так как они не равны (edi уже больше чем esi), то выход из цикла не произойдет далее edi будет увеличиваться и увеличиваться, пока не дойдет до конца доступного для записи участка памяти. Если заменить cmp esi,0 на cmp esi,1, то последний виток внешний цикл выполнит при esi==1, и внутренний цикл завершится, выполнив одну итерацию. Но тут все равно возникают проблемы, если массив состоит из одного элемента или вообще пуст, тогда esi уже при проверке условия выхода из внутреннего цикла будет меньше чем edi и условие выхода опять не сработает. Чтобы избежать такого "проскакивания" внутреннего цикла и нужно добавить проверку перед входом в него ("предусловие").

Комментариев нет:

Отправить комментарий