12.07.2005, 13:01
общий
это ответ
Здравствуйте, seerix!
EXE-программа, в отличии от COM, может занимать несколько сегментов, причем может иметь несколько сегментов кода, несколько сегментов данных и т.д. Надеюсь, что ты это знаешь.
Теперь по вопросам.
1. При ассемблировании физический адрес сегмента данных неизвестен. В заголовок EXE-программы помещаются все точки программы, в которых идет обращение к физическому адресу сегмента (псевдопеременные @CODE, @DATA и т.п., обращения типа SEG <метка>). Постоянными являются только смещения относительно начала сегмента. Операционная система при загрузке просматривает таблицу привязки сегментов и подставляет реальные значения в эти точки программы. Точнее, значения по указанным адресам изменяются на постоянное число. Потому что при линковке программы все базовые адреса сегментов заполняются определенным образом (например, первый кодовый сегмент равен 0, второй кодовый сегмент - 1000h, сегмент данных - 2000h, сегмент стека - 3000h, второй сегмент данных - 4000h). Пусть ОС грузит программу с сегмента 8000h. Тогда она просто прибавляет разницу между 8000 и 0 (т.е. 8000) ко всем адресам по указанным точкам. Тогда первый кодовый сегмент получит адрес 8000, второй кодовый сегмент - 9000, сегмент данных - A000, сегмент стека - B000, второй сегмент данных - C000.
Это же самое происходит и тогда, когда программа не запускается, а считывается в память для отладки. Поэтому под отладчиком ты видишь правильные адреса сегментов.
2. Assume - это директива компилятора. Она подсказывает компилятору, с помощью какого сегментного регистра лучше всего генерировать доступ к переменным данного сегмента. Допустим, у тебя будут 2 сегмента данных. В первом (DATA_ARRAY) ты разместишь постоянный массив из 65536 байт (больше в этот сегмент ничего уже не влезет), а во втором (DATASEG) - все остальные переменные.
Теперь записываешь команду ASSUME cs:CODESEG, ds:DATASEG, es:DATA_ARRAY, ss:STACKSEG
И когда ты будешь обращаться к простым переменным, обращение компилятором будет производиться через регистр ds, а когда будешь обращаться к массиву - то через es.
Но команда ASSUME НЕ УСТАНАВЛИВАЕТ значения сегментных регистров! Это действие остается на совести программиста! Поэтому если ты вручную не напишешь:
push DATA_ARRAY
pop es
то не сможешь получить доступ к своему массиву.
3. А операционной системе на все наплевать! Она тупо берет значения из заголовка EXE-программы и на основании их модифицирует байты по указанным адресам. Значения регистров CS, IP, SS и SP устанавливаются также из заголовка (точка входа и вершина стека), причем CS и SS заполняются реальными адресами.
Насчет того, что стек всегда один. В принципе, это так. Компилятор объединяет все сегменты стека в один. Но при этом ты сам запросто можешь перенести стек куда угодно. Но в каждый конкретный момент ты можешь работать только с одним сегментом стека. И одним сегментом кода (хотя их всего может быть несколько). А сегментов данных не только может быть несколько, но и одновременно (без перезагрузки сегментных регистров) ты можешь обращаться до 6-ти сегментов (4 сегмента данных: DS, ES, FS, GS; переменные в сегменте кода через CS и переменные в стеке (например, параметры и локальные переменные процедур) через SS).
4. Ну ты сам считай, какой нужен минимальный размер. Если ты стек не используешь или используешь мало (неглубокая вложенность процедур с малым числом параметров, стек только для хранения регистров), то тебе и 256 байт будет более чем достаточно. Если же ты используешь рекурсию, локальные переменные (в т.ч. и массивы), глубокая вложенность процедур, то тебе и всего сегмента может не хватить. Прикинь, насколько сильно ты используешь стек и исходи из этого. Неплохо увеличить твою максимальную прикидку байт на 100-256. Тогда это позволит без проблем пережить аппаратные прерывания (они первоначально используют стек прерванной программы для сохранения точки прерывания и некоторых регистров).
А если стека не хватит, то ты либо завесишь комп, либо получишь виндовое окошко с сообщением: "Программа выполнила недопустимую операцию и будет закрыта"