 |
CheckSum PROC NEAR
sub al,al ; очистить аккумулятор
; контрольной суммы
add al,[bx] ; прибавить текущее
; значение байта
inc bx ; ссылка на следующее
; значение
loop CheckSum
ret
CheckSum ENDP
Вы наверное согласитесь с выводом, что если подпрограмма на-
писана и отлажена (то есть правильно работает), то редко прихо-
дится снова просматривать ее текст. Все, что вам потребуется уз-
нать, - это какие функции выполняет подпрограмма, то есть, что
происходит, когда вы ее вызываете, и как подпрограмма взаимодейс-
твует с вызывающей ее программой. Этим целям довольно хорошо от-
вечает приведенный в примере описательный заголовок (который
иногда называют также "шапкой" программы).
Существует и много других методов комментирования, и вы, без
сомнения, придумаете такой метод, который больше подходит для ва-
шего стиля программирования. Важно только писать комментарии с
самого начала, благодаря чему комментирование станет со временем
неотъемлемой частью вашего стиля программирования.
Директивы определения сегментов
-----------------------------------------------------------------
И в данной главе, и в предыдущей, мы уже уделили много вре-
мени для пояснения того, что собой представляют директивы опреде-
ления сегментов и как они влияют на составляемую вами программу.
Однако, имеется еще один момент, которого мы пока не касались.
Суть его в следующем: откуда Турбо Ассемблер в точности знает, в
каком сегменте или сегментах находятся код и данные?
Управление сегментами - это один из наиболее сложных аспек-
тов программирования на языке Ассемблера для процессоров 8086. В
Турбо Ассемблере предусмотрен не один, а целых два набора дирек-
тив управления сегментами. Первый набор, состоящий из упрощенных
директив определения сегментов, делает управление сегментами от-
носительно легким и идеально подходит для компоновки модулей Ас-
семблера с языками высокого уровня, но поддерживает только неко-
торые из сегментных средств, имеющихся в Турбо Ассемблере.
Второй набор, состоящий из стандартных (полных) директив опреде-
ления сегментов, более сложно использовать, но он предусматрива-
ет полное управление сегментами, необходимое в некоторых прик-
ладных программах Ассемблера. Далее мы рассмотрим как
упрощенные, так и стандартные директивы определения сегментов.
Здесь же мы просто дадим обзор того, как можно использовать ди-
рективы определения сегментов для составления ваших программ (их
полное описание содержится в Главе 9 "Развитое программирование
на Турбо Ассемблере").
Упрощенные директивы определения сегментов
-----------------------------------------------------------------
Основными упрощенными директивами определения сегментов яв-
ляются директивы .STACK, .CODE, .DATA, .MODEL и DOSSEG. Рассмот-
рим эти директивы, разбив их на две группы. Первой будет группа
директив .STACK, .CODE и .DATA.
Директивы .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 ука-
зывает на данные. Программы не работают непосредственно с инс-
трукциями или стеком, однако с данными они работают постоянно.
Кроме того, программы могут в любой момент пожелать получить дан-
ные в нескольких разных сегментах. Нужно помнить о том, что про-
|