Ассемблер зациклился

102
08 января 2022, 07:20

Есть задание "Образовать строку из исходной, повторив 1-й и элемент 1 раз, 2-й элемент 2 раз, 3 и элемент – 3 раза и тд." на с++ с ассемблерными вставками. написал программу:

#include <iostream>
using namespace std;
void readName(char a)
{
    cout << a;
}
int main()
{
    char mass[7] = "stroka";
    char a='a';
    int len = 6;
    __asm
    {
        mov esi, -1
        mov ebx, 1
        mov ecx, 0
        go:
            inc esi
            inc ecx
            jmp m1 
            cmp esi, len
        jne go
        m1 :
            mov al, mass[esi]
            push ax
            call readName
            inc ebx
            cmp ebx, ecx        
        jne m1
        mov ax, 1
    }
    system("pause");
    return 0;
}

код должен работать по аналогии с кодом на с++:

char str[] = "stroka";
int k=1;
string newStr;
for (int i = 0; i < strlen(str); i++) {
  for (int j = 0; j < k; j++) {
    newStr += str[i];
  }
  k++;
}

но почему-то зацикливается на выведении первого символа. Помогите, пожалуйста) P.S. все делал в vs 2019

Answer 1

Для начала основы: пользовательский код может изменять регистры eax, edx, ecx, а все остальные регистры, если изменяются, должны восстановить свои прежние значения. Отсюда две ваших ошибки

  1. Вы легко модифицируете регистры esi, ebx
  2. Вы надеетесь, что функция printName не изменит ваши регистры

Если эти две ошибки исправить и немного подрихтовать код, то получится так

#include <iostream>
using namespace std;
void __cdecl printChar(char a)
{
    cout << a;
}
int main()
{
    char mass[7] = "stroka";
    __asm
    {
        pushad  // сохраняем все регистры в стек
        pushfd  // сохраняем регистр флагов в стек
        lea esi, mass  // загружаем в esi указатель на первый символ
        cld
        mov ebx, 1  // текущий индекс символа
        push eax  // зарезервировали место в стеке
        go:
            lodsb  // загружаем текущий символ в al
            test al, al  // если загрузили \0, 
            jz exit_  // то выходим
            mov edi, ebx  // в edi счетчик текущего символа
            mov [esp], eax  // а сам символ в стек
            loopChar:
                call printChar
                dec edi  // уменьшили счетчик
                jz endChar  // если счетчик = 0, то вышли из цикла
            jmp loopChar  // в цикле выводим символ
            endChar:
            inc ebx  // увеличили текущий индекс
        jmp go  // повторили операцию 
        exit_:
            pop eax  // не забыли достать символ из стека
            popfd     // восстановили регистр флагов
            popad   // восстановили регистры
    }
    system("pause");
    return 0;
}
Answer 2

В коде много бессмыслицы, но сразу можно заметить:

  1. Вызов функции readName будет разрушать содержимое некоторых регистров. В частности ecx. Поэтому ваши надежды на то, что содержимое ecx сохранится, необоснованы.

  2. В чем вообще заключется идея цикла

    m1 :
    ...
    cmp ebx, ecx        
    jne m1
    

    ?

    Вы сами внутри этого цикла ничего не меняете: ни ebx, ни ecx. Почему вы ожидаете, что этот цикл когда-то закончится?

READ ALSO
Принципы ООП. Интерфейс

Принципы ООП. Интерфейс

Пишу класс для работы с матрицамиУказатель и кол-во строк и столбцов сокрыто, это понятно

95
Получение NULL при побитовой конъюнкции

Получение NULL при побитовой конъюнкции

В проекте есть следующий код:

92