Консультация № 23375
11.07.2005, 23:40
0.00 руб.
0 3 2
Здравствуйте, уважаемые эксперты!
Увидел, что при создании exe файла под DOS(на MASM) необходимо явно осуществить связывание регистра DS с сегментом данных программы:
mov ax,@data
mov ds,ax
Без этого в DS вообще не понятно, что лежит.

Вопросы =)
1) Откуда при ассемблировании известен физический адрес сегмента данных @data? Ведь никто не знает куда его запихнет операционная система.
Если заглянуть в exe-шник, то видно, что MASM вставил вместо команды "mov ax,@data" байты "B8 00 00", что вообще-то соответствует команде "mov ax,0".
Две сгенерированные программы отличаются только 2 байтами в exe-заголовке(по смещениям 6h и 1Eh).
А в отладчике там стоит именно mov ax,<адрес сегмента данных>!
2) Зачем это надо(явное связывание)? Почему не хватает директивы assume ds:<имя сегмента данных>?
На www.microsoft.com по поводу assume написано: "Enables error checking for register values. After an ASSUME is put into effect, the assembler watches for changes to the values of the given registers.".
Что, по-моему, означает, что он ничего не привязывает, а лишь чего-то проверяет(чего, кстати, не понятно).
В связи с этим напрашивается...
3) Откуда операционная система знает какой сегментный регистр каким адресом инициализировать, если не делать никаких указаний на функциональное назначение сегментов при их определении?
Скажем, все ли правильно в таком объяснении: "Сегмент стека всегда в программе один, все сегменты из разных объектных файлов объединяются. У всех них необходимо явно указывать директиву stack, поэтому адрес того, что нужно положить в SS известен.
В CS вначале кладется сегмент с точкой входа программы, а при вызове процедуры из другого сегмента CS заменяется на адрес сегмента с этой процедурой(адрес возврата кладется в стек). А DS на то и необходимо явно инициализировать, потому что программа не знает какой из множества ее сегментов выступает в качестве сегмента данных.(вопрос 1)"
4) Кстати про стек... Какой нужен его минимальный размер? В отладчике видно, что почему-то даже в процессе 20 прерывания SP уменьшается довольно-таки сильно, то есть для размера стека уже нужен какой-то нижний предел.
И если его не хватит, то SP становится равным FFFFh, то есть начинают портится какие-то чужие данные?

Заранее спасибо за все ответы =)

Приложение:
.model small.datamessage db ‘Hello World!$‘.stack 256h.codemain proc mov ax,@data ; <-- mov ds,ax ; <-- mov ah,9 mov dx,offset message int 21h mov ax,4c00h int 21h main endpend main

Обсуждение

Неизвестный
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. Тогда это позволит без проблем пережить аппаратные прерывания (они первоначально используют стек прерванной программы для сохранения точки прерывания и некоторых регистров).

А если стека не хватит, то ты либо завесишь комп, либо получишь виндовое окошко с сообщением: "Программа выполнила недопустимую операцию и будет закрыта"
давно
Советник
419
1012
14.07.2005, 22:30
общий
это ответ
Здравствуйте, seerix!
есть таблица перераспределения. RELOCATION TABLE.
там все адреса этих чисел. чтобы их заменить на настоящие.
то есть, там сегменты по порядку идут. 0, 1, 2 и т.д.
в процессе загрузки программы загрузчик просматривает программу и заменяет соотв сегменты.
давно
Советник
419
1012
14.07.2005, 22:33
общий
как тебя похвалили ;)а ведь всё это описано в справке ;)почитать лень!!!
Форма ответа