- •1. Основы программирования для dos 9
- •1.1.7. Средства bios 21
- •2. Приемы системного программирования 57
- •Введение
- •1.Основы программирования для dos
- •1.1. Структура и выполнение программ в dos
- •Программа типа сом
- •Программа типа ехе
- •1.2.Использование служебных функций dos и bios
- •1.3.Вывод на экран в текстовом режиме
- •Средства dos
- •Средства bios
- •Выбор видеорежима
- •Управление положением курсора
- •Вывод символов на экран
- •Прямая работа с видеопамятью
- •1.4.Ввод с клавиатуры
- •Средства dos
- •Средства bios
- •1.5.Графические видеорежимы
- •Работа со стандартными графическими режимами
- •Работа с svga-режимами
- •(Продолжение таблицы 4)
- •(Окончание таблицы 4)
- •1.6.Работа с мышью
- •1.7.Другие устройства
- •Системный таймер
- •Последовательный порт
- •Параллельный порт
- •1.8.Работа с файлами
- •Создание и открытие файлов
- •Чтение из файла и запись в файл
- •Закрытие и удаление файла
- •Поиск файлов
- •Управление файловой системой
- •1.9.Управление памятью
- •Обычная память
- •Область памяти umв
- •Область памяти нма
- •Интерфейс ems
- •Интерфейс xms
- •1.10.Организация процессов
- •Запуск программ на выполнение
- •Переменные окружения
- •Командные параметры
- •2.Приемы системного программирования
- •2.1.Управляющие структуры
- •Структуры if... Then... Else
- •Структуры case
- •Конечные автоматы
- •2.2.Процедуры и функции
- •Передача параметров
- •Передача параметров по значению
- •Передача параметров по ссылке
- •Передача параметров по возвращаемому значению
- •Передача параметров по результату
- •Передача параметров по имени
- •Передача параметров отложенным вычислением
- •Передача параметров в регистрах
- •Передача параметров в глобальных переменных
- •Передача параметров в стеке
- •Передача параметров в потоке кода
- •Передача параметров в блоке параметров
- •Локальные переменные
- •2.3.Обработка прерываний
- •Обработчики прерываний
- •Прерывания от внешних устройств
- •Взаимодействие прикладных и системных обработчиков прерываний
- •2.4.Резидентные программы
- •Системные средства организации резидентных программ
- •Взаимодействие с резидентной программой
- •2.5.Драйверы устройств в dos
- •Литература
1.10.Организация процессов
В разделе 1.1 уже рассматривались основные характеристики исполнимых в DOS файлов — COM и EXE. Здесь вопросы, связанные с организацией DOS-процессов, рассматриваются более подробно. Описание резидентных программ, занимающих важное место среди DOS-процессов, вынесено в отдельный раздел (2.4).
Запуск программ на выполнение
Как и любая операционная система, DOS может загружать и выполнять программы. Во внешней памяти (на диске) программы хранятся в исполнимых файлах двух типов — ЕХЕ и СОМ. Файлам типа COM обычно имеют расширение .com, а файлы типа ЕХЕ — .exe, однако DOS использует свои методы для определения типа файла. Заголовок ЕХЕ-файла обязательно начинается с символов «MZ» или «ZM», и если они присутствуют в начале исполнимого файла и, кроме того, файл длиннее некоторого порогового значения (разного для разных версий DOS), то он загружается как ЕХЕ, если нет — как СОМ.
В ходе загрузки программы происходит формирование служебной области данных PSP (Program Segment Prefix — префикс программного сегмента) и настройка сегментных регистров, определяющих положение сегментов программы в памяти. Область PSP занимает 256 байт и всегда располагается перед сегментом команд (кодов) программы. Как ОС, так и сама программа в некоторых случаях могут обращаться к полям этой области.
СОМ-файлы содержат просто машинный код программы, переносимый в память без изменений и не содержащий служебной информации. По окончании загрузки в память СОМ-программы все четыре сегментных регистра оказываются настроенными на префикс программного сегмента PSP, то есть на единственный сегмент программы, включающий область PSP, а также участки кода, данных и стека; такую структуру можно также представить как несколько сегментов с общим началом (см. рис. 3). Остальные регистры устанавливаются следующим образом: AL = FFh, если первый параметр командной строки содержит неправильное имя диска (например, z:/something), иначе — 00h; АН = FFh, если второй параметр содержит неправильное имя диска, иначе 00h; SP = адрес последнего слова в сегменте (обычно FFFEh; меньше, если не хватает памяти).
-
256 б
CS=DS=ES=SS PSP
IP
команды
адреса
памяти
64 Кб
данные
SP
стек
Рис. 3. Пример организации программы СОМ после запуска
В начале ЕХЕ-файла размещен заголовок, содержащий признак формата ЕХЕ, размер программы, размер памяти, зарезервированной для програмы, информацию, определяющую положение сегментов стека и команд в программном модуле, а также положение соответствующих указателей (SP и IP), список команд с абсолютными адресами, не подлежащих модификации, и другие служебные данные, использующиеся при загрузке модуля в память. Следом за заголовком располагается собственно загрузочный модуль.
По окончании загрузки регистры CS и SS указывают на сегменты команд и стека, а указатели IP и SP определяют положение точки входа и вершины стека соответственно. Регистры DS и ES оказываются настроенными на область PSP (см. рис. 4). Если в программе предусмотрен отдельный сегмент данных (как это обычно и бывает), то программист должен включить в программу команды, настраивающие регистр DS на сегмент данных.
-
256 б
ES=DS PSP
CS:IP
сегмент
команд
(DS )
сегмент
данных
64 Кб
SS сегмент стека
64 Кб
адреса
памяти
SP 64 Кб
Рис. 4. Пример организации программы EXE после запуска
Поля PSP заполняются системой следующим образом:
+00h: слово — CDh 20h — команда INT 20h. Если СОМ-программа завершается командой RETN, управление передается на эту команду. Введено для совместимости с командой "CALL 0" операционной системы СР/М (предшественницы DOS)
+02h: слово — сегментный адрес первого байта после области памяти, выделенной для программы
+04h: байт — не используется DOS
+05h: 5 байт — 9Ah F0h FEh 1Dh F0h — команда CALL FAR на абсолютный адрес 000C0h, записанная так, чтобы второй и третий байты составляли слово, равное размеру первого сегмента для СОМ-файлов (в этом примере FEF0h). Введено для совместимости с командой СР/М CALL 5
+0Ah: 4 байта — адрес обработчика INT 22h (выход из программы)
+0Eh: 4 байта — адрес обработчика INT 23h (обработчик нажатия Сtrl-Break)
+12h: 4 байта — адрес обработчика INT 24h (обработчик критических ошибок)
+16h: слово — сегментный адрес PSP процесса, из которого был запущен текущий
+18h: 20 байт — JFT — список открытых идентификаторов, один байт на идентификатор, FFh — конец списка
+2Ch: слово — сегментный адрес копии окружения для процесса
+2Eh: 2 слова — SS:SP процесса при последнем вызове INT 21h
+32h: слово — число элементов JFT (по умолчанию 20)
+34h: 4 байта — дальний адрес JFT (по умолчанию PSP:0018)
+38h: 4 байта — дальний адрес предыдущего PSP
+3Ch: байт — флаг, указывающий, что консоль находится в состоянии ввода 2-байтного символа
+3Dh: байт — флаг, устанавливаемый функцией В711 прерывания 2Fh (при следующем вызове INT 21h для работы с файлом имя файла будет заменено на полное)
+3Eh: слово — не используется в DOS
+40h: слово — версия DOS, которую вернет функция DOS 30h (DOS 5.0+)
+42h: 12 байт — не используется в DOS
+50h: 2 байта — CDh 21h — команда INT 21h
+52h: байт — CBh — команда RETF
+53h: 2 байта — не используется в DOS
+54h: 7 байт — область для расширения первого FCB
+5Ch: 16 байт — первый FCB, заполняемый из первого аргумента командной строки
+6Ch: 16 байт — второй FCB, заполняемый из второго аргумента командной строки
+7Ch: 4 байта — не используется в DOS
+80h: 128 байт — командная строка и область DTA по умолчанию
Программа записывается в память, начиная с адреса PSP:0100h. Если загружается ЕХЕ-программа, использующая дальние процедуры или сегменты данных, командный процессор DOS модифицирует эти команды так, чтобы используемые в них сегментные адреса соответствовали сегментным адресам, которые получили эти процедуры и сегменты данных при загрузке программы в память.
Все эти действия выполняет одна функция DOS — загрузить и выполнить программу ("Exec").
Функция DOS 4Bh — Загрузить и выполнить программу
Ввод: АН = 4Bh
AL = 00h — загрузить и выполнить
AL = 01h — загрузить и не выполнять
DS:DX — адрес ASCIZ-строки с полным именем программы
ES:BX — адрес блока параметров ЕРВ:
+00h: слово — сегментный адрес окружения, которое будет скопировано для нового процесса (или 0, если используется текущее окружение)
+02h: 4 байта — адрес командной строки для нового процесса
+06h: 4 байта — адрес первого FCB для нового процесса
+0Ah: 4 байта — адрес второго FCB для нового процесса
+0Eh: 4 байта — здесь будет записан SS:SP нового процесса после его завершения (только для AL = 01)
+12h: 4 байта — здесь будет записан CS:IP (точка входа) нового процесса после его завершения (только для AL = 01)
AL = 03h — загрузить как оверлей
DS:DX — адрес ASCIZ-строки с полным именем программы
ES:BX — адрес блока параметров:
+00h: слово — сегментный адрес для загрузки оверлея
+02h: слово — число, которое будет использовано в командах, использующих непосредственные сегментные адреса,— обычно то же самое число, что и в предыдущем поле, 0 для СОМ-файлов
AL = 05h — подготовиться к выполнению (DOS 5.0+)
DS:DX — адрес следующей структуры:
+00h: слово — 00h
+02h: слово — бит 0 — программа — ЕХЕ; бит 1 — программа — оверлей
+04h: 4 байта — адрес ASCIZ-строки с именем новой программы
+08h: слово — сегментный адрес PSP новой программы
+0Ah: 4 байта — точка входа новой программы
+0Eh: 4 байта — размер программы, включая PSP
Вывод: CF = 0, если операция выполнена, ВХ и DX модифицируются,
CF = 1, если произошла ошибка, АХ = код ошибки (2 — файл не найден, 5 — доступ к файлу запрещен, 8 — не хватает памяти, 0Ah — неправильное окружение, 0Bh — неправильный формат)
Для подфункций 00 и 01 Exec требуется, чтобы было достаточно свободной памяти для загрузки программы. Как уже говорилось, обычно программа по окончании загрузки становится владельцем максимального блока памяти в системе (чаще всего — всей свободной памяти). ЕХЕ-программы, однако, могут быть сформированы так, чтобы им при загрузке отводилась область памяти меньшего объема (это делается с помощью определенного указания компоновщику программы). СОМ-программы для уменьшения отведенного им блока памяти до минимально необходимого должны воспользоваться функцией DOS 4Ah.
При вызове подфункции 0З DOS загружает оверлей (перекрытие). Оверлеи используются в программах, рассчитанных на выполнение в среде с ограниченным объемом памяти — они загружаются в память, выделенную текущим процессом (обычно, поверх уже ненужных данных). Перед загрузкой оверлея программа должна убедиться, что памяти для загрузки достаточно.
Функция Exec игнорирует расширение файла и различает ЕХЕ- и СОМ-файлы по первым двум байтам заголовка («MZ» для ЕХЕ-файлов).
Подфункция 05 должна вызываться после загрузки и перед передачей управления на программу, причем никакие прерывания DOS и BIOS нельзя вызывать после возвращения из этой подфункции и до перехода на точку входа новой программы.
Загруженной и вызванной таким образом программе предоставляется несколько способов завершения работы. Способ, который чаще всего применяется для СОМ-файлов,— команда RETN. При этом управление передается на адрес PSP:0000, где располагается код команды INT 20h.
Соответственно можно завершить программу сразу, вызвав INT 20h, но оба эти способа требуют, чтобы CS содержал сегментный адрес PSP текущего процесса. Кроме того, они не позволяют вернуть код возврата, который может передать предыдущему процессу информацию о том, как завершилась запущенная программа. Рекомендованный способ завершения программы — функция DOS 4Ch (Exit).
Функция DOS 4Ch — Завершить программу
Ввод: АН = 4Ch
AL = код возврата
Значение кода возврата можно использовать в пакетных файлах DOS как переменную ERRORLEVEL и определять из программы с помощью функции DOS 4Dh.
Функция DOS 4Dh — Определить код возврата последнего завершившегося процесса
Ввод: АН = 4Dh
Вывод: АН = способ завершения:
00h — нормальный
01h — Ctrl-Break
02h — критическая ошибка
03h — программа осталась в памяти как резидентная
AL = код возврата
CF = 0