CodeNet / Языки программирования / Ассемблер / СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0
СПРАВОЧНИК по системе программирования ТУРБО АССЕМБЛЕР 2.0
Директивы .STACK, .CODE и .DATA ----------------------------------------------------------------- Директивы определения сегментов .STACK, .CODE и .DATA определяют, соответственно, сегмент стека, сегмент кода и сегмент данных. Например, директива: .STACK 200h определяет стек размером в 200h (512) байт. Что касается стека, то это все, что вы сможете сделать. Необходимо просто убедиться, что в вашей программе имеется директива .STACK, и Турбо Ассемблер выделит для вас стек. Для обычных программ вполне подходит стек размером 200h, хотя в программах, интенсивно использующих стек (например, в программах, содержащих рекурсивные вызовы) может потребоваться стек большего размера. (Информация об исключениях при использовании директивы .STACK приведена в разделе "Невыделе- ние стека или выделение слишком маленького стека" в Главе 6.) Директива .CODE отмечает начало сегмента кода. Вы можете посчитать, что для Турбо Ассемблера достаточно очевидно, что все ваши инструкции относятся к сегменту кода. На самом деле Турбо Ассемблер позволяет вам (с помощью стандартных директив определе- ния сегментов) использовать несколько сегментов кода, а директива .CODE указывает Турбо Ассемблеру, в какой именно сегмент надо по- местить ваши инструкции. Определение сегмента кода еще проще, чем определение сегмента стека, так как аргументы для директивы .CODE указывать не требуется. Например: . . . .CODE sub ax,ax ; установить аккумулятор в значение 0 mov cx,100 ; число выполняемых циклов . . . Директива .DATA несколько более сложна. Как можно понять, директива .DATA отмечает начало сегмента данных. В этом сегменте следует размещать ваши переменные памяти. Например: . . . .DATA TopBoundary DW 100 Counter DW ? ErrorMessage BD 0dh,0dh,'***Ошибка***',0dh,0ah,'$' . . . Это довольно просто. Вся "сложность" директивы .DATA заклю- чается в том, что до того, как вы будете обращаться к ячейкам па- мяти в сегменте, определенном с помощью директивы .DATA, нужно явно загружать сегментный регистр DS идентификатором @data. Так как сегментный регистр можно загрузить из регистра общего назна- чения или ячейки памяти, но в него нельзя загрузить константу, регистр DS обычно загружается с помощью последовательности из двух инструкций: . . . mov ax,@data mov ds,ax . . . (Вместо регистра AX можно использовать любой общий регистр.) Дан- ная последовательность инструкций устанавливает DS таким образом, чтобы он указывал на сегмент данных, который начинается по дирек- тиве .DATA. Следующая программа выводит на экран текст, хранящийся в строке DataString: DOSSEG .MODEL .STACK small .DATA DataString DB 'Этот текст находится в сегменте данных' .CODE ProgramStart: mov bx,@data mov ds,bx ; устанавливает регистр DS на сегмент ; данных mov dx,OFFSET DataString ; DX указывает на смещение ; DataString в сегменте .DATA mov ah,9 ; номер функции DOS печати строки int 21h ; вызвать DOS для печати строки mov ah,4ch ; вызвать DOS для завершения программы END ProgramStart Без двух инструкций, которые устанавливают регистр DS в зна- чение сегмента, определенного с помощью директивы .DATA, функция печати строки не будет правильно работать. Строка DataString на- ходится в сегменте данных и недоступна, пока регистр DS не будет установлен в значение этого сегмента. Это можно рассматривать следующим образом: когда вы вызываете операционную систему DOS для печати строки, в паре регистров DS:DX вы передаете полный ад- рес в формате "сегмент:смещение". Полный указатель вида "сегмент: смещение" вы получите только после того, как в регистр DS будет загружен сегмент данных, а в DX - смещение DataString. У вас может возникнуть вопрос, почему нужно загружать ре- гистр DS, а не CS или SS (или ES)? Во первых, регистр CS никогда не загружается явно, так как при запуске программы за вас это делает операционная система DOS. Кроме того, если бы регистр CS уже не был установлен, когда приш- ло время выполнить первую инструкцию программы, процессор 8086 не знал бы, где найти эту инструкцию, и программа не стала бы рабо- тать. Пока для вас это может быть недостаточно очевидно, но по- верьте нам на слово: регистр CS загружается при запуске программы автоматически, и у вас нет необходимости загружать его явно. Аналогично, при запуске программы DOS загружает регистр SS, значение которого при выполнении программы обычно остается неиз- менным. Хотя изменять содержимое регистра SS можно, это редко оказывается желательным, и определенно это не стоит делать, если вы не знаете точно, что вы делаете. Таким образом, регистр SS аналогично регистру CS устанавливается при начале выполнения программы автоматически, и далее его трогать не нужно. Регистр DS существенно от них отличается. В то время как ре- гистр CS указывает на инструкции, а SS - на стек, регистр DS ука- зывает на данные. Программы не работают непосредственно с инс- трукциями или стеком, однако с данными они работают постоянно. Кроме того, программы могут в любой момент пожелать получить дан- ные в нескольких разных сегментах. Нужно помнить о том, что про- цессор 8086 в любое время позволяет вам получить доступ к любой ячейке памяти в пределах 1 Мбайта, но только в блоках по 64К (от- носительно сегментного регистра). Вы можете захотеть загрузить регистр DS одним сегментом, по- лучить доступ к данным в этом сегменте, а затем загрузить DS дру- гим сегментом, чтобы обратиться к другому блоку данных. В малень- ких и средних программах (таких, как в приведенных нами примерах) вам не потребуется использовать более одного сегмента данных, но в больших программах несколько сегментов данных используется час- то. Кроме того, вам потребуется загружать регистр DS различным значениями, если вы хотите получить доступ к системным областям памяти, например, к ячейкам памяти, используемым базовой системой ввода-вывода (BIOS). Из всего этого можно сделать следующий краткий вывод: Турбо Ассемблер позволяет вам в любой момент установить регистр DS в значение любого сегмента. За эту гибкость приходится расплачи- ваться тем, что вы должны явно устанавливать регистр DS в значе- ние нужного сегмента (обычно @data), что эквивалентно сегменту, который начинается с директивы .DATA. После этого вы сможете по- лучить доступ к ячейкам памяти этого сегмента. Сегментный регистр ES загружается аналогично регистру DS. Чаще всего вам не потребуется использовать регистр ES, но когда появится необходимость получить доступ к ячейке памяти в сегмен- те, на который указывает регистр ES, вы должны сначала загрузить регистр ES значением этого сегмента. Например, следующая прог- рамма загружает регистр ES значением сегмента .DATA, а затем загружает через ES символ, который нужно напечатать из этого сегмента: DOSSEG .MODEL small .STACK 200h .DATA OutputChar DB 'B' .CODE ProgramStart: mov dx,@data mov es,dx ; установить ES в значение ; сегмента .DATA mov bx,OFFSET OutputChar ; BX указывает на ; смещение OutputChar mov al,es:[bx] ; получить выводимый символ ; из сегмента, на который ; указывает регистр ES mov ah,2 ; функция DOS вывода символа int 21h ; вызвать DOS для вывода ; символа на экран END ProgramStart Обратите внимание, что регистр ES (как и регистр DS ранее) загружается последовательностью из двух инструкций: . . . mov dx,@Data mov es,dx . . . Положим, в данном примере нет конкретной причины использо- вать вместо DS регистр ES. Фактически, использование регистра ES означает, что мы применили префикс переопределения сегмента ES: (как это описывается в Главе 9). Однако во многих случаях чрезвы- чайно удобно, когда регистр DS указывает на один сегмент, а ре- гистр ES - на другой (особенно это касается использования строко- вых инструкций).
Оставить комментарий
Комментарии
1.
+3 / -0
21 сентября 2011, 05:53:24
Опечатка: в программе должно быть написано .model small и .stack без слова small, так же в конце программы после mov ah,4ch пропущено int 21h