Каждая программа имеет область данных, где размещаются глобальные переменные. Почему же локальные данные хранятся именно в стеке? Это делается для уменьшения объёма памяти занимаемого программой. Если программа будет последовательно вызывать несколько процедур, то в каждый момент времени будет отведено место только под данные одной процедуры, т. к. стек занимается и освобождается. Область данных существует всё время работы программы. Если бы локальные данные размещались в области данных, пришлось бы отводить место под локальные данные для всех процедур программы.
Локальные данные автоматически не инициализируются. Если в вышеприведённом примере функция f2 после функции f3 вызовет функцию f4, то функция f4 займёт в стеке место, которое до этого было занято функцией f3, таким образом, функции f4 «в наследство» достанутся данные функции f3. Поэтому каждая процедура обязательно должна заботиться об инициализации своих локальных данных.
Лекция №3. Структура программы на ассемблере
Все процессы в машине на самом низком, аппаратном уровне приводятся в действие только командами (инструкциями) машинного языка. Язык ассемблера – это символическое представление машинного языка. Ассемблер позволяет писать короткие и быстрые программы. Однако этот процесс чрезвычайно трудоёмкий. Для написания максимально эффективной программы необходимо хорошее знание особенностей команд языка ассемблера, внимание и аккуратность. Поэтому реально на языке ассемблера пишутся в основном программы, которые должны обеспечить эффективную работу с аппаратной частью. Также на языке ассемблера пишутся критичные по времени выполнения или расходованию памяти участки программы. Впоследствии они оформляются в виде подпрограмм и совмещаются с кодом на языке высокого уровня.
2.1. Идентификаторы
Понятие идентификатора в языке ассемблера ничем не отличается от понятия идентификатора в других языках. Можно использовать латинские буквы, цифры и знаки _ . ? @ $, причём точка может быть только первым символом идентификатора. Большие и маленькие буквы считаются эквивалентными.
2.2. Целые числа
В программе на языке ассемблера целые числа могут быть записаны в двоичной, восьмеричной, десятичной и шестнадцатеричной системах счисления. Для задания системы счисления в конце числа ставится буква b, o/q, d или h соответственно. Шестнадцатеричные числа, которые начинаются с «буквенной» цифры, должны предваряться нулём, иначе компилятор не сможет отличить число от идентификатора. Примеры чисел см. в разделе 2.6.
2.3. Символьные данные
Символы и строки в языке ассемблера могут заключаться в апострофы или двойные кавычки. Если в качестве символа или внутри строки надо указать апостроф или кавычку, то делается это следующим образом: если символ или строка заключены в апострофы, то апостроф надо удваивать, а кавычку удваивать не надо, и наоборот, если символ или строка заключены в двойные кавычки, то надо удваивать кавычку и не надо удваивать апостроф. Все следующие примеры корректны и эквивалентны: 'don''t', 'don"t', "don't", "don""t".
2.4. Комментарии
Комментарии в языке ассемблера начинаются с символа «точка с запятой» и могут начинаться как в начале строки, так и после команды.
2.5. Директива эквивалентности
Директива эквивалентности позволяет описывать константы:
<имя> EQU <операнд>
Все вхождения имени заменяются операндом. Операндом может быть константное выражение, строка, другое имя.
2.6. Директивы определения данных
Языки высокого уровня обычно являются типизированными. Каждая переменная имеет тип, который накладывает ограничения на операции над переменной и на использование в одном выражении переменных разных типов. Кроме того, языки высокого уровня позволяют работать со сложными типами, таким как указатели, записи/структуры, классы, массивы, строки, множества и т. п.
Язык Паскаль имеет достаточно жёсткую структуру типов. Присваивания между переменными разных типов минимальны, над указателями определены только операции присваивания, взятия значения и получение адреса. Поддерживается много сложных типов.
Язык С, который создавался как высокоуровневая замена языку ассемблера, имеет гораздо менее жёсткую структуру типов. Все целочисленные типы совместимы, тип char, конечно, хранит символы, но также сопоставим с целыми типами, логический тип отсутствует в принципе (для языка С это именно так!), над указателями определены операции сложения и вычитания. Сложные типы, такие как массивы, строки и множества, не поддерживаются.
Что касается языка ассемблера, то тут вообще вряд ли можно говорить о какой-либо структуре типов. Команды языка ассемблера оперируют объектами, существующими в оперативной памяти, т. е. байтом и его производными (слово, двойное слово и т. д.). Символьный, логический тип? Какая глупость! Указатели? Вот тебе 4 байта и делай с ними, что хочешь. В итоге, конечно, и можно сделать, что хочешь, только предварительно стоит хорошо подумать, что из этого получится.
Соответственно, в языке ассемблера существует 5 (!) директив для определения данных:
DB (define byte) – определяет переменную размером в 1 байт;
DW (define word) – определяет переменную размеров в 2 байта (слово);
DD (define double word) – определяет переменную размером в 4 байта (двойное слово);
DQ (define quad word) – определяет переменную размером в 8 байт (учетверённое слово);
DT (define ten bytes) – определяет переменную размером в 10 байт.
Все директивы могут быть использованы как для объявления простых переменных, так и для объявления массивов. Хотя для определения строк, в принципе, можно использовать любую директиву, в связи с особенностями хранения данных в оперативной памяти лучше использовать директиву DB.
Синтаксис директив определения данных следующий:
<имя> DB <операнд> [, <операнд>]
<имя> DW <операнд> [, <операнд>]
<имя> DD <операнд> [, <операнд>]
<имя> DQ <операнд> [, <операнд>]
<имя> DT <операнд> [, <операнд>]
Операнд задаёт начальное значение переменной. В качестве операнда может использоваться число, символ или знак вопроса, с помощью которого определяются неинициализированные переменные.
Если в качестве операнда указывается строка или если указано несколько операндов через запятую, то память отводится под несколько переменных указанного типа, т. е. получается массив. При этом именованным оказывается только первый элемент, а доступ к остальным элементам массива осуществляется с помощью выражения <имя> + <смещение>.
Для того чтобы не указывать несколько раз одно и то же значение, при инициализации массивов можно
системе счисления
h dd 0f1ah ; использовать конструкцию повторения DUP.
a db 10011001b ; Определяем переменную размером 1 байт с начальным значением, заданным в двоичной системе счисления
b db '!' ; Определяем переменную в 1 байт, инициализируемую символом '!'
d db 'string',13,10 ; Определяем массив из 8 байт
e db 'string',0 ; Определяем строку из 7 байт, заканчивающую нулём
f dw 1235o ; Определяем переменную размером 2 байта с начальным значением, заданным в восьмеричной системе счисления
g dd -345d ; Определяем переменную размером 4 байта с начальным значением, заданным в десятичной Определяем переменную размером 4 байта с начальным значением, заданным в шестнадцатеричной системе счисления
i dd? ; Определяем неинициализированную переменную размером 4 байта
j dd 100 dup (0) ; Определяем массив из 100 двойных слов, инициализированных 0
k dq 10 dup (0, 1, 2) ; Определяем массив из 30 учетверённых слов, инициализированный повторяющимися значениями 0, 1 и 2
l dd 100 dup (?) ; Определяем массив из 100 неинициализированных двойных слов
К переменным можно применить две операции – offset и type. Первая определяет адрес переменной, а вторая – размер переменной. Однако размер переменной определяется по директиве, и даже если с директивой, например, DD определён массив из нескольких элементов, размер всё равно будет равен 4.
2.7. Команды
Команды языка ассемблера – это символьная форма записи машинных команд. Команды имеют следующий синтаксис:
[<метка>:] <мнемокод> [<операнды>] [;<комментарий>]
Метка – это имя. Метка обязательно должна отделяться двоеточием, но может размещаться отдельно, в строке, предшествующей остальной части команды.
Метки нужны для ссылок на команды из других мест, например, в командах перехода. Компилятор языка ассемблера заменяет метки адресами команд.
Мнемокод – это служебное слово, указывающее операцию, которая должна быть выполнена. Язык ассемблера использует не цифровые коды операций, а мнемокоды, которые легче запоминаются. Мнемокод является обязательной частью команды.
Операнды команды, если они есть, отделяются друг от друга запятыми.
2.8. Операнды команд
В качестве операндов команд языка ассемблера могут использоваться:
регистры, обращение к которым осуществляется по именам;
непосредственные операнды – константы, записываемые непосредственно в команде;
ячейки памяти – в команде записывается адрес нужной ячейки.
Для задания адреса существуют следующие возможности.
Имя переменной, по сути, является адресом этой переменной. Встретив имя переменной в операндах команды, компилятор понимает, что нужно обратиться к оперативной памяти по определённому адресу. Обычно адрес в команде указывается в квадратных скобках, но имя переменной является исключением и может быть указано как в квадратных скобках, так и без них. Например, для обращения к переменной x в команде можно указать x или [x].
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |


