2) из стандартных (полных) директив определения сегментов (более сложно использовать, но он предусматривает полное управление сегментами, необходимое в некоторых прикладных программах ассемблера; см. уч. пос., ч.2, 10 и 11).
Взависимости от того, сколько и каких сегментов предусматривается
впрограмме, различают следующие модели памяти.
Сверхмалая (tiny) – код программы и ее данные должны размещаться внутри одного и того же сегмента размером 64 Кб. Код и данные имеют ближний тип (см. уч. пос., ч.3 и 7).
Малая (small) – код программы должен размещаться внутри одного сегмента данных размером 64 Кб, а данные – в отдельном сегменте данных (размером 64 Кб). И код, и данные должны быть ближнего типа (см. уч.
пос., ч.4 и 5).
Средняя (medium) – код программы может превышать 64 Кб, но данные должны помещаться в один сегмент размером 64 Кб. Код имеет дальний тип, а данные – ближний (см. уч. пос., ч.2 и 6).
Компактная (compact) – код программы должен помещаться в один сегмент размером 64 Кб, а данные могут превышать по размеру 64 Кб. Код имеет ближний тип, а данные должны быть дальнего типа (см. уч.пос.,
ч.14).
Большая (large) – код и данные программы могут превышать 64 Кб, но один массив данных не может превышать 64 Кб. И код и данные имеют дальний тип (см. уч. пос., ч.14).
Сверхбольшая (huge) – код и данные могут превышать по размеру 64 Кб. Массивы данных также могут превышать 64 Кб. Код и данные имеют дальний тип. Указатели на элементы массива также дальнего типа
(см.п.4.58; уч. пос., ч.14; 1, с. 112).
4.24. Стандартные директивы определения сегментов
Приведем пример программы, использующей стандартные директивы определения сегментов segment, ends и assume.
stacksg |
segment para stack 'stack' |
db 200h dup (?) |
|
stacksg |
ends |
datasg |
segment para 'data' |
HelloMessage db 'Привет!',13,10,'$'
datasg |
ends |
codesg |
segment para 'code' |
assume cs:codesg, ds:datasg, ss:stacksg ProgramStart:
mov ax,datasg
31
mov ds,ax |
; установить ds в значение сегмента данных |
mov dx,offset HelloMessage;ds:dx указывает на сообщение 'Привет!' |
|
mov ah,9 |
; функция DOS вывода строки |
int 21h |
; вывести строку на экран |
mov ah,4ch |
; функция DOS завершения программы |
int 21h |
; завершить программу |
codesg ends
end ProgramStart
4.25. Директива segment
Директива segment определяет начало сегмента. Метка, которая указывается в данной директиве, определяет начало сегмента. Например, директива:
codesg segment
определяет начало сегмента с именем codesg. Директива segment может также (необязательно) определять атрибуты сегмента, включая выравнивание в памяти на границу байта, слова, двойного слова, параграфа (16 байтов) или страницы (256 байтов). Другие атрибуты включают в себя способ, с помощью которого сегмент будет комбинироваться с другими сегментами с тем же именем и классом сегмента.
4.26. Директива ends
Директива ends определяет конец сегмента. Например: codesg ends
завершает сегмент с именем codesg, который начинался по директиве segment. При использовании стандартных директив определения сегментов необходимо явным образом завершать каждый сегмент.
4.27. Директива assume
Директива assume указывает, в значение какого сегмента установлен данный сегментный регистр. Директиву assume cs: требуется указывать в каждой программе, в которой используются стандартные сегментные директивы, так как ассемблеру необходимо знать о сегменте кода для того, чтобы установить выполняемую программу. Кроме того, обычно используются директивы assume ds: и assume es:, благодаря которым ассемблер знает, к каким ячейкам памяти можно адресоваться в данный момент.
Директива assume позволяет ассемблеру проверить допустимость каждого обращения к именованной ячейке памяти с учетом значения текущего сегментного регистра. Рассмотрим следующий пример:
Data1 segment para 'Data' Var1 dw 0
32
Data1 ends
Data2 segment para 'Data' Var2 dw 0
Data2 ends
Codesg segment para 'Code' assume cs:Codesg
ProgramStart: mov ax,Data1
mov ds,ax ; установить ds в Data1 assume ds:Data1
mov ax,[Var2] ; попытаться загрузить Var2 в ax (это приведет к
; |
ошибке, так как Var2 недоступна в сегменте Data1) |
mov ah,4ch |
; номер функции dos для завершения программы |
int 21h |
; завершить программу |
Codesg ends
end ProgramStart
Ассемблер отмечает в данной программе ошибку, так как в ней делается попытка получить доступ к переменной памяти Var2, когда регистр ds установлен в значение сегмента Data1 (к Var2 нельзя адресоваться, пока ds не будет установлен в значение сегмента Data2).
Чтобы сообщить ассемблеру, что в данный момент сегментный регистр не указывает ни на какой именованный сегмент, в директиве assume используется ключевое слово nothing.
Например: mov ax,0b800h mov ds,ax
assume ds:nothing
Здесь регистр ds устанавливается таким образом, чтобы указывать на цветной графический экран, а затем ассемблеру сообщается, что регистр ds не указывает ни на какой именованный сегмент.
В некоторых случаях директива assume может привести к тому, что ассемблер будет использовать для доступа к памяти не тот сегментный регистр, который вы ожидаете, а другой. Рассмотрим, например, следующий фрагмент программы:
Data1 segment para 'Data' Var1 dw 0
Data1 ends
Data2 segment para 'Data' Var2 dw 0
Data2 ends
Codesg segment para 'Code'
33
assume cs:Codesg ProgramStart:
mov ax,Data1
mov ds,ax ; установить ds в Data1 assume ds:Data1
mov ax,Data2
mov es,ax ; установить es в Data2 assume es:Data2
mov ax,[Var2] ; загрузить Var2 в ax
;ассемблер укажет процессору 8086, что загрузку нужно выполнять
;относительно es, т.к. к Var2 нельзя получить доступ относительно ds mov ah,4ch ; функция dos завершения работы программы
int |
21h |
; завершить программу |
Codesg |
ends |
|
end ProgramStart
В данном примере сообщение об ошибке не выводится. Но это не означает, что ассемблер позволяет вам сделать ошибку. Он модифицирует инструкцию
mov ax,[Var2]
для доступа к Var2 относительно сегментного регистра es, а не ds.
Это происходит потому что регистр ds установлен в значение сегмента Data1, а es установлен в значение сегмента Data2. Ассемблер совершенно правильно заключает, что к Var2 нельзя получить доступ относительно регистра ds, однако Var2 доступно относительно сегментного регистра es. В итоге ассемблер включает перед инструкцией mov специальный код (префикс переопределения сегмента), чтобы указать процессору 8086, что вместо сегментного регистра ds нужно использовать регистр es.
Таким образом, если вы корректно используете директивы assume, позволяя ассемблеру узнать о текущих установленных для регистров ds и es значениях, то он в некоторых случаях даже может выполнить автоматическую корректировку сегмента.
4.28. Директива end
Директива end отмечает конец исходного кода программы. Все строки, которые следуют за нею, ассемблером игнорируются. Например: stacksg segment para ‘Stack’
db 200h ; стек объемом 512 байтов stacksg ends
datasg segment para ‘Data’ ; начало сегмента данных datasg ends
codesg segment para ‘Code’ ; начало сегмента кода
34
main proc far
assume cs:codesg, ds:datasg, ss:stacksg
mov ax,datasg |
|
|
mov ds,ax |
; ds указывает на сегмент данных datasg |
|
mov |
ah,4ch |
|
int |
21h |
|
main endp |
|
|
codesg ends |
|
|
end |
main |
|
Это простейшая программа на ассемблере. Она ничего не делает, просто немедленно возвращает управление DOS.
Кроме завершения программы, директива end может указывать, где должно начинаться выполнение при запуске программы (с метки main). Иногда требуется начать выполнение программы в файле .exe не с первой инструкции. Директива end предусматривает такие случаи. Например:
stacksg segment para ‘Stack’ |
|
|
|||
db |
200h |
|
; стек объемом 512 байтов |
||
stacksg ends |
|
|
|
||
datasg segment para ‘Data’ |
; начало сегмента данных |
||||
datasg ends |
|
|
|
||
codesg segment para ‘Code’ |
; начало сегмента кода |
||||
Delay proc |
|
|
|
||
mov |
cx,60000 |
|
|
||
DelayLoop: |
|
|
|
||
loop DelayLoop |
|
|
|||
ret |
|
|
|
|
|
Delay endp |
|
|
|
||
mainproc far |
|
|
|
|
|
assume cs:codesg, ds:datasg, ss:stacksg |
|||||
ProgramStart: |
|
|
|
||
mov ax,datasg |
|
|
|||
mov ds,ax |
; ds указывает на сегмент данных datasg |
||||
call Delay |
; пауза на время, необходимое для выполнения 64 |
||||
циклов |
|
|
|
|
|
mov |
ah,4ch |
|
|
|
|
int |
21h |
|
|
|
|
main endp |
|
|
|
||
codesg ends |
|
|
|
||
end |
ProgramStart |
|
|
||
Выполнение здесь начинается не на первой инструкции исходного кода (mov cx,60000) по метке Delay. Вместо этого выполнение начинается
35