Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

epd627

.pdf
Скачиваний:
23
Добавлен:
02.05.2015
Размер:
816.97 Кб
Скачать

. . .

;Подпрограмма для деления значения на 10.

;Ввод: ax – значение, которое требуется разделить на 10

;Вывод: ax – значение, разделенное на 10

;dx – остаток значения, деленного на 10

DivideBy10 proc near

mov

dx,0

; подготовить dx:ax как 32-битовое делимое

mov

bx,10

; bx – 16-битовый делитель (именно здесь bx:=10)!

div bx ret

DivideBy10 endp codesg ends

В вызывающей программе подразумевается, что bx в процедуре DivideBy10 сохраняется, хотя фактически он устанавливается процедурой DivideBy10 в значение 10. В этом конкретном случае существует несколько возможных решений.

Например, способ 1 – сохранение регистра bx в вызывающей программе до вызова процедуры DivideBy10 и восстановление его тоже в вызывающей программе после вызова процедуры DivideBy10:

mov bx,1000

; bx :=1000

mov

ax,w1

; ax:=w1=200

push bx

; сохранить bx

call

DivideBy10

; разделить элемент на 10

pop

bx

; восстановить bx

add

bx,ax

; вычисление суммы bx:=bx+ax=1000+w1/10

. . .

; (в данном случае получится правильно)

либо регистр bx можно загрузить после вызова процедуры (способ 2):

mov

ax,w1

; ax:=w1=200

call

DivideBy10

; разделить элемент на 10

mov bx,1000

; bx :=1000

add

bx,ax

; вычисление суммы

или в начале процедуры DivideBy10 bx заносить содержимое регистра в стек, а при выходе из процедуры извлекать из стека (способ 3):

mov bx,1000

; bx :=1000

mov

ax,w1

; ax:=w1=200

call

DivideBy10

; разделить элемент на 10

add

bx,ax

; вычисление суммы

. . .

;Подпрограмма для деления значения на 10.

;Ввод: ax – значение, которое требуется разделить на 10

;Вывод: ax – значение, разделенное на 10

81

;

dx – остаток значения, деленного на 10

DivideBy10 proc near

push bx

; сохранить bx в стеке

mov

dx,0

; подготовить dx: ax как 32-битовое делимое

mov

bx,10

; bx –- 16-битовый делитель

div

bx

 

pop

bx

; восстановить bx (извлечь из стека

ret DivideBy10 endp

Первый подход к решению проблемы: все подпрограммы, которые могут изменять содержимое регистров, сохраняют их, а затем восстанавливают (требует дополнительного времени и увеличивает объем программы). Другой подход: каждая подпрограмма сопровождается комментарием, в котором указывается, какие регистры сохраняются, а какие разрушаются. При написании программы эта информация строго учитывается и тщательно проверяется, действительно ли в подпрограмме регистр не изменяется. Третий подход заключается в явном сохранении нужных регистров при вызове подпрограмм (см. уч. пос., ч.6).

5.6. Неопределенные символические имена [5, с.121-122]

Ассемблер генерирует сообщение о неопределенном символическом имени, если имя не определено в поле метки какой-либо строки ассемблерной программы. Если имя определено в другой программе (см. уч. пос., ч.2), то его следует определить как внешнее имя директивой extrn (external — внешний). Неправильная запись имени является наиболее вероятной причиной такой ошибки, особенно в тех случаях, когда используемые шестнадцатеричные константы записываются без начальной цифры.

secondproc near mov ax,cs mov ds,ax mov es,ax

;Неверные записи

;а) неопределенное имя value

mov

ax,value

; б) некорректная шестнадцатеричная запись

mov

bx,ffe2h; число ffe2h воспринимается как имя: нет нуля впереди

;в) неопределенное имя code mov cx,code1

mov dx,code2 mov si,code

82

ret

code1 dw 100 code2 dw 200 code3 dw 300 secondendp

Ниже приведен образец правильной записи предыдущего примера. Символическое имя value представлено 16-битовым внешним именем. Шестнадцатеричная запись числа начинается с цифры, и неправильно записанное имя code заменено на правильное code3.

secondproc near extrn value:word mov ax,cs

mov ds,ax mov es,ax

mov ax,value

 

mov bx,0ffe2h

; т.к. первый символ– цифра,

;

воспринимается как число

mov cx,code1 mov dx,code2 mov si,code3 ret

code1 dw 100 code2 dw 200 code3 dw 300 secondendp

5.7.Повторное определение символического имени [5, с.122-123]

Ассемблер формирует сообщение об ошибке, если одно и то же имя или метка два и более раз повторяется в поле метки ассемблерных строк программы.

case1:

decax ; ax:=ax-1 loop case1

and dx,bx cmp dx,0 jz case3

; Неверные записи

case1: ;имя case1 уже было определено

. . .

jz case3

83

Правильная запись предыдущего примера:

case1:

 

 

dec ax

; ax:=ax-1

; Неверные записи

 

loop

case1

 

and dx,bx

 

cmp

dx,0

 

jz case3

 

case2:

 

 

. . .

jz case3

5.8. Неправильный порядок операндов

Многие программисты ошибаются и изменяют порядок операндов в инструкциях процессора 8086 на обратный. Это, вероятно, связано с тем, что строка

mov ax,bx

которая означает "поместить bx в ax", читается слева направо, и это иногда приводит к путанице. Чтобы запомнить порядок операндов в языке ассемблера процессора 8086, нужно строку

mov ax,bx

рассматривать как ax := bx

5.9.Неправильное использование операндов

Вязыке ассемблера первый операнд всегда используется в качестве приемника (кроме синтаксиса АТ&T для UNIX и Linux, см. уч. пос., ч.12). Операнд может быть записан в регистре общего назначения, регистре сегмента или ячейке памяти. При использовании непосредственных данных в качестве операнда-приемника генерируется сообщение об ошибке:

1) неправильная запись

2) правильная запись

cmp З,al

cmp al,3

5.10. Потеря содержимого регистра при умножении

Большинство способов, с помощью которых программа на ассемблере может изменить состояние процессора, достаточно непосредственны и очевидны. Например, инструкция:

add bx,w1

значение по адресу w1 к bx и, чтобы отразить

прибавляет

16-битовое

результат

сложения,

изменяет флаги переполнения, знака, нуля,

 

 

84

дополнительного переноса, четности и переноса. Однако некоторые инструкции изменяют состояние процессора менее очевидным образом, и если программист забывает о необычных побочных эффектах, то возникает ошибка. Одной из таких ошибок является потеря содержимого регистра при умножении. Другие – такие как использование регистров ah, dx и edx (в МП 80386) после операции div и idiv в качестве старших разрядов частного, хотя содержат остаток и т.п., рассмотрены ниже (п.5.12-5.13).

В синтаксисе инструкций mul и imul явно указываются только один из операндов и размер, а регистры, используемые в качестве операндаприемника, просто подразумеваются. Это приводит к тому, что легко можно упустить из виду использование какого-либо неявного регистра. Есть много случаев, в которых, скажем, программист знает, что результат перемножения 16-разрядного значения на 16-разрядную величину, поместится в регистр ax. При этом часто забывают, что теряется содержимое регистра dx. Поэтому всегда нужно помнить о том, что при использовании инструкций mul и imul уничтожается содержимое не только регистров al, ax или eax (в МП 80386), но также и ah, dx или edx (в МП

80386).

5.11. Не подготовлены регистры при делении

Распространенной ошибкой для случаев, когда делимое по размеру совпадает с делителем и размещено в al или ax, является то, что программист не обнуляет регистры ah, dx.

Пример 1. Не очищается ah при делении байта на байт:

; нет обнуления ah или ax, т.е. пропущена команда xor ah,ah или xor ax,ax

mov al,b1

; al:=b1

div b2

; ax используется как делимое (но ah не подготовлен)

mov byte ptr rezult,al

Пример 2. Не очищается dx при делении слова на слово. ; пропущена команда зануления dx, т.е. команда xor dx,dx

mov ax,w1 ;ax:=w1

div w2 ; dx:ax используется как делимое (но dx не подготовлен) mov word ptr rezult,ax

5.12. Неправильное использование регистра после деления

Не учитывается, что регистры ah и dx содержат остаток и используют в качестве старших разрядов частного.

Пример 1. Ошибочно используется ah после деления байта на байт: mov al,b1 ; b1/b2

div b2

mov word ptr rezult,ax ;ошибка: в ah содержится остаток,

85

; а не старшие разряды частного (правильно будет mov byte ptr result,al). Пример 2. Ошибочно используется регистр dx после деления слова на

слово:

 

xor dx,dx

;\

 

 

mov ax,w1

; |w1/w2

 

 

div w2 ;/

 

 

 

mov word ptr rezult,ax

 

 

mov word ptr result+2,dx; ошибка: в dx содержится остаток, а не

;

старшие разряды частного. Правильно будет mov word ptr result+2,0

 

Пример 3. Ошибочно используется регистр dx после деления двойно-

го слова на слово:

 

 

mov ax,word ptr d1

 

 

mov dx,word ptr d1+2

 

 

div w1

 

 

 

mov word ptr rezult,ax

; результат деления d1/w1

 

mov word ptr rezult+2,dx

;ошибка: в регистре dx содержится

 

;

 

остаток, а не старшие разряды частного

 

;

 

(правильно будет mov word ptr result+2,0)

Пример ошибочного использования регистра edx после деления учетверенного слова на двойное слово приведен в уч. пос., ч.7.

5.13. Потеря содержимого регистра при делении

Потеря содержимого регистра при делении заключается в том, что теряется значение, помещенное в регистр до операции деления (значение регистров al, ah, ax, dx теряется, т.к. туда заносится частное (в регистры al, ax) или остаток от деления (в регистры ah , dx).

Пример 1. Теряется содержимое ah после деления байта на байт:

mov ah,b3

; ah:=b3

mov al,b1

; b1/b2

div b2

 

mov byte ptr rezult,al

add al,ah ; ошибка: в регистре ah содержится остаток, а не b3 ; (правильно будет команду mov ah,b3 выполнять не до деления, а после).

Пример 2. Теряется содержимое dx после деления слова на слово:

mov dx,w3

 

mov ax,w1

; w1/w2

div w2

 

mov word ptr rezult,ax

mov word ptr result+2,0 ; ошибка: в регистре dx содержится

;остаток, а не w3 (правильно будет команду mov dx,w3

;

выполнять после деления, а не до него).

86

5.14. Неправильное использование регистров в командах cbw и cwd

Неправильное использование регистров при преобразовании байта в слово и слова в двойное слово заключается либо в ошибочном размещении исходного значения, либо в использовании в качестве результата не регистров ax (dx:ax). Для cbw и cwd исходные значения должны быть размещены в регистрах al и ax, соответственно. Программисты же иногда ошибочно помещают исходное значение в какой-нибудь другой регистр (например, в bl или bx).

Пример 1.

mov bl,b3

; bl := b3

cbw

; ошибка: результат, получившийся в ax, не имеет никакого

;

отношения к регистру bl (а значит, и к b3)

Пример 2.

 

mov al,b3

; al := b3

cbw

; результат получился в регистре ax

mov rez_w,bx ; ошибка: в bx не содержится результат выполнения

;команды cbw

5.15.Применение команд преобразования cbw и cwd

кбеззнаковым данным

При применении команд cbw и cwd к беззнаковым данным может возникнуть ошибка. Когда знаковый бит равен единице, старший байт или слово соответственно заполняется единицами, а не нулями, как это требуется в данном случае. Это надо учитывать при выполнении логических операций и операций сдвига и вместо инструкций cbw и cwd применять зануление старшего байта или слова командой xor. Например, если al= 128 (т.е. 100000002), то после выполнения команды cbw в регистре ax будет число 65408 (т.е. 11111111100000002), а должно остаться равным

128, но расширенным на 16 битов (т.е. 00000000100000002).

5.16.Изменение отдельными инструкциями флага переноса

Вто время как некоторые инструкции "непредвиденным" образом изменяют содержимое регистров или флагов, другие инструкции не влияют на все те флаги, изменения которых вы ожидаете. Например, инструкция

inc ah

выглядит логически эквивалентной инструкции

add ah,1

и это действительно так, но инструкция add в случае слишком большого для операнда-приемника результата устанавливает флаг переноса, а инструкция inc на флаг cf не влияет. В результате инструкции

add ax,1

; ax := ax+1 (и cf:=1, если возник перенос)

 

87

adc dx,0 ; dx := dx+0+<значение cf>

можно использовать для увеличения 32-битового значения, хранящегося в регистрах dx: ax, а инструкции

inc ax ; ax:=ax+1 (cf не меняется, даже если возник перенос) adc dx,0

нельзя. То же самое имеет место для инструкции dec. Инструкции loop, loopz и loopnz также не влияют на состояние флагов (см. уч. пос., ч.6 и 7). Это можно использовать, например, в тех случаях, когда нужно организовать цикл и при этом не изменять значения флагов, в т.ч. и флага переноса.

5.17. Программист долго не использует состояние флагов

Состояние флагов сохраняется до тех пор, пока следующая инструкция их не изменит. Обычно это не слишком долгий интервал времени. Хорошей практикой программирования является возможно скорейшее использование флагов их установки, что позволяет избежать многих потенциальных ошибок. Например, часто хочется проверить условие, задать содержимое одного-двух регистров и только после этого в соответствии с результатом проверки выполнить переход. Инструкции:

cmp

ax,1

;вычисление (ax-1) без присвоения полученного значения

;

ах и присвоение значений (0 или 1 – зависит от результата) флагам:

;

нуля (zf), знака (sf), переноса (cf) и других (of , af и pf)

mov

ax,0

;ax:=0 значения флагов не изменяет

jg

 

Positive

; если больше (т.е если zf=0 и sf=of), переход на Positive

представляют собой допустимый способ проверки состояния регистра ax, установки его в значение 0 и обработки результата. А инструкции:

cmp

ax,1

sub

ax,ax ; присвоение значений (0 или 1 – зависит от результата)

;

флагам:нуля (zf), знака (sf), переноса (cf) и других (of , af и pf)

jg

Positive

которые выглядят более привлекательно, поскольку такой код короче и выполняется быстрее, правильно работать не будут, так как при вычитании теряется содержимое всех флагов, установленных при сравнении. Это типичная проблема, возникающая, когда не торопятся проверить флаг.

5.18. Ошибки при использовании регистров

При использовании регистров для записи операндов в ассемблерной программе необходимо соблюдать следующие правила:

1.Нельзя задавать 8- и 16-разрядный (а для процессора 80386 также 8-

и32-разрядный или 16- и 32-разрядный) регистры для записи двух

88

операндов одной команды (это правило не распространяется на команды movsx и movzx, см. уч. пос., ч.7).

2.Операции со стеком должны осуществляться только в 16-битовых кодах. Если 8-разрядный регистр указывают для работы со стеком (push/pop), то выдается сообщение об ошибке.

3.Сегментные регистры не могут прямо использоваться в арифметических вычислениях, логических операциях или для непосредственной передачи данных. Кроме того, сегментные регистры могут быть использованы только либо как источники операндов, либо только как приемники операндов, но никак не одновременно.

Неверные записи

add ax,dl ; а) одновременное использование

;8-битового регистра (dl) и 16-битового регистра (ax) push bl ; б) 8-разрядный регистр указывают для работы со стеком

add ds,100 ; в) сегментный регистр ds используется при сложении

mov es,200 ; г) непосредственный операнд передается в регистр es

mov ss,cs

 

;д)оба операнда являются сегментными регистрами

Ниже приведена правильная запись предыдущих примеров

xor dh,dh

 

 

 

add ax,dx

; а)

 

 

 

 

 

 

 

xor bh,bh

 

 

 

push bx

; б)

 

 

 

 

 

 

addcx,100

; в)

 

 

 

 

 

 

movcx,200

; г)

moves,cx

 

 

 

 

 

; д)

movcx,cs

movss,cx

 

 

 

89

5.19. Выход из диапазона адресов [5, с.124-125]

Все команды условного перехода используют режим относительной адресации с 8-битовым смещением. Если относительный адрес символа, используемого в качестве операнда-адреса перехода, выходит за диапазон + 127 байтов -128 байтов от конца команды условного перехода, выдается сообщение об ошибке.

Неверные записи

and al,7fh

 

je next_proc

;между символическим адресом end_proc и next_proc

;

более 127 байтов (это важно, т .к. при выполнении

end_proc:

; команды je указатель команды (ip) показывает

;

на end_proc, а перейти надо на next_proc)

ret

data dw 300 dup(?) next_proc:

clc

Ниже приведена правильная запись предыдущего примера: and al,7fh

jne end_proc jmp next_proc

end_proc: ret

data dw 300 dup(?) next_proc:

clc

Библиографический список

1.Зубков С.В. Assembler для DOS, Windows и UNIX.–3-е изд., стер.–

М.: ДМК Пресс; СПб.: Питер, 2005.– 608 с.

2.Бурдаев О.В., Иванов М.А., Тетерин И.И. Ассемблер в задачах защиты информации/ Под ред. И.Ю.Жукова. – М.: КУДИЦ-ОБРАЗ, 2002. – 320 с.

3.Абель Питер. Ассемблер и программирование для IBM PC. Британская Колумбия. Технологический институт.

4.Олейник Л.Е. Язык ассемблера для микропроцессора 8086: Курс лекций. – Омск: Сибирская региональная школа бизнеса, 2000.

5.Дао. Л. Программирование микропроцессора 8088. – М.: Мир, 1988.

356 с.

6.Абель Питер. Язык и программирование для IBM PC: Пер. с англ.– Киев: Век; М.: Энтроп; Киев: НТИ, 2003. – 736 с.

90

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]