Лекции по Вычислительным машинам, системам и сетям   

5. Программирование на языке Ассемблер

5.8. EXE- и COM-программы

Любая программа транслируется в исполняемый файл, имеющий либо EXE, либо COM формат.

В EXE-программе создаются отдельные сегменты для кода, для данных и для стека, причем, если программа большая, то сегментов для данных и для кода может быть даже несколько. Все эти сегменты так или иначе описывает программист при написании программы. Сегмент стека в принципе можно не описывать (не создавать), в этом случае программа будет работать со стеком DOS, хотя tlink при трансляции выдаст предупреждение (Warning: no stack), которое можно проигнорировать.

В COM-программе для кода, данных и стека отводится один общий сегмент. То есть COM-формат используется при написании программ небольшого объема, полностью помещающихся в 64 Кбайта памяти. В дальнейшем, в основном, будем придерживаться именно этого формата.

Загружая в память на исполнение как COM-, так и EXE-программу, DOS, помимо самой программы, размещает в памяти еще и так называемый «префикс программного сегмента» (PSP). Занимает PSP 256 байтов памяти и в него DOS записывает различную служебную информацию. Для EXE-программ DOS сама выделяет память под PSP, т.е. программист об этом заботиться не должен. А вот для COM-программ ситуация иная, здесь программист сам должен выделить память под PSP с помощью директивы org 100h (или org 256). Ситуация в памяти после загрузки на исполнение EXE-программы показана на рис. 5.6.

ds; es à

 
 

PSP

ss à

 
 

Сегмент стека

ss:sp à

 
 

Сегмент данных

cs:ip à

 
 

Программа

(сегмент кода)

Рис. 5.6

Как видно из рис. 5.6, сегментный регистр ds сразу после загрузки указывает «не туда, куда надо» (ds указывает на первый байт PSP, а должен указывать на первый байт сегмента данных). Именно поэтому в начале EXE-программы ds нужно настроить надлежащим образом.

Стандартно EXE-программа пишется следующим образом:

stack segment  stack        ;описание сегмента стека

;Первое слово stack – название сегмента, можно было ;назвать сегмент и по-другому, например, vasya.

;Транслятор предупредит, что используем зарезервиро-;ванное слово stack не по назначению. Это предупреждение ;можно проигнорировать. Второе слово stack говорит ;транслятору, а потом и DOS, что это сегмент стека.

db 100 dup (0)          ;выделяем под стек 100 байт (можно ;больше, можно меньше)

stack ends                    ;конец сегмента стека

data segment           ;описание сегмента данных

; Здесь располагаются переменные и массивы

data ends

code segment           ;описание сегмента кода

assume cs:code, ds:data

;Это директива для транслятора. Объяснять ее назначение ;долго и сложно. Проще поверить, что она необходима

start:     ;программа начинается с метки (любой!)

mov ax, data

mov ds, ax      ;настраиваем ds на начало сегмента ;данных

;здесь пишется программа         

mov ah, 4ch

int 21h        ;стандартный выход из программы

code ends

end start         ;startметка, с которой мы начали ;программу.

Дадим необходимые пояснения. Все, что находится в строке программы после «точки с запятой», транслятор понимает как комментарий. Сегменты stack, data и code можно было назвать и другими именами, но тогда мы должны использовать в директивах ends и assume и команде mov ax, data эти другие имена. Изменять второе слово stack нельзя, тогда у нас не будет создан сегмент стека. Причины настройки ds на начало сегмента данных объяснялись в этом разделе выше. Остается выяснить, зачем нужен стандартный выход из программы.

Когда процессор выполняет программу, он «понятия не имеет», где она кончается. Он просто выбирает команды из памяти и их выполняет. Выполнив последнюю команду программы, процессор продолжит выбирать информацию из памяти и будет пытаться выполнять эту информацию в качестве команд. Поскольку после последней команды программы в памяти, скорее всего, «мусор», такая ситуация почти наверняка приведет к зависанию системы. Поэтому в конце программы организуется корректный возврат управления в ту среду (DOS NAVIGATOR или Far или ….), из которой была запущена наша программа.

Ситуация в памяти после запуска СОМ-программы представлена на рис. 5.7.

cs; es; ds; ss à

 
 

PSP

cs:ip à

 
 

Программа

ss:sp à

 

Рис. 5.7

Как видно из рис. 5.7, DOS настраивает все сегментные регистры на первый байт PSP. В регистр ip программист, выделяя директивой org 100h место под PSP, заносит 100h. После этого пара cs:ip указывает на первую команду программы. В указатель стека sp DOS загрузит число fffeh.

После этого пара ss:sp  указывает на ячейку, отстоящую почти на 64 Кайта от начала PSP, с тем, чтобы стек располагался как можно дальше от программы и данных и не мог их испортить при своем росте в сторону младших адресов.

Стандартно СОМ-программа пишется таким образом:

code segment

assume cs: code, ds: code

org 100h

start:

jmp begin        ; перепрыгиваем область данных

;здесь можно располагать переменные и массивы

begin:

; здесь располагается программа

mov ah, 4ch            

int 21h                ; стандартный выход из программы

;здесь можно располагать переменные и массивы

code ends

end start

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



*****

© 2009-2017 Банк лекций siblec.ru
Лекции для преподавателей и студентов. Формальные, технические, естественные, общественные, гуманитарные, и другие науки.