Программное обеспечение вычислительных машин и систем. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Программное обеспечение вычислительных машин и систем.



Программное обеспечение вычислительных машин и систем.

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

 

 

Рекомендовано ФГБОУ ВПО «Госуниверситет - УНПК» для

использования в учебном процессе в качестве практикума для высшего

профессионального образования

 

Орел, 2014

УДК

ББК

К

Рецензенты:

кандидат технических наук, доцент кафедры «Информационные

системы» Федерального государственного образовательного

учреждения высшего профессионального образования «Государственный    университет – учебно-научно-производственный комплекс»

А.П. Гордиенко,

 

доктор физико-математических наук, профессор кафедры «Математическое моделирование» Федерального государственного образовательного

учреждения высшего профессионального образования «Тульский                     государственный университет»

В.И. Желтков

 

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ АССЕМБЛЕРА

Организация памяти для хранения программ

Согласно принципам Джона фон Неймана (1903 – 1957, венгро-американский математик), вычислительная машина (ВМ) выполняет вычисления в соответствии с программой, которая располагается в памяти ВМ. Любая программа включает в себя команды (операторы) и данные (операнды). Программа выполняется с целью получения результирующих данных на основе преобразования исходных, с возможным формированием промежуточных данных.

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

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

Начальный адрес (адрес начала сегмента) – это номер (адрес) ячейки памяти, с которой начинается сегмент.

Длина сегмента это количество входящих в него ячеек памяти.

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

На рисунке 1 представлены примеры сегментов, указаны адреса ячеек в памяти, входящих в сегмент, и их смещения (в квадратных скобках) относительно начала сегмента (0100 h).

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

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

0000h
FFFFh
Память
0100h [0000h]
Сегмент
Сегмент
0101h [0001h]
0102h [0002h]
0103h [0003h]
Адрес ячейки в памяти (физический)
Адрес ячейки в сегменте (смещение)

Рис. 1. Размещение сегментов в памяти

Определение данных

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

Метка Мнемоника Операнд,...,Операнд; Комментарий

Метка (символьный адрес) обозначает смещение (номер ячейки в сегменте данных) первого резервируемого байта. К метке в сегменте данных обладает теми же свойствами, что и метка в сегменте кода, но является обязательной.

Мнемоника определяет длину каждого операнда:

1) DB (определить байт). Диапазон для целых чисел без знака: 0...255, для целых чисел со знаком: -128…127.

2) DW (определить слово – два байта). Диапазон для целых чисел без знака: 0...65535, для целых чисел со знаком: -32768…32767.

3) DD (определить двойное слово – четыре байта).

Диапазон для целых чисел без знака: 0...4294967295, для целых чисел со знаком: -2147483648…2147483647.

Операнды показывают инициализируемые данные или объем резервируемого пространства. Выражение может содержать константу или символ? для неопределенного значения.

Примеры:

Data_byte DB 104

Data_word DW 100H,FFH,-5

Data_DW   DD 5*25,0FFFDH,1

Data_str DB ‘H’,’E’,’L’,’L’,’O’

Data_str1 DB ‘HELLO’

При определении большого числа ячеек можно применять оператор повторения

DUP (Операнд,...,Операнд).

Примеры:

Arr DB 30 DUP(1,2) – зарезервирует по адресу Arr 30 однобайтовых ячеек с начальными значениями 1 в нечетных и 2 в четных байтах;

ММ1 DB 15 DUP(14) – означает, что по адресу ММ1 находятся 15 байт, содержащих шестнадцатиричную цифру 0ЕH (14 – в десятичной системе).

Arr_DW DW 4 DUP(?) – определяет по адресу Arr _ DW 4 ячейки, содержащих произвольную информацию.

Операнды могут задаваться в различных системах счисления: для двоичной системы счисления после значения операнда ставится символ B, для шестнадцатеричной – символ H, для десятичной – ничего.

Примеры:

Data_word DW 100H – по адресу Data _ word зарезервировано 2 байта с первоначальным значением 100 в шестнадцатеричной системе счисления (или 256 в десятичной).

Met DB 01010011B – по адресу Met зарезервирована однобайтовая ячейка с первоначальным значением 01010011 в двоичной системе счисления (или 83 в десятичной).

ARG DB 15 – по адресу ARG зарезервирована однобайтовая ячейка с первоначальным значением 15 в десятичной системе счисления.

Далее рассмотрим основные команды языка ассемблера для процессора Intel 8086, необходимые для выполнения заданий лабораторного практикума.

Команды передачи данных

Предназначены для пересылок данных, адресов и непосредственных операндов в регистры или в ячейки памяти. Их описание представлено в табл. 1.2.

Таблица 1.2

Формат команд передачи данных

Название команды Мнемоника и формат команды Описание действия
Переслать MOV DST,SRC (DST)(SRC) Копирование содержимого операнда - источника в операнд - приёмник
Загрузить эффективный адрес LEA DST,SRC (REG) (SRC) Загрузка эффективного адреса (смещения) операнда - источника в регистр процессора
Обменять XCHG OPR1,OPR2 (OPR1) (OPR2) (OPR2) (OPR1) Обмен значениями между операндами

При выполнении указанных выше команд ни один из флажков не изменяется.

Что касается режимов адресации, то операнд - приёмник не может быть непосредственным значением и не может быть сегментным регистром CS.

В команде LEA операнд REG не может быть сегментным регистром, а источник не может иметь непосредственный или регистровый режим.

В команде MOV один из операндов должен быть регистром.

В команде XCHG хотя бы один из операндов должен быть регистром, но ни один из операндов не может быть сегментным регистром.

 

Примеры:

mov ah,8; Переслать в регистр ah десятичное значение 8

mov bx,mm; Переслать в регистр bx значение по адресу mm

         ; в сегменте данных

mov ah,al; Переслать в регистр ah содержимое регистра al

mov mm,bx; Переслать в ячейку по адресу mm в сегменте данных

         ; содержимое регистра bx

lea bp,mm; Загрузить смещение ячейки mm в регистр bp

xchg bx,mm; Обменять содержимое регистра bx со значением

          ; по адресу mm в сегменте данных

xchg ch,ah; Обменять содержимое регистров ch и ah

xchg mm,ax; Обменять значение по адресу mm в сегменте

          ; данных с содержимым регистра ax

Логические команды

Логические команды выполняют логические операции над битами операндов. Размерность операндов должна быть одинакова.

Логические команды наиболее часто используются для селективных (выборочных) установок, инвертирования, сброса или проверки битов в операнде - получателе в соответствии с двоичным значением операнда - источника. Такие действия часто встречаются в операциях над битами регистров и данных ввода-вывода. При этом операнд - источник называют маской, а сама операция называется маскированием. Описание логических команд представлено в табл. 1.8.

Команда логического сравнения TEST выполняет операцию логического умножения над операндами; при этом сами операнды не изменяются. Результат операции формируется во временной памяти: бит результата равен 1, если соответствующие биты операндов равны 1, в противном случае бит результата равен 0.

Команда NOT не воздействует на флажки. Остальные команды сбрасывают OF и CF, оставляют АF не определенным и устанавливают СF, Z F, PF по обычным правилам.

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

Таблица 1.8

Формат логических команд

Название команды Мнемоника и формат команды Описание действия
Инвертировать NOT OPR (OPR)not OPR Инвертирование всех битов операнда
Объединить по «ИЛИ» OR DST,SRC (DST)(DST) or (SRC) Выполнение операции логического «ИЛИ» над соответствующими парами битов источника и приёмника
Объединить по «И» AND DST,SRC (DST)(DST) and (SRC) Выполнение операции логического «И» над соответствующими парами битов источника и приёмника
Сложить по MOD2 («исключающее ИЛИ») XOR DST,SRC (DST)(DST) xor (SRC) Выполнение операции «Исключающее ИЛИ» над соответствующими парами битов источника и приёмника
Проверить TEST OPR1,OPR2 OPR1 and OPR2 Выполнение операции логического сравнения над соответствующими парами битов двух операндов

Примеры:

mov al,01001101b; Переслать в регистр al двоичное

;значение 01001101 b

not al; Инвертировать биты содержимое регистра al

or al, 10000001b; Установить нулевой и седьмой биты

; в регистре al в 1

and al, 11110101b; Сбросить первый и третий биты в содержимом

; регистра al

test al, 00000001b; Проверить, является ли содержимое регистра al

                        ; нечётным числом (нулевой бит должен быть 1)

jnz m1; Выполнить переход на команду с адресом m 1, если нулевой

     ; бит содержимого регистра al равен 1 (число нечётное)

     ; В этом случае результат команды test равен 1 и значение

     ; флага zf равно 0

Команды передачи управления

Данные команды нарушают естественный порядок выполнения команд программы.

Изменение потока управления происходит при наличии команд переходов (условного и безусловного), а также вызова процедур.

1. Команды перехода. При выполнении команд перехода в счётчик команд IP принудительно записывается новое значение – новый адрес в памяти, начиная с которого будут выполняться команды.

1.1. Команда безусловного перехода обеспечивает переход по заданному адресу без проверки каких-либо условий.

Формат команды:

JMP Модификатор адрес_перехода

Этой командой задаются внутрисегментные и межсегментные переходы.

Модификатор указывает вид перехода (внутрисегментный прямой, внутрисегментный косвенный, межсегментный прямой, межсегментный косвенный) и принцип изменения содержимого регистров CS и IP. Модификатор не всегда указывается в команде JMP.

Описание команды безусловного перехода для внутрисегментных переходов представлено в табл. 1.10.

Таблица 1.10

Формат команды для внутрисегментных переходов

Вариант внутрисегментного перехода Мнемоника и формат команды Описание действия
Прямой короткий переход (расстояние от команды JMP до адреса перехода не превышает -128 или 127 байт) JMP OPR JMP SHORT PTR OPR (IP) (IP) + 8- битное смещение, определяемое OPR
Прямой переход (на расстояние от 128 байт до 64 Кбайт) JMP OPR JMP NEAR PTR OPR (IP) (IP) + 16- битное смещение, определяемое OPR
Косвенный переход (в команде указывается не сам адрес перехода, а место, где он находится) JMP OPR JMP WORD PTR OPR (IP) (EA), где EA- эффективный адрес перехода, определяемый OPR

 

 

Прямой короткий внутрисегментный переход применятся, когда расстояние от команды JMP до адреса перехода находится в диапазоне от -128 байт (адрес перехода расположен до команды JMP в программе) до +127 байт (адрес перехода расположен после команды JMP в программе). В последнем случае для указания короткого перехода в команде JMP используется модификатор SHORT PTR. При выполнении короткого перехода длина команды безусловного перехода составляет два байта.

Прямой внутрисегментный переход отличается от предыдущего варианта перехода тем, что расстояние между адресом перехода и командой JMP находится в диапазоне от 128 байт до 64 Кбайт, т.е. переходы между командами могут осуществляться в пределах всего сегмента кода. Для уточнения вида перехода может использоваться модификатор NEAR PTR. При выполнении внутрисегментного прямого перехода длина команды безусловного перехода составляет три байта.

В команде косвенного внутрисегментного перехода указывается не сам адрес перехода, а его местоположение, т.е. смещение (эффективный адрес) в сегменте данных. Если адрес ячейки памяти, где хранится адрес перехода, задаётся транслятору через регистр (с помощью команды LEA), то в команде перехода необходимо использовать модификатор WORD PTR для дополнительного сообщения о том, что переход является внутрисегментным.

Примеры:

jmp short ptr met; Выполняется переход на команду с адресом met

…        ; (расстояние до 127 байт)

met: inc ah; Увеличение содержимого регистра ah на 1

met: inc ah; Увеличение содержимого регистра ah на 1

jmp met; Выполняется переход на команду с адресом met

… ; (расстояние до -128 байт)

jmp met; Выполняется переход на команду с адресом met

…    ; (расстояние от 128 байт до 64 Кбайт)

met: inc ah; Увеличение содержимого регистра ah на 1

d_s segment

 a dw met; По адресу a указан адрес команды,

               ; на которую должен быть выполнен переход

d_s ends

c_s segment

assume ds:d_s, cs:c_s

begin:

mov ax, d_s

mov ds, ax

jmp a; Выполняется ближний переход на команду, адрес которой

  ; указан по смещению a в сегменте данных

met: inc ah; Увеличение содержимого регистра ah на 1

c_s ens

end begin

d_s segment

 a dw met; По адресу a указан адрес команды,

               ; на которую должен быть выполнен переход

d_s ends

c_s segment

assume cs:c_s

begin:

mov ax, d_s

mov ds, ax

lea bp,a; Загружается адрес ячейки a в регистр bp

jmp word ptr [bp]; Выполняется ближний косвенный переход

               ; на команду, адрес которой указан в регистре bp

met: inc ah; Увеличение содержимого регистра ah на 1

c_s ens

end begin

Формат команды безусловного перехода для межсегментных переходов описан в табл. 1.11.

При выполнении прямого межсегментного перехода в команде указывается адрес перехода длиной четыре байта, из которых два старшие байта – начальный адрес нового сегмента кода, а младшие два байта – адрес команды (смещение) в этом сегмента кода. Команда прямого межсегментного перехода имеет длину пять байтов. Использование модификатора FAR PTR обязательно.

Таблица 1.11

Формат команды для межсегментных переходов

Вариант межсегментного перехода Мнемоника и  формат команды Описание действия
Прямой переход JMP FAR PTR OPR (CS) начальный адрес сегмента, определяемого OPR (IP) смещение в сегменте из OPR
Косвенный переход JMP FAR PTR OPR JMP DWORD PTR OPR (IP) (EA), где EA – эффективный адрес, определяемый OPR (CS) (EA + 2), где EA – эффективный адрес из OPR

В команде косвенного межсегментного перехода в качестве операнда указывается адрес области памяти, в которой содержатся смещение в новом сегменте и начальный адрес нового всего (всего четыре байта). Если адрес области памяти в команде перехода указан прямо, то используется модификатор FAR PTR, если адрес области памяти указан косвенно, через регистр, то используется модификатор DWORD PTR.

Пример:

d_s segment

a dd c_s1:m1; По адресу a указан адрес перехода в сегмент c _ s 1

               ; по смещению m 1

d_s ends

c_s1 segment

assume cs:c_s1

m1: mov ah,4

add ah,7

jmp c_s:m2

c_s1 ends

c_s segment

assume ds:d_s, cs:c_s

begin:

lea bp, a; В регистр bp загружается адрес ячейки a

jmp dword ptr [bp]; Выполняется безусловный межсегментный

 ; переход по адресу, указанному в регистре bp

m2: mov ah,4ch

int 21h

c_s ends

end begin

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

Не модифицируются все флажки условий.

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

1.2. Ветвление (условный переход) происходит только при соблюдении определённого условия, в противном случае выполняется следующая по порядку команда программы. Условием, на основании которого осуществляется переход, чаще всего выступают признаки результата выполнения предшествующей арифметической или логической команды (без флага AF). Каждый из признаков фиксируется в своём разряде регистра флагов PSW. Возможен и такой подход, когда решение о переходе принимается в зависимости от состояния одного из регистров общего назначения, куда предварительно помещается результат операции сравнения CMP.

Формат команды CMP представлен в табл. 1.12.

Таблица 1.12

Формат команды сравнения

Название команды Мнемоника и формат команды Описание действия
Сравнить CMP OPR1,OPR2 (OPR1) - (OPR2) Выполняется сравнение путём вычитания операндов, при этом сами операнды не изменяются

Команда CMP устанавливает статусные флаги в зависимости от результата сравнения операндов, не меняя самих операндов.

Команды условного перехода позволяют выполнять только короткие переходы (внутрисегментные прямые переходы в диапазоне от -128 байт до +127 байт).

Команды условного перехода и их формат представлены в табл. 1.13.

Многие команды условного перехода, представленные в табл. 1.13, эквивалентны, так как в них анализируются одинаковые флаги.

 

Таблица 1.13

Формат команд условного перехода

Название команды Мнемоника и формат команды Критерий условного перехода (в CMP) Значение флагов для перехода
1 2 3 4
Перейти, если равно JE OPR OPR1 = OPR2 ZF = 1
Перейти, если не равно JNE OPR OPR1 <> OPR2 ZF = 0
Перейти, если ниже (меньше)/ не выше или равно (без знака) JB/ JNAE OPR OPR1 < OPR2 CF = 1
Перейти, если не ниже (меньше)/ выше или равно (без знака) JNB/ JAE OPR OPR1 >= OPR 2 CF = 0
Перейти, если ниже или равно/ не выше (без знака) JBE/ JNA OPR OPR1 <= OPR2 CF = 1 или ZF = 1
Перейти, если не ниже или равно/ выше (больше) (без знака) JNBE/ JA OPR OPR1 > OPR2 CF = 0 и ZF = 0
Перейти, если меньше/ не больше (со знаком) JL/ JNGE OPR OPR1 < OPR2 SF <> OF
Перейти, если не меньше/ больше или равно (со знаком) JNL/ JGE OPR OPR1 => OPR2 SF = OF
Перейти, если меньше или равно/ не больше (со знаком) JLE/ JNG OPR OPR1 <= OPR2 SF <> OF или ZF = 1
Перейти, если не меньше или равно/ больше (со знаком) JNLE/ JG OPR OPR1 > OPR2 SF = OF и ZF = 0  
Перейти, если ноль JZ OPR [OPR1 = OPR2] ZF = 1
Перейти, если не ноль JNZ OPR [OPR1 <> OPR2] ZF = 0
Перейти, если знак установлен JS OPR [OPR1 < OPR2] SF = 1
Перейти, если знак сброшен JNS OPR [OPR1 > OPR2] SF = 0
Перейти, если есть переполнение JO OPR - OF = 1
Перейти, если нет переполнения JNO OPR - OF = 0

 

Продолжение таблицы 1.13

1 2 3 4
Перейти, если паритет установлен JP OPR - PF = 1
Перейти, если паритет сброшен JNP OPR - PF = 0
Перейти, если перенос установлен JC OPR - CF = 1
Перейти, если перенос сброшен JNC - CF = 0
       

Рассмотрим примеры использования команд условного перехода:

… sub ah,al jz m1 add ah,3 jmp m2 m1: add al,2 m2: mov ah,4 … … cmp ah,al je m1 add ah,3 jmp m2 m1: add al,2 m2: mov ah,4 …

Левый фрагмент иллюстрирует проверку содержимого регистров ah и al на равенство. При этом используются флаги, в частности, флаг нуля ZF. Предварительно выполняется вычитание содержимого регистров: если их значения равны, то в результате образуется ноль и изменяется значение флага ZF. Команда jz проверяет условие: если флаг ZF равен 1, то выполняется переход на команду с адресом m 1, иначе выполняется команда сложения, следующая за командой условного перехода. Команда с адресом m 2 выполняется в любом случае. Правый фрагмент выполняет ту же проверку, но с использованием команды сравнения cmp и команды перехода по равенству содержимого регистров ah и al (je m 1).

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

Формат этих команд представлен в таблице 1.14.

Команда LOOP и её расширения позволяет организовывать циклы, подобные циклам for в языках высокого уровня с автоматическим уменьшением счётчика цикла. Количество повторений содержится в регистре CX, который в командах управления циклами выполняет функции счётчика цикла.

Таблица 1.14

Формат команд циклов

Название команды Мнемоника и формат команды Проверяемое условие
Зациклить LOOP OPR CX <> 0
Зациклить, пока ноль или равно LOOPZ/ LOOPE OPR CX <> 0 или ZF = 0
Зациклить, пока не ноль или не равно LOOPNZ/ LOOPNE OPR CX <> 0 или ZF = 1
Переход по CX JCXZ OPR CX = 0

Команды LOOPZ / LOOPE и LOOPNZ / LOOPNE по принципу своей работы являются взаимообратными. Они расширяют действие команды LOOP тем, что дополнительно анализируют флаг нуля ZF. Это даёт возможность организовывать досрочный выход из цикла по значению флага ZF. Обычно эти команды применяются в операциях поиска определённого значения в последовательности или при сравнении двух чисел.

Команда JCXZ по своему формату идентична командам условного перехода. Она выполняет проверку регистра CX и осуществляет переход на команду с указанным адресом, если содержимое регистра CX равно 0. Но, как и в командах цикла, здесь регистр CX тоже играет роль счётчика цикла.

За исключением команды JCXZ, которая не изменяет счётчик цикла CX, остальные команды управления циклами уменьшают текущее содержимое счётчика CX на 1. Затем, если проверяемое условие удовлетворяется, то выполняется переход по адресу, указанному в команде цикла. В противном случае осуществляется переход на команду, следующую после LOOP или её расширений, т.е. выполняется выход из цикла.

Операнд OPR должен быть меткой, которая находится в диапазоне от -128 до127 байт от команды, следующей за командой цикла. Это означает, что команды цикла позволяют выполнять короткие внутрисегментные переходы. Для работы с длинными циклами следует использовать команды условного перехода и команду JMP.

Часто возникает необходимость в организации вложенных циклов. Основная проблема, которая при этом возникает – как сохранить значения счётчиков CX для каждого цикла. Для временного хранения значений счётчиков внешнего и внутреннего циклов можно использовать другие регистры процессора, ячейки памяти или стек.

Примеры:

m1: cmp ah,al; Сравнение содержимого регистров ah и al

jg m2; Если содержимое ah больше значения в al,

  ; то выполняется переход на метку m2 (выход из цикла)

add ah,3; иначе, увеличивается содержимое регистра ah на 3

jmp m1; Выполняется переход на метку m2 (начало цикла)

m2: mov ah,4

mov cx,20; Количество повторений внешнего цикла

с1: push cx; Помещение счётчика внешнего цикла в стек

…           ; Команды внешнего цикла

mov cx,10; Количество повторений внутреннего цикла

c2:

…           ; Команды внутреннего цикла

loop c2; Конец внутреннего цикла

… ; Команды внешнего цикла

pop cx; Восстановление из стека счётчика внешнего цикла

loop c1; Конец внешнего цикла

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

Во втором примере описаны вложенные циклы. Для временного хранения счётчика внешнего цикла используется стек. Перед запуском внутреннего цикла текущее содержимое регистра cx запоминается в стеке. Затем в cx помещается значение счётчика внутреннего цикла, и выполняются его команды. По окончании внутреннего цикла выполняются команды внешнего цикла. После этого из стека извлекается значение счётчика внешнего цикла для последующей проверки условий внешнего цикла.

3. Процедуры. Процедура (подпрограмма) – это группа команд для решения конкретной подзадачи, обладающая средствами получения управления из точки вызова задачи более высокого уровня и возврата управления в эту точку.

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

Общее описание процедуры представлено на рисунке 10.

Процедура ограничивается операторами PROC и ENDP, перед которыми указывается имя процедуры. После PROC указывается тип процедуры: процедура ближнего вызова (директива NEAR) или процедура дальнего вызова (директива FAR). В первом случае процедура располагается в том же сегменте кода, что и основная программа, и при вызове такой процедуры выполняется внутрисегментный переход. Во втором случае процедура располагается в другом сегменте кода, и при её вызове выполняется межсегментный переход.

Имя процедуры
PROC
NEAR или FAR
Тело процедуры (команды и директивы языка ассемблера)
RET
Имя процедуры
ENDP

Рис. 10. Общее описание процедуры

Между этими операторами располагается тело процедуры, состоящее из команд и директив языка ассемблера. Последней командой процедуры является команда RET, по которой осуществляется возврат из данной процедуры в вызвавшую её программу или другую процедуру на команду, следующую за командой последнего вызова процедуры.

Вызов процедуры осуществляется командой CALL, за которой следует имя процедуры. Формат команды CALL:

CALL Модификатор имя_процедуры

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

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

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

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

1. В начале программы (до первой исполняемой команды).

c_s segment

assume cs:c_s

Pr1 proc near

Ret

P1 endp

begin:      ;начало программы

end begin

2. В конце программы (после команды корректного завершения работы и возвращения управления операционной системе – ОС).

c_s segment

assume cs:c_s

begin:

mov ah, 4ch

int 21h;корректное завершение работы и передача управления ОС

P1 proc near

Ret

P1 endp

c_s ends

end begin

3. Внутри тела программы или другой процедуры (должен быть предусмотрен обход процедуры с помощью оператора JMP).

c_s segment

assume cs:c_s

begin:

jmp m1

P1 proc near

Ret

P1 endp

m1: …

mov ah, 4ch

int 21h;корректное завершение работы и передача управления ОС

c_s ends

end begin

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

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

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

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

Взаимодействие вызывающей и вызываемой процедур иллюстрирует рисунке 11.

Вызывающая процедура A
Вызываемая  процедура B
Процедура A вызывается из основной программы
Процедура A возвращается в основную программу

Рис. 11. Взаимодействие вызывающей и вызываемой процедур

Рассмотрим в качестве примера программу, использующую вызов процедуры.

s_s segment stack "stack"

dw 12 dup(?)

s_s ends

d_s segment

 aa dw 10

d_s ends

c_s segment

 assume ss:s_s,ds:d_s,cs:c_s

begin:

 mov ax,d_s

 mov ds,ax

  call pr1;вызов подпрограммы

  mov ah,4ch

 int 21h

pr 1 proc near   ;начало подпрограммы (ближний вызов)

 push ax        ;записать в стек содержимое регистра AX

 mov ax, aa

 pop ax          ;выбрать из стека содержимое регистра AX

  ret                      ;команда возврата на следующую команду после

                      ;вызова процедуры

pr 1 endp        ;конец подпрограммы

c_s ends

end begin

Поскольку процедура расположена в том же сегменте кода, что и основная программа и описана как процедура ближнего вызова (директива NEAR), то переход будет внутрисегментным

При выполнении вызова процедуры pr 1 (команда call pr 1) в стек помещается адрес возврата – значение счётчика команд IP, содержащего на данный момент адрес команды, которая должна будет выполняться после текущей (mov ah,4 ch). Значение регистра IP замещается новым значением – адресом первой команды процедуры. При достижении команды возврата из процедуры (ret) из стека в регистр IP записывается старое значение, что обеспечивает возврат в основную программу на команду, которая непосредственно следует за командой вызова процедуры.

Стековые команды

Организация стека. Стек – это однонаправленная очередь, данные в которую помещаются и извлекаются в строго определённом порядке. Стековая память обеспечивает такой режим работы, когда информация записывается и считывается по принципу «последним записан – первым считан» (LIFO – Last Input First Output). Такая память используется для временного хранения данных, например, запоминания и восстановления регистров процессора (контекста) при обработке подпрограмм и прерываний. Работу стековой памяти поясняет рисунок 12.

 
A
 
A
B
A
B
C
D
E
F
H
A
B
C
D
E
F
H
 
A
B
C
D
E
F
H
 
A
Занесение в стек
Извлечение из стека

Рис.12. Логика работы стековой памяти

Когда слово A заносится в стек, то располагается в первой свободной ячейке. Каждое следующее записываемое слово перемещает всё содержимое стека на одну ячейку вверх и занимает освободившуюся ячейку. Запись очередного слова после H приводит к переполнению стек



Поделиться:


Последнее изменение этой страницы: 2020-12-09; просмотров: 110; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.191.174.168 (0.205 с.)