-или отсутствие ends или endp.
7.3Программист забывает о возврате в DOS
Имеется несколько вариантов возврата в DOS. Правильно завершать работу будет, например, программа, использующая команду ret или функцию 4Ch (см. учеб.пособ., ч.1). Об ошибках при возврате из процедуры в вызвавшую программу см. подраздел 7.19.
7.4. Вызов процедуры, которая портит содержимое нужных регистров
При разработке программы на ассемблере нередко подразумевают, что при обращении к другим процедурам регистры остаются неизмененными. На самом деле это не так. Каждая процедура может сохранить или уничтожить содержимое любого из регистров (см. учеб.пособ., ч.1, 2 и 6).
7.5. Неопределенные символические имена
Ассемблер генерирует сообщение о неопределенном символическом имени, если имя не определено в поле метки какойлибо строки ассемблерной программы. Неправильная запись имени является наиболее вероятной причиной такой ошибки (см. учеб.пособ., ч.1).
7.6. Повторное определение символического имени
Ассемблер формирует сообщение об ошибке повторного определения символического имени в тех случаях, когда одно и то же имя или метка два и более раз повторяются в поле метки ассемблерных строк программы (см. учеб.пособ., ч.1).
7.7. Неправильный порядок операндов
Многие программисты ошибаются и изменяют порядок операндов в инструкциях процессора 8086 на обратный (см. учеб.пособ., ч.1).
7.8. Неправильное использование операндов
51
7.8.1. В языке ассемблера первый операнд всегда используется в качестве приемника (кроме синтаксиса АТ&T для UNIX и Linux, см. учеб.пособ., ч.12). Операнд может быть записан в регистре общего назначения, регистре сегмента или ячейке памяти. Генерируется сообщение об ошибке при использовании непосредственных данных в качестве операнда-приемника:
cmp З,al; нельзя заменить значение 3 содержимым регистраal. Правильная запись:
cmp al,3 ; (содержимое регистра al станет равным 3).
7.8.2. Нельзя задавать 8- и 16-разрядный (а для процессора 80386 также 8- и 32-разрядный или 16- и 32-разрядный) операнды одной команды (это правило не распространяется на команды movsx и movzx, см. учеб.пособ., ч.7). Ошибочны следующие команды (несоответствие типов):
mov al, w1 ; al – однобайтовый регистр;
;w1 – однословная переменная памяти ( два байта);
mov ax, b1 ; ax – однословный регистр;
;b1 – однобайтовая переменная.
7.8.3.Операции со стеком не должны содержать переменные памяти (разрешаются – только однословные регистры). Если 8- разрядную или 16-разрядную переменную указывают для работы со стеком (push/pop), то выдается сообщение об ошибке. Примеры:
а) push w1 ; правильно будет: mov ax,w1 и затем push ax б) pop w1 ; правильно будет: pop ax и затем mov w1,ax
в) push b1 |
; правильно: mov al,b1 и затем xor ah,ah и push |
ax |
|
г) pop b1 |
; правильно будет: pop ax и затем mov al,b1/ |
Примечание. Использование стека для временного хранения однобайтовых переменных (см. примеры в) и г)) не эффективно.
7.9. Потеря содержимого регистра при умножении
Всегда нужно помнить о том, что при использовании инструкций mul и imul изменяется (не сохраняется) содержимое не только регистров al, ax или eax, но также и ah, dx или edx (см. учеб.пособ.,
ч. 1).
7.10. Не подготовлены регистры при делении
52
Распространенной ошибкой для случаев, когда делимое по размеру совпадает с делителем и размещено в al, ax или eax, является то, что программист не обнуляет регистры ah, dx, edx (см. учеб.пособ.,
ч.1).
7.11. Неправильное использование регистра после деления
Не учитывается, что регистры ah и dx содержат остаток, и используют их в качестве старших разрядов частного (см. учеб.пособ., ч. 1).
Пример ошибочного использования регистра edx после деления учетверенного слова на двойное слово приведен в учеб.пособ., ч.7.
7.12. Потеря содержимого регистра при делении
Потеря содержимого регистра при делении заключается в том, что теряется значение, помещенное в регистр до операции деления (значение регистров al, ah , ax, dx, eax, edx,, помещенное в регистр до операции деления, теряется, т.к. в регистр будет помещено частное (в регистры al, ax, eax) или остаток от деления (в регистры ah , dx, edx), см. учеб.пособ., ч. 1 и 7.
7.13.Неправильное использование регистров
вкомандах cbw и cwd
Неправильное использование регистров при преобразованиях заключается либо в ошибочном размещении исходного значения, либо в не использовании в качестве результата регистров ax, (dx:ax). Для cbw и cwd исходные значения должны быть размещеныв регистрах al и ax соответственно. Программисты же иногда ошибочно помещают исходное значение в какой-нибудь другой регистр (например, в bl или bx).
7.14.Применение команд cbw и cwd
кбеззнаковым данным
При применении команд cbw и cwd к беззнаковым данным может возникнуть ошибка. Когда знаковый бит равен единице, старший байт или слово соответственно заполняется единицами, а не
53
нулями, как требуется в этом случае. Это надо учитывать при выполнении логических операций и операций сдвига и вместо инструкций cbw и cwd применять зануление старшего байта или слова командой xor (см. учеб.пособ., ч.1).
7.15. Изменение отдельными инструкциями флага переноса
Некоторые инструкции не влияют на те флаги, изменения которых вы ожидаете. Например, инструкция inc в случае слишком большого для операнда-приемника результата на флаг cf не влияет.
То же самое имеет место для инструкций dec, loop, loopz и loopnz (не влияют на состояние флагов, см. учеб.пособ., ч.6 и 7).
Это можно использовать в тех случаях, когда нужно выполнить одну из этих инструкций без нарушения установки флага переноса.
7.16. Программист долго не использует состояние флагов
Состояние флагов сохраняется до тех пор, пока следующая инструкция их не изменит. Обычно это не слишком долгий интервал времени. Хорошей практикой программирования является возможно скорейшее использование флагов, что позволяет избежать многих потенциальных ошибок.
7.17. Ошибки при использовании регистров
При использовании регистров для записи операндов в ассемблерной программе необходимо соблюдать следующие правила:
7.17.1.Нельзя задавать 8- и 16-разрядный (а для процессора 80386 также 8- и 32-разрядный или 16- и 32-разрядный) регистры для записи двух операндов одной команды (это правило не распространяется на команды movsx и movzx, см. учеб.пособ., ч.7).
7.17.2.Операции со стеком должны осуществляться только в шестнадцатибитовых кодах. Если 8-разрядный регистр указывают для работы со стеком (push/pop), то выдается сообщение об ошибке.
7.17.3.Сегментные регистры не могут прямо использоваться в арифметических вычислениях, логических операциях или для непосредственной передачи данных. Кроме того, сегментные регистры
54
могут быть использованы либо как источники операндов, либо как приемники операндов, но никак не одновременно (см. учеб.пособ., ч. 1).
7.18. Выход из диапазона адресов
Все команды условного перехода используют режим относительной адресации с 8-битовым смещением. Если относительный адрес символа, используемого в качестве операндаадреса перехода, выходит за диапазон + 127 байтов - 128 байтов от конца команды условного перехода, выдается сообщение об ошибке (см. учеб.пособ., ч. 1)
7.19. Программист забывает об инструкции ret
Правильный вызов подпрограммы состоит: а) из вызова подпрограммы из другой части кода; б) выполнения подпрограммы;
в) возврата из подпрограммы в вызывающую программу.
Если не включить в процедуру инструкцию ret, по которой управление будет передаваться в вызывающий код, то продолжается выполнение кода, расположенного непосредственно после процедуры.
7.20. Генерация неверного типа возврата
Директива proc, во-первых, определяет имя, по которому будет вызываться процедура, и, во-вторых, управляет типом (ближним или дальним) процедуры.
Тип процедуры используется ассемблером для определения того, какой тип вызовов нужно генерировать при вызове процедуры из того же исходного файла и для определения типа инструкции ret, которая выполняется, когда процедура возвращает управление. Например:
; Подпрограмма ближнего типа для сдвига dx:ax вправо на 2 байта.
LongShiftRight2 proc near
shr |
dx,1 |
|
rcr |
ax,1 |
; сдвиг dx:ax вправо на 1 бит |
shr |
dx,1 |
|
rcr |
ax,1 |
; сдвиг dx:ax вправо еще на 1 бит |
|
|
55 |