Материал: 1117

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

. . .

;Подпрограмма для деления значения на 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

Источник: https://studfile.net/preview/16407174/