- •1. Внутренние регистры
- •1.1. Регистры данных
- •1.2. Регистры сегментов
- •1.3. Регистры указателей и индексов
- •1.4. Регистр командного указателя
- •1.5. Флаговый регистр
- •2. Структура и процесс разработки программы на языке ассемблера
- •2.1. Команды
- •2.2. Псевдооператоры
- •2.2.1. Псевдооператор segment
- •2.2.2. Псевдооператор proc
- •2.2.3. Псевдооператор assume
- •2.2.4. Псевдооператор end
- •2.2.5. Псевдооператоры определения данных
- •2.3. Инициализация программы
- •3. Команды пересылки данных
- •3.1. Команда mov
- •3.2. Команды push и рор
- •4. Режимы адресации
- •4.1. Регистровая и непосредственная адресация
- •4.2. Исполнительный адрес
- •4.3. Прямая адресация
- •4.4. Косвенная регистровая адресация
- •4.5. Адресация по базе
- •4.6. Прямая адресация с индексированием
- •4.7. Адресация по базе с индексированием
- •5. Команды передачи управления
- •5.1. Команды call и ret
- •5.2. Команда безусловного перехода jmp
- •5.3. Команды условной передачи управления
- •5.4. Команды управления циклами
- •6. Команды обработки строк
- •6.1. Команды пересылки строк movs, movsb, movsw
- •6.2. Префиксы повторения
- •6.3. Команды сравнения строк. Команда cmps.
- •6.4. Команды сканирования строк
- •6.5. Команды загрузки и сохранения строки
- •7. Взаимодействие языков Си и Ассемблера
- •7.1. Внутренняя структура программы на языке Си для ibm pc
- •7.2. Использование функций на языке Ассемблера
- •7.2.1. Основы взаимодействия языков Си и Ассемблера
- •7.2.2. Передача управления в подпрограмму и обратно
- •7.2.3. Использование глобальных данных
- •7.2.4. Использование аргументов функции
- •7.2.5. Возвращение значения через имя подпрограммы
- •7.2.6. Использование аргументов
- •7.3. Вызов функций на языке Си из программ на языке Ассемблера
- •7.4. Использование локальных данных
- •8. Арифметические команды
- •8.1. Форматы хранения десятичных чисел
- •8.2. Команды сложения
- •8.3. Коррекция результата сложения для bcd-форматов
- •8.4. Команда приращения значения приемника на единицу
- •8.5. Команды вычитания
- •8.6. Коррекция результата вычитания для bcd-форматов
- •8.7. Команда уменьшения содержимого приемника на единицу
- •8.8. Команда обращения знака
- •8.9. Команды умножения
- •8.10. Коррекция результатов умножения
- •8.11. Команды деления
- •8.12. Коррекция результатов деления
- •8.13. Команды расширения знака
- •9. Команды манипулирования битами
- •9.1. Логические команды and, or и xor
- •9.2. Команда логического отрицания nот
- •9.3. Команда проверки test
- •9.4. Команды сдвига и циклического сдвига
- •9.4.1. Команды сдвига
- •9.4.2. Команды циклического сдвига
- •10. Команды работы с флагами
- •10.1. Команды управления флагами
- •10.2. Команды пересылки флагов
- •11. Псевдооператоры определения идентификаторов и операции
- •11.1. Псевдооператоры определения идентификаторов
- •11.2. Операции
- •11.2.1. Арифметические операции
- •11.2.2. Логические операции
- •11.2.3. Операции отношения
- •11.2.4. Операции, возвращающие значения
- •11.2.5. Операции присваивания атрибутов
- •12. Условные псевдооператоры
- •13. Макроопределения
- •13.1. Сравнение макроопределений и процедур
- •13.2. Состав макроопределений
- •13.3. Псевдооператоры макроассемблера
- •13.3.1. Псевдооператор local
- •13.3.2. Псевдооператоры повторения
- •13.3.3. Условные псевдооператоры
- •13.3.4. Псевдооператор eхiтм
- •13.4. Операции в макроопределениях
- •13.5. Задание макроопределений в исходных программах
- •13.5.1. Использование библиотеки макроопределений
- •13.5.2. Указания для задания макроопределений
- •13.5.3. Считывание библиотеки макроопределений в программу
- •13.5.4. Удаление макроопределений
7.2.3. Использование глобальных данных
Подпрограмма на языке ассемблера может иметь доступ к глобальным данным, которые определены в модуле на языке Си с использованием внешнего класса хранения данных. Напомним, что внешними объектами в программе на языке Си являются те, которые определены вне любой функции и не объявлены ключевым словом static.
Чтобы обеспечить подпрограмме на языке ассемблера доступ к внешним объектам, в ней надо определить сегмент данных и использовать для ссылки на внешние объекты псевдооператор EXTRN.
Приведем пример, в котором подпрограмма изменяет значение внешнего объекта VAL.
extern “C” void f2(); /* Объявить внешнюю подпрограмму на языке
ассемблера */
int val; /* Определить внешний объект данных */
main ( )
{
f2 ( );
printf ("val = %d\n", val);
}
DGROUP GROUP DATA
DATA SEGMENT WORD PUBLIC 'DATA'
ASSUME DS: DGROUP
EXTRN _VAL: WORD
DATA ENDS
PGROUP GROUP PROG ; Сегмент команд
PROG SEGMENT BYTE PUBLIC 'PROG'
ASSUME CS: PGROUP
PUBLIC _F2
_F2 PROC NEAR
MOV _VAL, 4
RET
_F2 ENDP
PROG ENDS
END
7.2.4. Использование аргументов функции
Передача данных может осуществляться через аргументы. Напомним, что в языке Си аргументы передаются по значению, т.е. вызываемая подпрограмма или функция получает через стек копию каждого аргумента.
Пусть а1, а2 и аЗ – целые значения.
Тогда вызов
f3(a1, а2, аЗ);
будет преобразован компилятором языка Си в последовательность команд микропроцессора:
PUSH АЗ
PUSH А2
PUSH А1
CALL _F3
Заметим, что аргументы помещаются в стек в порядке, обратном тому, в котором они указаны при вызове функции.
Обратим внимание на то, что ячейка с адресом возврата имеет адрес [SP]. (Квадратные скобки означают "содержимое SP".) Копии аргументов имеют адреса [SP] + 2, [SP] + 4 и [SP] + 6.
Микропроцессор 8088 не позволяет использовать регистр указателя стека SP в команде MOV для извлечения значений, находящихся по этим адресам. Поэтому следует воспользоваться регистром ВР (регистр указателя базы).
Одной из первых команд любой подпрограммы, которой требуется адресоваться к аргументам, является помещение в регистр ВР значения регистра SP. Однако по соглашению нельзя уничтожать содержимое регистра ВР в подпрограмме. Поэтому следует сначала сохранить содержимое регистра ВР, а затем воспользоваться регистром ВР в подпрограмме.
Непосредственно перед возвратом в вызывающую функцию следует восстановить исходное значение ВР. Обычно старое содержимое регистра ВР сохраняют в стеке.
Таким образом, общепринятые заголовок и заключительная часть подпрограммы имеют следующий вид:
_F3 РRОС NEAR
PUSH ВР ; Сохранить значение ВР при вызове
MOV BP, SP ; Установить новое значение ВР
...........................
POP ВР ; Восстановить исходное значение ВР
RET ; Вернуться в вызывающую функцию
_F3 ENDP
В этом случае на время исполнения подпрограммы стек будет содержать не только аргументы и адрес возврата, но еще и сохраненное значение регистра ВР.
Используем тот же вызов функции, что и в предыдущем примере:
f3 (a1, а2. аЗ):
Адреса аргументов можно задать следующим образом:
[ВР]+4 ; Адрес первого аргумента
[ВР]+6 ; Адрес второго аргумента
[ВР]+8 ; Адрес третьего аргумента
Следующим примером является подпрограмма, которая получает три целых аргумента, суммирует их, а затем возвращает полученный результат через внешнюю переменную VAL.
extern “C” void sum();
int val;
main ()
{
int x = 10;
sum(x, 20, 20+5);
printf( "val = %d\n", val );
}
..............................
_SUM PROC NEAR
PUSH BP ; Сохранить значение BP при вызове
MOV BP, SP ; Установить новое значение BP
MOV АХ, [ВР]+4 ; Первый аргумент находится в стеке
; над сохраненными значениями BP и
; адреса возврата
ADD АХ, [ВР]+6 ; Добавить значение второго аргумента ADD АХ, [BP]+8 ; Добавить значение третьего аргумента MOV _VAL, AX ; Поместить результат в VAL
POP BP ; Восстановить исходное значение BP
RET
_SUM ENDP
В подпрограмме на языке ассемблера первый аргумент загружается в регистр АХ, а значения остальных аргументов добавляются к этому регистру с помощью команды сложения ADD. Затем результат (находящийся в регистре АХ) помещается во внешний объект VAL.
В языке Си можно передавать объекты длиной в одно слово, символы, длинные целые значения, значения с плавающей точкой одинарной и двойной точности, а также (если это обеспечивается компилятором) целые структуры. В этом случае размер части стека, занимаемой каждым аргументом, зависит от его размера. Однако при передаче аргументов компиляторы языка Си выполняют некоторые упрощения:
1) объекты типов char, short и int занимают в стеке по одному слову;
2) объекты типа long занимают по два слова;
3) объекты типов float и double передаются в формате объектов типа double и требуют в стеке по четыре слова;
4) если компилятор обеспечивает передачу структур по значению, то в стеке требуется столько байтов, чтобы в них поместились все элементы структуры. При этом обычно число выделяемых байтов округляется до целого, кратного размеру слова;
5) для указателя требуется одно или два слова в зависимости от используемой модели распределения памяти.
Смещение адреса каждого аргумента от значения регистра BP определяется в результате как сумма размеров всех предшествующих объектов, помещенных в стек. Таким образом, программист должен знать тип каждого аргумента, передаваемого подпрограмме, а также порядок, в котором они помещаются в стек.
Наряду с возвращением значений через глобальные объекты подпрограмма на языке ассемблера может возвращать их через аргументы вызова или как значение подпрограммы. Рассмотрим соглашения о реализации этих способов возвращения значений.
