Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ПРОГРАММНАЯ ИНЖЕНЕРИЯ.docx
Скачиваний:
115
Добавлен:
09.09.2018
Размер:
2.83 Mб
Скачать

2.5.2. Программа типа exe

В листинге показана EXE-программа,которая выводит на экран приветствие: "Hello, World!". Сравните ее сCOM-программой,как видите, программа типа EXE имеет немного более сложный вид, зато дляEXE-файловотсутствует ограничение в 64 Кбайт, поэтому все большие программы используют именно этот формат.

 

http://www.sklyaroff.ru

 

40

 

 

 

Листинг 2.2. Программа типа EXE (hello2.asm)

 

.model

small

 

 

 

.stack

100h

 

 

 

.code

 

 

 

 

start: mov

ax,@data

 

mov

ds,ax

 

 

 

mov

ah,9

 

 

 

mov

dx,offset message

 

int

21h

 

 

 

mov

ax,4C00h

 

int

21h

 

 

 

.data

 

 

 

 

message

db

"Hello, World!",0Dh,0Ah,'$'

end start

Компиляция и линковка осуществляется точно так же, как и в предыдущем примере, командой:

ml hello2.asm

Программа ml автоматически определит, какой файл требуется создать — типа EXE или типа COM.

В результате будет получен файл hello2.exe размером 546 байт. Как видите размер EXE-файлапочти в 23 раза больше файла типа COM, хотя оба они выполняют одну и ту же функцию — выводят на экран фразу "Hello, World!". Это плата за размещение служебной информации (заголовка) внутриEXE-файла.Заголовок в EXE файлах составляет 512 байт, несложно подсчитать, что без учета заголовка код занимает всего 34 байта(546-512=34).

Рассмотрим исходный текст EXE-программы,чтобы понять, как она работает.

Директива .model small устанавливает модель памяти типа SMALL, а это значит, что мы можем разбить нашу программу на три сегмента: сегмент стека устанавливается директивой .STACK, сегмент кода начинается с директивы .CODE, а сегмент данных с директивы .DATA (в листинге я выделил эти директивы полужирным шрифтом).

Директива .STACK сразу позволяет задать размер стека, который рекомендуется устанавливать не короче 100h. Даже если вы в своей программе не будете использовать стек, его все равно необходимо задать, т. к. стек будет использовать

MS-DOS.

В каждой EXE-программесразу после метки начала кода (в нашем случаеstart), должны стоять следующие две команды:

mov ax,@data mov ds,ax

Они необходимы, чтобы загрузить в сегментный регистр DS адрес сегмента данных (.data), иначе мы не сможем обращаться в программе к данным (у нас в сегменте данных только одна строка message). Для сегментных регистров CS и SS подобные операции делать не нужно, так какMS-DOSзаносит адреса сегментов кода и стека в регистры CS и SS автоматически.

Предопределенная метка @data по сути является адресом сегмента данных. Вас наверное смущает почему мы не написали сразу более логичную на первый взгляд операцию:

mov ds,@data

Однако это нельзя сделать, т. к. в сегментные регистры можно загружать данные только из какого-либодругого регистра. Поэтому мы сначала загружаем адрес в регистр AX, а уже из регистра AX копируем в DS.

Программы типа EXE должны завершаться особым образом — вызовом DOSфункции 4Ch:

mov ax,4C00h int 21h

Здесь мы одной командой mov ax,4C00h помещаем в регистр AH значение 4Ch, а в регистр AL — код возврата (значение 0), а командаint 21h вызывает функцию на выполнение.

После загрузки программы типа EXE ее образ в памяти будет выглядеть так, как показано на рис. 2.2.

Как и в COM-файлахобразEXE-программыначинается с префикса программного сегмента (PSP), который создается и заполняется системой. Объем PSP всегда равен 256 байтам. PSP содержит данные, используемые системой при выполнении программы (в разд. 2.8 структура PSP будет рассмотрена подробно). 

 В EXE-программе определяется сегмент стека, в то время как COM-программа генерирует стек автоматически.

 Сегмент данных. В EXE программе обычно определяется сегмент данных, а регистр DS инициализируется адресом этого сегмента. В COM-программе все данные должны быть определены в сегменте кода.

4. Деление программы на сегменты. Использование сегментных регистров для адресации различных сегментов программы.

Программа на ассемблере представляет собой совокупность блоков памяти, называемых сегментами. Программа может состоять из одного или нескольких таких блоков-сегментов. Сегменты программы имеют определенное назначение, соответствующее типу сегментов: кода, данных и стека. Названия типов сегментов отражают их назначение.

Процессор имеет 6 так называемых сегментных регистров: CS, DS, SS, ES, FS и GS. Их существование обусловлено спецификой организации и использования оперативной памяти.

16-битные регистры могли адресовать только 64 Кб оперативной памяти, что явно недостаточно для более или менее приличной программы. Поэтому память программе выделялась в виде нескольких сегментов, которые имели размер 64 Кб. При этом абсолютные адреса были 20-битными, что позволяло адресовать уже 1 Мб оперативной памяти. Возникает вопрос – как имея 16-битные регистры хранить 20-битные адреса? Для решения этой задачи адрес разбивался на базу и смещение. База – это адрес начала сегмента, а смещение – это номер байта внутри сегмента. На адрес начала сегмента накладывалось ограничение – он должен был быть кратен 16. При этом последние 4 бита были равны 0 и не хранились, а подразумевались. Таким образом, получались две 16-битные части адреса. Для получения абсолютного адреса к базе добавлялись четыре нулевых бита, и полученное значение складывалось со смещением.

Сегментные регистры использовались для хранения адреса начала сегмента кода (CS – code segment), сегмента данных (DS – data segment) и сегмента стека (SS – stack segment). Регистры ES, FS и GS были добавлены позже. Существовало несколько моделей памяти, каждая из которых подразумевала выделение программе одного или нескольких сегментов кода и одного или нескольких сегментов данных: tinysmallmediumcompactlarge и huge. Для команд языка ассемблера существовали определённые соглашения: адреса перехода сегментировались по регистру CS, обращения к данным сегментировались по регистру DS, а обращения к стеку – по регистру SS. Если программе выделялось несколько сегментов для кода или данных, то приходилось менять значения в регистрах CS и DS для обращения к другому сегменту. Существовали так называемые «ближние» и «дальние» переходы. Если команда, на которую надо совершить переход, находилась в том же сегменте, то для перехода достаточно было изменить только значение регистра IP. Такой переход назывался ближним. Если же команда, на которую надо совершить переход, находилась в другом сегменте, то для перехода необходимо было изменить как значение регистра CS, так и значение регистра IP. Такой переход назывался дальним и осуществлялся дольше.

32-битные регистры позволяют адресовать 4 Гб памяти, что уже достаточно для любой программы. Каждую Win32-программу Windows запускает в отдельном виртуальном пространстве. Это означает, что каждая Win32-программа будет иметь 4-х гигабайтовое адресное пространство, но вовсе не означает, что каждая программа имеет 4 Гб физической памяти, а только то, что программа может обращаться по любому адресу в этих пределах. А Windows сделает все необходимое, чтобы память, к которой программа обращается, «существовала». Конечно, программа должна придерживаться правил, установленных Windows, иначе возникает ошибка General Protection Fault.

Под архитектурой Win32 отпала необходимость в разделении адреса на базу и смещение, и необходимость в моделях памяти. На 32-битной архитектуре существует только одна модель памяти – flat (сплошная или плоская). Сегментные регистры остались, но используются по-другому1. Раньше необходимо было связывать отдельные части программы с тем или иным сегментным регистром и сохранять/восстанавливать регистр DS при переходе к другому сегменту данных или явно сегментировать данные по другому регистру. При 32-битной архитектуре необходимость в этом отпала, и в простейшем случае про сегментные регистры можно забыть.

  1. Способы адресации данных на ассемблере

https://lektsia.com/2x2b3.html

Рассмотрев команды пересылки, мы можем рассмотреть все способы адресации операндов в командах на ассемблере.

Даже если вы поначалу не будете использовать все способы адресации, все равно я вам советую внимательно ознакомиться с ними, т. к. вы должны как минимум уметь читать чужой код с использованием различных методов адресации операндов.