Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 3000239.doc
Скачиваний:
23
Добавлен:
30.04.2022
Размер:
1.12 Mб
Скачать

5.3. Начальная загрузка сегментных регистров

Итак, директивой ASSUME программист сообщает, что все имена из таких-то программных сегментов должны сегментироваться по таким-то сегментным регистрам, и требуем, чтобы ассемблер формировал машинные команды с соответствующими префиксами. Однако это еще не значит, что программа будет работать правильно. Скажем, по команде MOV АХ,А2 будет сформирована команда MOV AX,ES:A2, но если регистр ES не будет указывать на начало сегмента А, то такая команда проработает неправильно. Нужно еще, чтобы в сегментных регистрах находились правильные значения – номера соответствующих сегментов памяти. Но в начале выполнения программы в этих регистрах ничего нет. Поэтому выполнение программы надо начинать с команд, которые загружают в сегментные регистры номера соответствующих сегментов памяти (напомним, что директива ASSUME такую загрузку не осуществляет). Рассмотрим, как делается такая загрузка.

Пусть регистр DS надо установить на начало сегмента В. Напомним, что имени сегмента в языке ассемблера приписывается значение – первые 16 битов начального адреса этого сегмента, а это как раз то, что должно быть записано в сегментный регистр, поэтому надо выполнить присваивание DS:=B. Однако сделать это командой MOV DS.B нельзя, поскольку имя сегмента – это константное выражение, т. е. непосредственный операнд, а по команде MOV запрещена пересылка непосредственного операнда в сегментный регистр. Поэтому такую пересылку надо делать через другой, несегментный регистр, например, через АХ:

MOV АХ, В

MOV DS, AX ; DS: =B

Аналогично загружается и регистр ES.

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

Теперь о регистре SS, который должен указывать на начало стека. Загрузить этот регистр можно двояко. Во-первых, его можно загрузить в самой программе -так же, как мы загружаем регистры DS и ES. Во-вторых, такую загрузку можно поручить операционной системе. Для этого в директиве SEGMENT, открывающей описание сегмента стека, надо указать специальный параметр STACK, например:

S SEGMENT STACK

В таком случае загрузка S в регистр SS будет выполнена автоматически до начата выполнения программы. Ясно, что из этих двух вариантов загрузки регистра SS лучше второй, поэтому в дальнейшем при описании сегмента стека всегда будет указываться параметр STACK.

5.4. Структура программы

Теперь необходимо рассказать о том, как выглядит полная программа на языке ассемблера. Следует отметить, что какой-либо фиксированной структуры программы на языке ассемблера нет, но для небольших программ, в которых используется три сегмента – команд, данных и стека. Типичной является такая структура:

STACK SEGMENT STACK ; сегмент стека

DB 128 DUP (?)

STACK ENDS

DATA SEGMENT ; сегмент данных

<описания переменных и т. п.>

DATA ENDS

CODE SEGMENT ; сегмент команд

ASSUME CS: CODE, DS: DATA, SS: STACK

START; MOV AX, DATA

MOV DS, AX ;загрузка DS

«остальные команды программы>

CODE ENDS

END START ;конец программы, точка входа

Следует пояснить эту структуру программы.

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

Сегмент стека в нашей программе описан с параметром STACK (совпадение этого параметра с именем сегмента допустимо), поэтому в самой программе нам уже не надо загружать сегментный регистр SS. Не надо, как уже отмечалось, загружать и регистр CS. Поэтому в начале программы, как видно, загружается лишь регистр DS.

Относительно сегмента стека нужно сделать следующее замечание. Даже если сама программа не использует стек, описывать в программе сегмент стека все равно надо. Дело в том, что стек программы использует операционная система при обработке прерываний, возникающих (например, при нажатии клавиш на клавиатуре) в процессе работы программы. А пока лишь следует отметить, что в такой ситуации рекомендуемый размер стека (с запасом) – 128 байтов.

В конце программы обязательно надо указать директиву END. Она является признаком конца текста программы, все строки за ней считаются уже не относящимися к программе. Кроме того, в этой директиве указывается так называемая точка входа – метка той команды, с которой должно начаться выполнение программы (в общем случае это необязательно первая команда программы). В данном случае это метка START.

Теперь сделаем несколько замечаний общего характера.

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

Может возникнуть вопрос: разрешается ли в сегменте данных размещать команды, а в сегменте команд размещать описания переменных? Да, можно, но лучше это не делать, т. к. будут проблемы с сегментированием. Поэтому рекомендуется в сегменте данных описывать только переменные, а в сегменте команд размещать только команды.

Директива INCLUDE

В общем случае обращение к директиве INCLUDE (включить) имеет такой вид:

INCLUDE <имя файла>

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

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

INCLUDE А: MACROS.TXT

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

В заключение данного раздела приведем пример простой, но полной программы на языке ассемблера. Эта программа вводит 50 символов и выводит их в обратном порядке.

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

INCLUDE IO.ASM ; подключение операций ввода-вывода

S SEGMENT STACK ; сегмент стека

DB 128 DUP(?)

S ENDS

D SEGMENT ; сегмент давних

N EQU 50 ; число символов для ввода

X DB N DUP (?),'$’ ; строка для записи символов

; + конец строки

D ENDS

С SEGMENT;сегмент команд

ASSUME SS: S, DS: D, CS: C

BEG: MOV AX, D

MOV DS, AX

OUTCH '>‘ ; приглашение к вводу

MOV CX, N ; счетчик цикла

MOV SI, N-1 ; индекс (=49, 48 ...)

IN: INCH X [SI] ; ввод символа и запись его

; в конец X

DEC SI

LOOP IN

LEA DX,X ;вывод строки Х

OUTSTR

FINISH

С ENDS

END BEG ; точка входа - команда BEG