Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Финогенов-основы_языка_ассемблера.doc
Скачиваний:
44
Добавлен:
17.09.2019
Размер:
3.35 Mб
Скачать

Глава 3

Команды и алгоритмы

105

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

Структура и образ памяти программы .СОМ

Как уже отмечалось, программа типа .СОМ отличается от программы типа .ЕХЕ тем, что содержит лишь один сегмент, включающий все ком­поненты программы: PSP, программный код (т.е. оттранслированные в машинные коды программные строки), данные и стек. Структура типич­ной программы типа .СОМ на языке ассемблера выглядит следующим образом:

code segment ;

assume CS:text,DS:text

org lOOh ;Мссто для PSP main proc

;Текст программы main endp

;Определения данных code ends

end main .

Программа содержит единственный сегмент code. В операторе ASSUME указано, что сегментные регистры CS и DS будут указывать на этот един­ственный сегмент. Оператор ORG lOOli резервирует 256 байт для PSP. За­полнять PSP будет по-прежнему система, но место под него в начале сегмента должен отвести программист. В программе нет необходимости инициализировать сегментный регистр DS, поскольку его, как и осталь­ные сегментные регистры, инициализирует система. Данные можно раз­местить после программной процедуры (как это показано в приведенном примере), или внутри нее, или даже перед ней. Следует только иметь в виду, что при загрузке программы типа .СОМ регистр IP всегда инициа­лизируется числом lOOh, поэтому сразу вслед за оператором ORG lOOli должна стоять первая выполнимая команда программы. Если данные же­лательно расположить в начале программы, перед ними следует помес­тить оператор перехода на фактическую точку входа, например jmp entry.

Образ памяти программы типа .СОМ показан на рис. 3.2. После загруз­ки программы все сегментные регистры указывают на начало единствен­ного сегмента, т.е. фактически на начало PSP. Указатель стека автомати­чески инициализируется числом FFFEh. Таким образом, независимо от фактического размера программы, ей выделяется 64 Кбайт адресного про­странства, всю нижнюю часть которого занимает стек. Поскольку1 верхняя граница стека не определена и зависит от интенсивности и способа ис­пользования стека программой, следует опасаться затирания стеком ниж-

ней части программы. Впрочем, такая опасность существует и в програм­мах типа .ЕХЕ, так как в реальном режиме нет никаких механизмов защи­ты, и при сохранении в стеке большего объема данных, чем может так поместиться, данные начнут затирать поля того сегмента, который рас­положен над стеком, в наших примерах — сегмента данных.

CS.DS, ES, SS —>

Префикс программы (PSP) 256 байт

• = 010011

Программа и данные

Стек

<— SP = FFFEh

Рис. 3.2. Образ памяти программы .СОМ

Программы типа .СОМ отличаются от .ЕХЕ-программ не только от­сутствием сегментов данных и стека. В гл. 2 было показано, что при вырав­нивании сегментов на байт, что делается с помощью описателя byte

data segment byte

системные программы располагают сегменты загружаемой программы с некоторым перекрытием, что позволяет избежать пустых промежутков между сегментами в памяти, возникающих из-за того, что размеры сег­ментов могут быть не кратны величине параграфа — 16 байт. Такое распо­ложение сегментов требует изменения значений ссылок на адреса ячеек памяти. В состав программного файла с расширением .ЕХЕ входит табли­ца с перечнем байтов программы, содержимое которых может подверг­нуться изменению в процессе загрузки программы в память. Поэтому, кстати, размер файла с расширением .ЕХЕ может превышать истинный размер программы в памяти.

Программа типа .СОМ состоит из единственного сегмента, и пробле­ма настройки ссылок не возникает. Файл с расширением .СОМ почти в точности отражает содержимое памяти после загрузки программы. Отли­чие заключается только в том, что в программном файле отсутствует пре­фикс программы PSP, который система вставляет в программу в процес­се ее загрузки. Таким образом, файл с расширением .СОМ обычно оказы­вается на 256 байт короче своего образа в памяти.

Если оттранслировать и скомпоновать программ}', написанную в фор­мате .СОМ, обычным образом, образуется программный файл с расши­рением .ЕХЕ. Этот файл можно запустить на выполнение, однако.рабо-тать он будет неверно. Дело в том, что система, загружая файл типа .ЕХЕ в память, пристраивает перед загруженной программой префикс и на­страивает на него регистры DS и ES. В результате значение DS окажется на 10h меньше, чем сегментный адрес сегмента с командами и данными,

. -Т"

_Liaaa_j Команды и алгоритмы

106

что приведет к неправильной адресации при обращении к полям данных. Программ)1, написанную в формате .СОМ, можно запускать только в виде файла с расширением .СОМ, для которого в DOS предусмотрен свой алгоритм загрузки и запуска. Для того, чтобы компоновщик создал файл с расширением .СОМ, в строке запуска компоновщика необходимо пре­дусмотреть ключ /t (при использовании компоновщика TLINK.EXE):

tlink /х /v /3 /t p,p

Для того, чтобы избежать ошибок при подготовке программ, целесо­образно подготовить два командных файла для трансляции и компоновки программных примеров — один для программ типа .ЕХЕ, и другой для программ типа .СОМ. Разумеется, файлам надо назначить различающиеся имена.

Рассмотрим пример законченной программы типа .СОМ, которая выводит на экран строку текста.

; Место под PSP ;Функция вывода на экран

;Пример 3-2. Простая .СОМ-программа assume CS:code,DS:code code segment

org 256 main proc

mov

mov

lilt

mov

int

main endp , I

msg db 16,16,16,'Программа типа .COM', 17,17,17,'$' code ends end main

В начале программы отведено 256 байт под PSP; в программе отсут­ствует инициализация регистра DS; поле данных размещено в программ­ном сегменте непосредственно после последней команды. Для разнообра­зия в строку, выводимую на экран, включены коды 16 и 17, которые отображаются на экране в виде залитых треугольников (рис. 3.3). Как вид­но из этого рисунка, программа имела имя Р. СОМ и запускалась из ката­лога F:\CURRENT.

Рассмотрим важный в принципиальном плане вопрос о месте разме­щения данных в .СОМ-программе. В нашем примере данные описаны в конце программного сегмента вслед за процедурой main, которая, как и в

AH,09h

DX,offsct msg

21h

;Функция завершения ; про граммы

AX,4COOh

Рис. 3.3. Вывод программы 3-2.


F:\CURREHT>p.com ^»Программа типа .СОИ 444


21h

107

предыдущих примерах, введена скорее для порядка, чем по необходимо­сти. С таким же успехом можно было предложение с именем msg помес­тить после вызова int21h, внутри процедуры main. Третий возможный ва­риант, с которым мы еще столкнемся в примерах резидентных программ, приведен ниже.

assume CS:code,DS:code code segment

256

; Место под PSP

org main proc

jmp start ;Первая выполнимая команда

msg db 16,16,16,"Программа типа .COM', 17,17,17,'$' start: mov AH,09h ;Функция вывода на экран

mov DX.offset msg

int 21h

;Продолжение программы

Таким образом, данные могут быть размещены как после программы, так И среди выполнимых предложений программы. Важно только соблюс­ти обязательное условие: ни при каких обстоятельствах на данные не дол­жно быть передано управление. В первом случае (пример 3-2) данные по­мещены за вызовом функции DOS, завершающей программу. Ясно, что после выполнения этой функции упраштение уже не вернется в нашу про­грамму, а будет передано командному процессору, поэтому размещение здесь данных вполне возможно. В последнем фрагменте данные описаны, можно сказать, в середине программы. Однако перед ними стоит команда безусловного перехода jmp, которая приводит при выполнении програм­мы к обходу данных.

А вот чего нельзя было сделать, так это разместить данные после зак­рытия сегмента, как это сделано в приведенном ниже (неправильном!) фрагменте:

main cndp ;Конец процедуры

code ends ;Конец сегмента

msg db 16,16,16,'Программа типа .СОМ', 17,17,17,'$' end main

Это второе обязательное условие: из чего бы ни состояла программа, все ее компоненты должны входить в те или иные сегменты. Вне сегментов допус­каются только нетранслируемые директивы ассемблера типа .586 или assume.

Наконец, третье условие, о котором уже говорилось, относится толь­ко к программам типа .COM. DOS, загрузив программу в память, иници­ализирует указатель команд числом lOOh, т.е. адресом первой команды вслед за оператором org lOOh. Поэтому главная процедура .СОМ-програм-мы (если в ней имеется несколько процедур) обязательно должна быть первой, причем первое предложение этой • процедуры должно быть вы­полнимой командой (например, командой jmp, как это показано выше).

108