Использование DOS сегментной организации памяти для загрузки и выполнения программ
При загрузке программы в оперативную память DOS инициализирует как минимум три сегментных регистра: CS, DS и SS. При этом совокупности байтов, представляющих команды процессора (код программы), и данные, помещаются из файла на диске в оперативную память, а адреса этих сегментов записываются в регистры CS и DS соответственно. Сегмент стека либо выделяется в области, указанной в программе, либо совпадает (если он явно в программе не описан) с самым первым сегментом программы. Адрес сегмента стека помещается в регистр SS. Программа может иметь несколько кодовых сегментов и сегментов данных и в процессе выполнения специальными командами выполнять переключения между ними.
Для того чтобы адресовать одновременно два сегмента данных, например при выполнении операции пересылки из одной области памяти в другую, можно использовать регистр дополнительного сегмента данных ES. Кодовый сегмент и сегмент стека всегда определяются содержимым своих регистров (CS и SS), и поэтому в каждый момент выполнения программы всегда используется какой-то один кодовый сегмент и сегмент стека. Причем если переключение кодового сегмента – довольно простая операция, то переключать сегмент стека можно только при условии четкого представления логики работы программы со стеком, иначе это может привести к зависанию системы.
Все сегменты могут использовать различные области памяти, а могут частично или полностью совпадать (рис. 3.1).
Кодовый сегмент должен обязательно описываться в программе, все остальные могут отсутствовать. В этом случае DOS при загрузке программы в оперативную память инициализирует регистры DS и ES значением адреса префикса программного сегмента PSP (Program Segment Prefics) – специальной области оперативной памяти размером 256 (100h) байт. PSP может использоваться в программе для определения имен файлов и параметров из командной строки, введенной при запуске программы на выполнение, объема доступной памяти, переменных окружения системы и т.д. Регистр SS при этом инициализируется значением сегмента, находящегося сразу за PSP, т.е. первого сегмента программы. При этом необходимо учитывать, что стек "растет вниз", иными словами при помещении в стек каких-либо значений содержимое регистра SP, указывающего на вершину стека, уменьшается на 2, а при считывании из стека – на столько же увеличивается (цифра 2 связана с тем, что стек работает со словами, а не с байтами). Таким образом, при помещении в стек каких-либо значений они могут затереть PSP и программы, находящиеся в младших адресах памяти (например, DOS), что может привести к непредсказуемым последствиям. Поэтому рекомендуется всегда явно описывать сегмент стека в тексте программы, задавая ему размер, достаточный для нормальной работы.
В примере программы HELLO из главы 1 в тексте программы явно описаны два сегмента – кода с именем CODE и данных с именем DATA. Директива ASSUME связывает имена этих сегментов, которые в общем случае могут быть произвольными, с сегментными регистрами CS и DS соответственно (рис.3.2).
Как видно из рисунка, сегмент стека в данном случае установлен на конец PSP, что при его интенсивном использовании могло бы привести к неожиданным результатам (именно по этой причине компоновщик выдал предупреждение Warning: No stack).
После инициализации в регистре IP находится смещение первой команды программы относительно начала кодового сегмента, адрес которого помещен в CS. Процессор, считывая эту команду, начинает выполнение программы, постоянно изменяя содержимое регистра IP и при необходимости CS для получения кодов очередных команд до тех пор, пока не встретит команду завершения программы.
DS после загрузки программы установлен на начало PSP, поэтому для его использования в первых двух командах программы выполняется загрузка DS значением сегмента данных программы.
Рис.3.2. Состояние сегментных регистров после загрузки программы HELLO.