Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скляров И. Изучаем Assembler за 7 дней (2010).pdf
Скачиваний:
1335
Добавлен:
23.02.2015
Размер:
2.11 Mб
Скачать

http://www.sklyaroff.ru

39

В MS-DOS строки выводится на экран до первого встреченного символа $, поэтому строка "Hello, World!" завершается этим символом. Как поступить, когда нужно вывести на экран сам символ $ мы узнаем позже.

Код 0Dh является управляющим символом ASCII "возврат каретки", а код 0Ah символом ASCII "перевод строки". Эти два управляющих символа переводят курсор на первую позицию следующей строки.

Команда RET предназначена для выхода из процедуры, но в COM-файлах эта команда корректно завершает программу.

Директива END должна завершать код программы. Вместе с этой директивой всегда указывается метка, с которой начинается выполнение программы (start в нашем случае).

Отмечу, что большинство примеров программ реального режима далее в книге будут рассчитаны на компиляцию в COM-файлы, но вы легко сможете переписать их в EXE-формат, если такая необходимость появится.

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

Рис. 2.1. Образ памяти программы типа COM

Образ программы начинается с префикса программного сегмента (PSP), который создается и заполняется системой. Объем PSP всегда равен 256 байтам. PSP содержит данные, используемые системой при выполнении программы (в разд. 2.8 структура PSP будет рассмотрена подробно). Как видите, программа типа COM состоит из единственного сегмента, в котором вместе располагаются код, данные и стек. Сегментные регистры (CS, DS, ES, SS) инициализируются автоматически и содержат одно и то же значение, указывающее на начало PSP. Значение регистра IP=100h.

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:

http://www.sklyaroff.ru

41

mov ax,4C00h int 21h

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

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

Рис. 2.2. Образ памяти программы типа EXE

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

CS устанавливается на начало сегмента кода, в IP при этом загружается относительный адрес первой команды;

DS и ES указывают на начало PSP, что позволяет программе с помощью этих значений обращаться к содержимому PSP;

SS указывает на начало сегмента стека, в SP при этом загружается смещение конца сегмента стека.

2.6.Основные различия между программами типа EXE и COM

В таблице 2.1 представлены основные отличия программ типа COM и EXE.

 

http://www.sklyaroff.ru

 

 

 

 

 

 

 

42

 

Таблица 2.1. Основные различия между программами типа COM и EXE

 

 

 

 

 

 

 

 

Программа типа COM

Программа типа EXE

 

 

 

 

 

 

 

 

 

 

Максимальный размер

65536 минус 256 (префикс)

Неограничен

 

 

 

 

файла (в байтах)

минус 2 (стек), таким

 

 

 

 

 

 

 

образом,

максимальный

 

 

 

 

 

 

 

размер

составляет 65278

 

 

 

 

 

 

 

байт

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Служебная информация в

Отсутствует

 

512

байт

(располагается

 

файле (заголовок)

 

 

 

непосредственно перед кодом

 

 

 

 

 

программы)

 

 

 

 

 

 

 

 

 

 

Максимальное количество

Только один

 

Несколько сегментов

кода и

 

сегментов

 

 

 

несколько сегментов данных.

 

 

 

 

 

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

всегда

только

 

 

 

 

 

один

 

 

 

 

 

 

 

 

 

 

 

 

Модель памяти

Только TINY

 

Любая кроме TINY

 

 

 

 

 

 

 

Размер стека (в байтах)

Генерируется автоматически

Определяется программистом

 

 

и составляет: 65536 минус

в сегменте стека (директива

 

 

256 (префикс) минус размер

.stack). Должен быть не

 

 

выполняемой

программы и

меньше 100h

 

 

 

 

 

данных

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Содержимое стека при

Слово со значением 0

Инициализирован

 

или

 

входе

 

 

 

неинициализирован

 

 

 

 

 

 

 

 

 

Точка входа

PSP:100h

 

Определяется

оператором

 

 

 

 

 

END

 

 

 

 

 

 

 

 

 

 

 

 

 

IP при входе

100h

 

 

Относительный

адрес

точки

 

 

 

 

 

входа

 

 

 

 

 

 

 

 

 

 

Содержимое регистра CS

Адрес PSP

 

Адрес сегмента, содержащего

 

при входе

 

 

 

точку входа

 

 

 

 

 

 

 

 

 

 

 

 

Содержимое регистра DS

Адрес PSP

 

Адрес PSP

 

 

 

 

при входе

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Содержимое регистра ES

Адрес PSP

 

Адрес PSP

 

 

 

 

при входе

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Содержимое регистра SS

Адрес PSP

 

Адрес сегмента стека

 

 

 

при входе

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Содержимое регистра SP

Адрес

вершины доступной

Размер стека

 

 

 

 

при входе

памяти

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Способ завершения

ret

 

 

mov ax,4C00h

 

 

 

 

 

 

 

 

int 21h

 

 

 

2.7. Функции BIOS и DOS

Упрограммиста под MS-DOS существует три способа работы с устройствами:

1.Непосредственная работа на самом низком уровне с использованием портов ввода-вывода.

2.С помощью вызова функций BIOS.

3.С помощью вызова функций DOS.

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

1. При переключении процессора в "защищенный" режим, так как функции DOS и BIOS в этом случае становятся недоступными.

http://www.sklyaroff.ru

43

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

3.При программировании нестандартного, нового, либо устаревшего оборудования для которого нет соответствующих функций BIOS и DOS.

4.Если требуется повышенная защита информации, например, при использовании крипто-ключа подключаемого к LPT, COM, либо USB-порту компьютера.

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

Функции BIOS и DOS при обращении к устройствам тоже работают с портами вводавывода, но эта работа скрыта от программиста. Все, что нужно сделать программисту — это просто вызвать функцию и считать возвращаемый ею результат, а функция все сделает самостоятельно. Существуют тысячи различных функций BIOS и DOS (где взять их описание будет рассказано ниже).

Все функции BIOS и DOS вызываются с помощью программных прерываний (командой INT). Прерывания мы будем подробно рассматривать в разд. 6.5. Сейчас лишь стоит отметить, что в реальном режиме процессора существует всего 256 прерываний, они нумеруются от 0h до FFh (от 0 до 255 в десятичной нотации).

Функции MS-DOS используют всего 32 прерывания с номерами в диапазоне от 20h до 3Fh (с 32-го по 63-е в десятичной нотации).

Функций BIOS используют диапазон прерываний с 10h по 1Fh (c 16-го по 31-е в десятичной нотации) и c 40h по 4Ah (с 64-го по 74-е в десятичной нотации), а также прерывание под номером 5h.

Несмотря на то, что функции BIOS и DOS используют лишь небольшие диапазоны прерываний, с помощью этих прерываний можно вызвать тысячи различных функций.

Например, с помощью прерывания INT 21h можно вызывать несколько сотен различных функций MS-DOS. Номер нужной функции обычно помещается в регистр AH перед вызовом прерывания. Кроме этого большинство функций требуют чтобы в определенные регистры были помещены различные необходимые параметры (аргументы функции).

Например, вспомним, как мы вызывали функцию DOS 09h:

mov

ah,9

; номер функции 09h

mov

dx,offset message

; аргумент функции

int

21h

; вызываем прерывание 21h

После того как функция выполнит свою работу, некоторые регистры будут содержать возвращаемые значения. Результат работы большинства функций возвращается в регистр AL. Например, в результате успешного выполнения функции DOS 09h в регистре AL окажется значение 24h (код символа $).

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

Описание всех функций DOS и BIOS можно найти в каких-нибудь старых справочниках по программированию под MS-DOS, а также в интернете в знаменитом "листе прерываний Ральфа Брауна" (Ralf Brown's Interrupt List) по адресу http://www.cs.cmu.edu/~ralf/files.html или по адресу http://www.ctyme.com/rbrown.htm. Лист прерываний Ральфа Брауна содержит описания всех прерываний от 0 до 255 и соответственно всех функций DOS и BIOS, которые используют определенные прерывания, но эти описания на английском языке. Последняя версия листа прерываний Ральфа Брауна имеет номер 61 (Release 61) и, по-видимому, этот лист обновляться уже не будет никогда.

Я сначала хотел сделать в конце этой книги приложение с описаниями всех функций DOS и BIOS, но отказался от этой затеи, т. к. во-первых, по количеству страниц такое приложение получилось бы в несколько раз больше основной части книги, а вовторых, программирование под MS-DOS уже не актуально. Напомню, мы изучаем основы программирования под MS-DOS, лишь только для того чтобы нам проще