
- •140010, Г. Люберцы, Московской обл., Октябрьский пр-т, 403.
- •Глава 1. Архитектура реального режима
- •1.1. Память и процессор
- •Глава 1
- •Глава 1
- •1.2. Распределение адресного пространства
- •Глава 1
- •1.3. Регистры процессора
- •Глава 1
- •Глава 1
- •9 7H Шестнадцатернчное обозначение числа
- •Глава 1
- •1.4. Сегментная структура программ
- •Глава 1
- •Глава 1
- •Глава 1
- •1.5. Стек
- •Глава 1
- •1.6. Система прерываний
- •Глава 1
- •Глава I
- •1.7. Система ввода-вывода
- •Глава I
- •Глава 1
- •Глава 2. Основы программирования
- •2.1. Подготовка и отладка программы
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •2.2. Представление данных
- •Глава 2
- •Глава 2
- •2.3. Описание данных
- •Глава 2
- •Глава 2
- •2.4. Структуры и записи
- •Глава 2
- •Глава 2
- •2.5. Способы адресации
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 2
- •2.7. Вызовы подпрограмм
- •Глава 2
- •2.8. Макросредства ассемблера
- •Глава 2
- •Глава 2
- •Глава 2
- •Глава 3. Команды и алгоритмы
- •Глава 3
- •Глава 3
- •Глава 3
- •Глава 3
- •Глава 3
- •Глава 3
- •Глава 3
- •Глава 3
- •3.2. Циклы и условные переходы
- •Глава 3
- •Глава 3
- •3.3. Обработка строк
- •Глава 3
- •3.4. Использование подпрограмм
- •Глава 3
- •Глава 3
- •3.5. Двоично-десятичные числа
- •Глава 3
- •Глава 3
- •3.6. Программирование аппаратных средств
- •Глава 3
- •37Ah Порт управлсш!я
- •Глава 3
- •Глава 3
- •Глава 4. Расширенные возможности
- •4.1. Архитектурные особенности
- •Глава 4
- •4.2. Дополнительные режимы адресации
- •Глава 4
- •4.3. Использование средств 32-разрядных процессоров в программировании
- •Глава 4
- •Глава 4
- •Глава 4
- •4.4. Основы защищенного режима
- •Глава 4
- •Глава 4
- •Глава 4
- •Глава 4
- •Idiv Деление целых чисел со знаком
- •Imul Умножение целых чисел со знаком
- •In Ввод из порта
- •Inc Инкремент (увеличение на 1)
- •Int Программное прерывание
- •Into Прерывание по переполнению
- •Iret Возврат из прерывания
- •1 Lods Загрузка операнда из строки : lodsb Загрузка байта из строки lodsw Загрузка слова из строки
- •Операнд
- •Xadd память, регистр
- •Xchg Обмен данными между операндами
- •Xlat Табличная трансляция
- •Xor Логическое исключающее или
- •Содержание
Глава 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 Команды
и алгоритмы
что приведет к неправильной адресации при обращении к полям данных. Программ)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