Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Магда Юрий - Ассемблер для процессоров Intel Pe...rtf
Скачиваний:
45
Добавлен:
01.07.2025
Размер:
565 Кб
Скачать
☆

4.3. Структура программ на ассемблере masm

Программа, написанная на ассемблере MASM, может состоять из нескольких частей, называемых модулями, в каждом из которых могут быть определены один или несколько сегментов данных, стека и кода. Любая законченная программа на ассемблере должна включать один главный, или основной (main), модуль, с которого начинается ее выполнение. Основной модуль может содержать программные сегменты, сегменты данных и стека, объявленные при помощи упрощенных директив. Кроме того, перед объявлением сегментов нужно указать модель памяти при помощи директивы . MODEL. Поскольку подавляющее большинство современных приложений являются 32‑разрядными, то основное внимание в этом разделе мы уделим именно таким программам, хотя не обойдем вниманием и 16‑разрядные программы, которые все еще используются. Начнем с 16‑разрядных программ. В следующем примере показана 16‑разрядная программа на ассемблере, в которой используются упрощенные директивы ассемблера MASM:

.model small, с ; эта директива указывается до объявления

; сегментов

.stack 100h ; размер стека 256 байт

.data ; начало сегмента данных

. . .

: данные

. . .

.code ; здесь начинается сегмент программ

main:

. . .

: команды ассемблера

. . .

end main

end

Здесь оператор end main указывает на точку входа main в главную процедуру. Оператор end закрывает последний сегмент и обозначает конец исходного текста программы. В 16‑разрядных приложениях MS‑DOS можно инициализировать сегментные регистры так, чтобы они указывали на требуемый логический сегмент данных. Листинг 4.1 демонстрирует это.

Листинг 4.1. Пример адресации сегментов в программе MS‑DOS

.model large

.data

s1 DB «TEST STRINGS»

.code

mov AX. @data

mov DS. AX

lea DX. s1

mov AH. 9h

int 21h

mov ax. 4c00h

int 21h

end

Здесь на экран дисплея выводится строка si. При помощи следующих команд в сегментный регистр DS помещается адрес сегмента данных, указанного директивой .data:

mov AX. @data

mov DS. AX

Затем строка s1 , адресуемая через регистры DS:DX, выводится на экран с использованием прерывания 9h функции 21h MS‑DOS. Попробуйте закомментировать проанализированные две строки кода и посмотреть на результат работы программы.

Для 32‑разрядных приложений шаблон исходного текста выглядит иначе:

.model flat

.stack

.data

: данные

.code

main:

. . .

: команды ассемблера

. . .

end main

end

Основное отличие от предыдущего примера – другая модель памяти (flat), предполагающая 32‑разрядную линейную адресацию с атрибутом near.

Как видно из примера, «классический» шаблон 32‑разрядного приложения содержит область данных (определяемую директивой .data), область стека (директива .stack) и область программного кода (директива .code). Может случиться так, что 32‑разрядному приложению на ассемблере потребуется несколько отдельных сегментов данных и/или кода. В этом случае разработчик может создать их с помощью директивы SEGMENT. Директива SEGMENT определяет логический сегмент и может быть описана следующим образом:

имя SEGMENT список атрибутов

. . .

имя ENDS

Замечу, что директива SEGMENT может применяться с любой моделью памяти, не только flat. При использовании директивы SEGMENT потребуется указать компилятору на то, что все сегментные регистры устанавливаются в соответствии с моделью памяти flat. Это можно сделать при помощи директивы ASSUME:

ASSUME CS: FLAT, DS:FLAT, SS:FLAT, ES:FLAT, FS:ERROR, GS:ERROR

Регистры FS и GS программами не используются, поэтому для них указывается атрибут ERROR.

Сейчас мы рассмотрим программный код 32‑разрядной процедуры на ассемблере (она называется segex), в которой используются два логических сегмента данных. Процедура выполняет копирование строки sre, находящейся в сегменте данных datai, в область памяти dst в сегменте данных data2 и содержит один логический сегмент программного кода (code segment).

Успокою читателей, незнакомых с принципами работы процедур (они рассмотрены далее в книге): в данном случае нас будет интересовать код внутри процедуры segex (команды, находящиеся между директивами segex proc и segex endp). Исходный текст программного кода процедуры segex представлен в листинге 4.2.

Листинг 4.2. Использование двух логических сегментов данных в 32‑разрядной процедуре

.586

.model flat

option casemap:none

data1 segment

src DB «Test STRING To Copy»

len EQU $‑src

data1 ends

data2 segment public

dst DB len+1 DUPC + ')

data2 ends

code segment

_seg_ex proc

assume CS: FLAT,DS:FLAT, SS:FLAT, ES:FLAT, FS:ERR0R, GS:ERR0R

mov ESI, offset datai

mov EDI, offset data2

cld

mov CX, len

rep movsb

mov EAX, offset data2

ret

_seg_ex endp

code ends

end

При использовании модели flat доступ к данным осуществляется по 32‑разрядному смещению, поэтому смысл показанных ниже команд, загружающих адреса логических сегментов (а заодно и адреса строк src и dst) в регистры ESI и EDI, думаю, понятен:

mov ESI, offset data1

mov EDI, offset data2

Группа следующих команд выполняет копирование строки src в dst, при этом регистр СХ содержит количество копируемых байтов:

cld

mov CX. len

rep movsb

В регистре ЕАХ возвращается адрес строки‑приемника dst. Обращаю внимание читателей на то, что никакой инициализации сегментных регистров не требуется, поскольку это делается при помощи директивы . model flat. Еще один важный момент: программа, использующая модель flat, выполняется в одном физическом сегменте, хотя логических сегментов может быть несколько, как в нашем случае.

Работоспособность процедуры легко проверить, вызвав ее из программы на Visual C++ .NET (нужно только включить объектный файл процедуры в проект приложения). Исходный текст приложения приведен в листинге 4.3.

Листинг 4.3. Программа, вызывающая процедуру seg_ex

#include <stdio.h>

exterr "С" char* seg_ex(void);

int main(void)

{

printf(«EXTERNAL MODULE EXAMPLE: %s\n», seg_ex());

return 0;

}

Здесь процедура segex является внешней, поэтому объявлена как extern. Результатом выполнения программы будет строка на экране дисплея

EXTERNAL MODULE EXAMPLE: Test STRING To Copy+