Материал: 1117

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

datasg ends

codesg segment para ‘Code’ ;сегмент кода

. . .

 

mov

ax,datasg

; первый способ

mov

es,ax

 

. . .

 

mov

ex,[DataSeg]

; второй способ

. . .

codesg ends

Чтобы скопировать содержимое одного сегментного регистра (например, cs) в другой сегментный регистр (например, ds), придется передать значение через регистр общего назначения или стек:

mov

ax,cs

; способ 1 (передача через регистр общего назначения)

mov

ds,ax

; ds := ax = cs

или

 

 

push

cs

; способ 2 (передача через стек)

pop

ds

; ds := cs

копируют содержимое регистра в ds. Первый метод работает быстрее, но при втором методе требуется меньший объем кода.

При работе с инструкцией mov имеются ограничения, когда дело касается сегментных регистров. В операциях сложения, вычитания, логических операциях или сравнениях использовать cегментные регистры нельзя. Но заносить содержимое сегментных регистров в стек и перемещать из стека в сегментные регистры – можно.

4.71. Перемещение данных в стек и из стека

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

mov ax,[bp+4]

загружает регистр ax содержимым слова в сегменте стека со смещением bp+4.

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

mov ax,1 push ax pop bx

76

заносят значение (равное 1) из регистра ax в вершину стека, затем извлекают 1 из вершины стека и сохраняют ее в bx.

5.ТИПИЧНЫЕ ОШИБКИ ПРИ ВЫПОЛНЕНИИ РАБОТЫ

5.1.Перечень часто встречающихся ошибок

Вперечень таких ошибок входят:

-открытая процедура или открытый сегмент;

-программист забывает о возврате в DOS;

-программист забывает о стеке или резервирует маленький стек;

-вызывает процедуру, которая портит содержимое нужных регистров;

-неопределенные символические имена;

-повторное определение символического имени;

-неправильный порядок операндов;

-неправильное использование операндов;

-потеря содержимого регистра при умножении;

-не подготовлены регистры при делении;

-неправильное использование регистра после деления;

-потеря содержимого регистра при делении;

-неправильное использование регистров при преобразовании байта в слово и слова в двойное слово в командах cbw и cwd;

-применение команд преобразования cbw и cwd к беззнаковым данным;

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

-программист долго не использует состояние флагов;

-ошибки при использовании регистров:

а) 8- и 16-разрядный регистры для операндов одной команды; б) 8-разрядный регистр указывается для работы со стеком;

в) сегментные регистры прямо используются в арифметических вычислениях, логических операциях или для непосредственной передачи данных;

г) сегментные регистры могут использоваться либо как источники операндов, либо как приемники операндов, но никак не одновременно;

- выход из диапазона адресов.

В каждом языке имеется свое множество ошибок, которые обычно очень легко сделать, но не всегда просто обнаружить. Не является исключением и язык ассемблера. Типичные ошибки, которые допускаются при программировании на ассемблере, и рекомендации, как можно их избежать, приведены ниже. Источники данной информации: [1], [4], [5], [6].

77

5.2. Открытая процедура или открытый сегмент [5, с.120-121]

Для каждой директивы proc должна быть записана соответствующая директива endp. Если директива endp пропущена или имя процедуры в записи endp записано неверно, ассемблер генерирует предупреждающее сообщение, что процедура не закрыта надлежащим образом. Точно так же директива ends должна быть задана для каждой директивы segment. Если утверждение ends пропущено или имя сегмента в нем указано неверно, генерируется предупреждающее сообщение, что сегмент открыт. Наиболее вероятной причиной ошибки является неверная запись имени сегмента или процедуры.

sample segment

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

proc1proc near

 

and al,00

; а) нет соответствующей директивы endp для

ret

;

процедуры proc1

;

(имя proc1 в записи endp указано неверно)

proc

endp

 

proc2

proc near

; б) нет соответствующей директивы ends

or

al,0ffh

 

ret

 

 

proc2 endp end

Данный вариант является правильной записью предыдущего примера: sample segment

proc1proc near and al,00 ret

proc1 endp proc2 proc near

or al,0ffh ret

proc2 endp sample ends

end

5.3.Программист забывает о возврате в DOS

ВПаскале, Си и других языках программа завершается и возвращается в операционную систему DOS автоматически, когда нет больше выполняемого кода, даже если в программе отсутствует явная команда ее завершения. В языке ассемблера это не так. Ассемблер выполняет только те действия, которые вы явно указываете. Когда вы

78

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

codesg segment para ‘Code’ ;сегмент кода

DoNothing proc near nop

DoNothing endp codesg ends

end DoNothing

Выполняемый код, сгенерированный при ассемблировании и компоновке данной программы, состоит только из отдельной инструкции nop. В ассемблере директива endp (как и все другие директивы) не генерирует кода, она просто уведомляет ассемблер, что код для процедуры DoNothing закончился, директива ends просто уведомляет ассемблер об окончании сегмента codesg, а директива end DoNothing – о том, что код данного модуля закончился и программа должна начать выполнение с метки DoNothing. Нигде в выполняемом коде не содержится инструкции для передачи управления обратно в операционную систему DOS, когда программа закончится. В результате, когда программа будет запущена, после инструкции nop будут выполняться инструкции, которые случайно окажутся в памяти непосредственно за nop. В этой точке управление будет потеряно, и для возврата в операционную систему DOS может потребоваться программная или аппаратная перезагрузка. Имеется несколько вариантов возврата в DOS. Правильно завершать работу будет, например, следующая версия предыдущей программы, использующая

функцию 4Ch:

 

 

codesg segment para

‘Code’

;сегмент кода

DoNothing proc near

 

 

nop

 

 

 

mov

ah,4Ch

; функция DOS завершения процесса

int

21h

; вызвать DOS для завершения программы

DoNothing endp

 

 

codesg ends

 

 

end

DoNothing

 

 

5.4.Программист забывает о стеке или резервирует маленький стек

Вбольшинстве программ для резервирования пространства для стека должна присутствовать директива определения сегмента стека (стандартная или упрощенная) и должно быть зарезервировано достаточное пространство, чтобы его хватило для максимальных потребностей в программе (например, директивой db).

79

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

Не требуется выделять стек в программах, которые предполагается преобразовать в файлы типа .com. Файлы .com выполняются со стеком, расположенным в самой вершине программного сегмента (который имеет размер 64 Кб или меньше, если доступно меньше 64 Кб), поэтому максимальный размер стека в этом случае просто равен объему памяти, оставшейся в программном сегменте. При написании программ в формате

.com следует иметь это в виду, так как при увеличении программы соответственно уменьшается стек. Нужно также учитывать, что при работе больших программ в формате .com, выполняемых на компьютерах с небольшой доступной памятью или запущенных из операционной среды DOS наряду с другими программами, могут возникнуть проблемы со стеком. Простейший способ избежать этих потенциальных проблем состоит в написании программ не в формате .com, а в формате .exe с резервированием стека большого объема.

5.5. Вызов подпрограммы, которая портит содержимое нужных регистров

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

1000 + w1/10.

 

 

datasg segment para

‘Data’

;сегмент данных

w1 dw 200

 

 

datasg ends

 

 

codesg segment para

‘Code’

;сегмент кода

. . .

 

 

mov

bx,1000

 

; bx :=1000

mov

ax,w1

 

; ax:=w1=200

call DivideBy10

 

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

add bx,ax

 

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

;(в данном случае получится bx:=bx+ax=10+w1/10), т.е. не правильно

;правильный результат должен быть1000 + w1/10.

80

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