Далее мы приведем такой же пример программы, как и в преды-дущем разделе, но на этот раз используем стандартные директивы определения сегментов SEGMENT, ENDS и ASSUME.
DGROUP GROUP _DATA, STACK
ASSUME CS:_TEXT, DS:_DATA, SS:STACK
STACK SEGMENT PARA STACK ‘STACK’
DB 200h DUP (?)
STACK ENDS
_DATA SEGMENT WORD PUBLIC ‘DATA’
MemVar DW 0
_DATA ENDS
_TEXT SEGMENT WORD PUBLIC ‘CODE’
ProgramStart:
mov ax,_DATA
mov ds,ax
mov ax,[MemVar]
mov ah,4ch
int 21h
_TEXT ENDS
END ProgramStart
Теперь вы видите, почему упрощенные директивы определения сегментов называются упрощенными. Однако, многое из того, что де-лают упрощенные директивы определения сегментов предназначено для того, чтобы облегчить компоновку модулей Ассемблера с языками вы-сокого уровня, что является излишним в автономных программах на Ассемблере. Приведем пример программы HELLO с использованием стандартных директив определения сегментов:
Stack Segment PARA STACK ‘STACK’
DB 200h DUP (?)
Stack ENDS
Data SEGMENT WORD ‘DATA’
HelloMessage DB ‘Привет!’,13,10,’$’
Data ENDS
Code Segment WORD ‘CODE’
ASSUME CS:Code, DS:Data
ProgramStart:
mov ax,Data
mov ds,ax ; установить DS в значение
; сегмента данных
mov dx,OFFSET HelloMessage ; DS:DX указывает
; на сообщение ‘Привет!’
mov ah,9 ; функция DOS вывода строки
int 21h ; вывести строку на экран
mov ah,4ch ; функция DOS завершения
; программы
int 21h ; завершить программу
Code ENDS
END ProgramStart
Последний пример не слишком усложнился, но тем не менее яс-но, что стандартные директивы определения сегментов более сложны, чем упрощенные директивы.
В Главе 9 стандартные (полные) директивы определения сегмен-тов описываются более подробно. В данном разделе разделе мы пы-таемся только дать вам представление о том, что делают стандарт-ные директивы определения сегментов.
Директива SEGMENT
Директива SEGMENT определяет начало сегмента. Метка, которая указывается в данной директиве, определяет начало сегмента. Нап-ример, директива:
Cseg SEGMENT
определяет начало сегмента с именем Cseg. Директива SEGMENT может также (необязательно) определять атрибуты сегмента, включая вы-равнивание в памяти на границу байта, слова, двойного слова, па-раграфа (16 байт) или страницы (256 байт). Другие атрибуты вклю-чают в себя способ, с помощью которого сегмент будет комбиниро-ваться с другими сегментами с тем же именем и классом сегмента.
Директива ENDS
Директива ENDS определяет конец сегмента. Например:
Cseg ENDS
завершает сегмент с именем Cseg, который начинался по директиве SEGMENT. При использовании стандартных директив определения сег-ментов вы должны явным образом завершать каждый сегмент.
Директива ASSUME
Директива ASSUME указывает Турбо Ассемблеру, что в значение какого сегмента установлен данный сегментный регистр. Директиву ASSUME CS: требуется указывать в каждой программе, в которой ис-пользуются стандартные сегментные директивы, так как Турбо Ас-семблеру необходимо знать о сегменте кода для того, чтобы устано-вить выполняемую программу. Кроме того, обычно используются директивы ASSUME DS: и ASSUME ES:, благодаря которым Турбо Ас-семблер знает, к каким ячейкам памяти вы можете адресоваться в данный момент.
Директива ASSUME позволяет Турбо Ассемблеру проверить допус-тимость каждого обращения к именованной ячейке памяти с учетом значения текущего сегментного регистра. Рассмотрим следующий при-мер:
Data1 SEGMENT WORD ‘DATA’
Var1 DW 0
Data1 ENDS
Data2 SEGMENT WORD ‘DATA’
Var2 DW 0
Data2 ENDS
Code SEGMENT WORD ‘CODE’
ASSUME CS:Code
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 ; завершить программу
Code ENDS
END ProgramStart
Турбо Ассемблер отмечает в данной программе ошибку, так как в ней делается попытка получить доступ к переменной памяти Var2, когда регистр DS установлен в значение сегмента Data1 (к Var2 нельзя адресоваться, пока DS не будет установлен в значение сег-мента Data2).
Важно понимать, что Ассемблер на самом деле не знает, что регистр DS установлен в значение Data1. С помощью директивы ASSUME вы указали Турбо Ассемблеру, что нужно сделать такое допу-щение. Директива ASSUME дает вам способ в любой момент сообщить Ассемблеру о значении сегментного регистра, после чего Турбо Ас-семблер будет сообщать вам, если вы пытаетесь сделать невозмож-ное.
Однако Турбо Ассемблер не может перехватывать все подобные ошибки. Когда в ссылке на память используется именованная пере-менная памяти (такая, как Var1 и Var2 в предыдущем примере), Тур-бо Ассемблер может проверить допустимость этой ссылки, так как каждая именованная переменная памяти явным образом связана с сег-ментом. Невозможно сообщить Турбо Ассемблеру, к какому сегменту пытается обратиться инструкция:
mov al,[bx]
В этом случае Турбо Ассемблер должен предположить, что зна-чение сегментного регистра DS соответствует тому сегменту, к ко-торому вы хотите обратиться.
Если в данный момент сегментный регистр не указывает ни на какой именованный сегмент, то чтобы сообщить об этом Ассемблеру, можно использовать в директиве ASSUME ключевое слово NOTHING.
Например:
mov ax,0b800h
mov ds,ax
ASSUME ds:NOTHING
Здесь регистр DS устанавливается таким образом, чтобы указы-вать на цветной графический экран, а затем Турбо Ассемблеру сооб-щается, что регистр DS не указывает ни на какой именованный сег-мент. Вот еще один способ ссылки на цветной графический экран:
ColorTextSeg SEGMENT AT 0B8000h
ColorTextMemory LABEL BYTE
ColorTextSeg ENDS
mov ax,ColorTextSeg
mov ds,ax
ASSUME ds:ColorTextSeg
Обратите внимание, что в директиве AT, которая следует за директивой SEGMENT, задается явный начальный адрес сегмента.
Сделаем последнее замечание по директиве ASSUME: в некоторых случаях она может привести к тому, что Турбо Ассемблер будет ис-пользовать для доступа к памяти не тот сегментный регистр, кото-рый вы ожидаете, а другой. Рассмотрим, например, следующий фраг-мент программы:
Data1 SEGMENT WORD ‘DATA’
Var1 DW 0
Data1 ENDS
Data2 SEGMENT WORD ‘DATA’
Var2 DW 0
Data2 ENDS
Code SEGMENT WORD ‘CODE’
ASSUME CS:Code
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 ; завершить программу
Code ENDS
END ProgramStart
Данный пример должен быть вам знаком: это модифицированная версия фрагмента программы, использованного нами ранее для того, чтобы показать, как директива ASSUME позволяет Турбо Ассемблеру указать вам, когда вы пытаетесь использовать недопустимую ссылку на память. Однако в данном примере сообщение об ошибке не выво-дится. Но это не означает, что Турбо Ассемблер позволяет вам сде-лать ошибку. Он модифицирует инструкцию:
mov ax,[Var2]
для доступа к Var2 относительно сегментного регистра ES, а не сегментного регистра DS.
Это происходит по следующим причинам. Две директивы ASSUME информируют Турбо Ассемблер о том, что регистр DS установлен в значение сегмента Data1, а ES установлен в значение сегмента Data2. Турбо Ассемблер совершенно правильно заключает, что к Var2 нельзя получить доступ относительно регистра DS, однако Var2 дос-тупно относительно сегментного регистра ES. В итоге Турбо Ассем-блер включает перед инструкцией MOV специальный код (префикс пе-реопределения сегмента), чтобы указать процессору 8086, что вмес-то сегментного регистра DS нужно использовать сегментный регистр ES.
Какое все это имеет для вас значение? Это значит, что если вы корректно используете директивы ASSUME, позволяя Турбо Ассем-блеру узнать о текущих установленных для регистров DS и ES значе-ниях, то он может автоматически вам помочь, проверяя возможность доступа к именованным переменным в памяти и в некоторых случаях даже может выполнить автоматическую корректировку сегмента.
Общее обсуждение префиксов переопределения сегментов и стан-дартные директивы определения сегментов обсуждаются в Главе 10.
