ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 24.12.2021
Просмотров: 12193
Скачиваний: 10

5 1 8 Глава 7. Уровень языка ассемблера
Во время выполнения объектной программы задействовано только три уровня:
микроархитектурный уровень, уровень команд и уровень операционной системы.
Следовательно, во время работы программы в памяти компьютера можно найти
три программы: пользовательскую объектную программу, операционную систему
и микропрограмму (если она есть). Никаких следов исходной программы не оста-
ется. Таким образом, число уровней, присутствующих при выполнении програм-
мы, может отличаться от числа уровней, присутствующих до трансляции. Следует
отметить, что хотя мы определяем уровень по командам и языковым конструкциям,
доступным программистам этого уровня (а не по технологии реализации), некото-
рые авторы иногда проводят различие между уровнями, реализованными интер-
претаторами, и уровнями, реализованными при трансляции.
Введение в язык ассемблера
Трансляторы можно разделить на две группы в зависимости от отношения между
входным и выходным языком. Если входной язык является символической ре-
презентацией числового машинного языка, то транслятор называется
ассембле-
ром,
а входной язык называется
языком ассемблера.
Если входной язык является
языком высокого уровня (например, Java или С), а выходной язык является либо
числовым машинным языком, либо символической репрезентацией последнего,
то транслятор называется
компилятором.
Что такое язык ассемблера?
Язык ассемблера — это язык, в котором каждое высказывание соответствует ров-
но одной машинной команде. Иными словами, существует взаимно однозначное
соответствие между машинными командами и операторами в программе на языке
ассемблера. Если каждая строка в программе на языке ассемблера содержит ровно
один оператор и каждое машинное слово содержит ровно одну команду, то про-
грамма на языке ассемблера в п строк произведет программу на машинном языке
из п слов.
Мы используем язык ассемблера, а не программируем на машинном языке
(в шестнадцатеричной системе счисления), поскольку на языке ассемблера програм-
мировать гораздо проще. Использовать символьные имена и адреса вместо двоич-
ных и восьмеричных намного удобнее. Многие могут запомнить, что обозначения-
ми для сложения (add), вычитания (subtract), умножения (multiply) и деления
(divide) служат команды ADD, SUB, MUL и DIV, но мало кто может запомнить соответ-
ствующие числа, которые использует машина. Программисту на языке ассемблера
нужно знать только символические названия, поскольку ассемблер транслирует
их в машинные команды.
Это утверждение касается и адресов. Программист на языке ассемблера может
дать имена ячейкам памяти, и уже ассемблер должен будет выдавать правильные
числа. Программист на машинном языке всегда должен работать с числовыми но-
мерами адресов. Сейчас уже нет программистов, которые пишут программы на
машинном языке, хотя несколько десятилетий назад до изобретения ассемблеров
программы именно так и писались.

Введение в язык ассемблера 519
Язык ассемблера имеет несколько особенностей, отличающих его от языков
высокого уровня. Во-первых, это взаимно однозначное соответствие между вы-
сказываниями языка ассемблера и машинными командами (об этом мы уже гово-
рили). Во-вторых, программист на языке ассемблера имеет доступ ко всем объек-
там и командам, присутствующим на целевой машине. У программистов на языках
высокого уровня такого доступа нет. Например, если целевая машина содержит
бит переполнения, программа на языке ассемблера может проверить его, а про-
грамма на языке Java не может. Программа на языке ассемблера может выполнить
любую команду из набора команд целевой машины, а программа на языке высоко-
го уровня не может. Короче говоря, все, что можно сделать в машинном языке,
можно сделать и на языке ассемблера, но многие команды, регистры и другие объек-
ты недоступны для программиста, пишущего программы на языке высокого уров-
ня. Языки для системного программирования (например С) часто занимают про-
межуточное положение. Они обладают синтаксисом языка высокого уровня, но
при этом с точки зрения возможностей доступа ближе к языку ассемблера.
Наконец, программа на языке ассемблера может работать только на компьюте-
рах одного семейства, а программа, написанная на языке высокого уровня, потен-
циально может работать на разных машинах. Возможность переносить программ-
ное обеспечение с одной машины на другую очень важна для многих прикладных
программ.
Зачем нужен язык ассемблера?
Язык ассемблера довольно труден. Написание программы на языке ассемблера
занимает гораздо больше времени, чем написание той же программы на языке вы-
сокого уровня. Кроме того, очень много времени занимает отладка.
Но зачем же тогда вообще писать программы на языке ассемблера? Есть две
причины: производительность и доступ к машине. Во-первых, профессиональный
программист языка ассемблера может составить гораздо меньшую по размеру про-
грамму, которая будет работать гораздо быстрее, чем программа, написанная на
языке высокого уровня. Для некоторых программ скорость и размер весьма важ-
ны. Многие встроенные прикладные программы, например программы в кредит-
ных карточках, сотовых телефонах, драйверах устройств, а также процедуры BIOS
попадают в эту категорию.
Во-вторых, некоторым процедурам требуется полный доступ к аппаратному
обеспечению, что обычно невозможно сделать на языке высокого уровня. В эту
категорию попадают прерывания и обработчики прерываний в операционных сис-
темах, а также контроллеры устройств во встроенных системах, работающих в ре-
жиме реального времени.
Первая причина (достижение высокой производительности) является более
важной, поэтому мы рассмотрим ее подробнее. В большинстве программ лишь
небольшой процент всего кода отвечает за большой процент времени выполне-
ния программы. Обычно 1% программы отвечает за 50% времени выполнения,
а 10% программы отвечает за 90% времени выполнения.
Предположим, что для написания программы на языке высокого уровня требу-
ется 10 человеко-лет и что полученной программе требуется 100 секунд, чтобы

5 2 0
Глава 7. Уровень языка ассемблера
выполнить некоторую типичную контрольную задачу.
(Контрольная задача
— это
программа проверки, которая используется для сравнения компьютеров, компи-
ляторов и т. п.). Написание всей программы на языке ассемблера может занять
50 человеко-лет. Полученная в результате программа будет выполнять контрольную
задачу примерно за 33 секунды, поскольку хороший программист может оказаться
в три раза умнее компилятора (хотя об этом можно спорить бесконечно). Ситуа-
ция проиллюстрирована в табл. 7.1.
Так как только крошечная часть программы отвечает за большую часть време-
ни выполнения этой программы, возможен другой подход. Сначала программа
пишется на языке высокого уровня. Затем проводится ряд измерений, чтобы опре-
делить, какие части программы отвечают за большую часть времени выполнения.
Для таких измерений обычно используется системный тактовый генератор. С его
помощью можно узнать, сколько времени затрачивается на каждую процедуру,
сколько раз выполняется каждый цикл и т. п.
Предположим, что 10% программы отвечает за 90% времени ее выполнения. Это
значит, что из всех 100 секунд работы 90 секунд проводится в этих 10%, а 10 се-
кунд — в оставшихся 90% программы. Эти 10% программы можно усовершенство-
вать, переписав их на язык ассемблера. Этот процесс называется
настройкой
(tuning).
Он проиллюстрирован в табл. 7.1. На переделку основных процедур потребуется
еще 5 лет, но время выполнения программы сократится с 90 секунд до 30 секунд.
Таблица 7 . 1
. Сравнение программирования на языке ассемблера и на языке
высокого уровня (с настройкой и без настройки)
Язык ассемблера
Язык высокого уровня
Смешанный подход до настройки
Критические 10%
Остальные 90%
Всего 10 100
Смешанный подход после настройки
Критические 10% 6 30
Остальные 90% 9 10
Всего 15 40
Сравним этот смешанный подход, в котором используется и язык ассемблера,
и язык высокого уровня, с подходом, в котором применяется только язык ас-
семблера (табл. 7.1). При втором подходе программа работает примерно на 20%
быстрее (33 секунды против 40 секунд), но более чем за тройную цену (50 челове-
ко-лет против 15). Более того, у смешанного подхода есть еще одно преимущество:
гораздо проще переделать в код ассемблера уже отлаженную процедуру, написан-
ную на языке высокого уровня, чем писать процедуру на языке ассемблера с нуля.
Количество человеко-лет,
затрачиваемых
на написание программы
50
10
1
9
Время выполнения
программы в секундах
33
100
90
10

Введение в язык ассемблера 521
Отметим, что если бы написание программы занимало только 1 год, соотношение
между смешанным подходом и подходом, при котором используется только язык
ассемблера, составляло бы 4:1 в пользу смешанного подхода.
Программист, который использует язык высокого уровня, не занят перемеще-
нием битов и может так решить задачу, так построить программу, что в конце
концов достигнет действительно большого увеличения производительности. А про-
граммисты, пишущие программы на языке ассемблера, обычно стараются так по-
строить команды, чтобы сэкономить несколько циклов, поэтому у них такой ситу-
ации возникнуть не может.
Расскажем о двух экспериментах, проведенных во время разработки системы
MULTICS. Грехем [49] описал процедуру PL/I, за три месяца переделанную в но-
вую версию, которая была в 26 раз меньше и работала в 50 раз быстрее, чем исход-
ная. Он описал еще одну процедуру PL/L, которая получилась в 20 раз меньше
исходной и работала в 40 раз быстрее, чем исходная, после двух месяцев работы. Кор-
бато [27] описал процедуру PL/I, размер кода которой был сокращен с 50 000 слов
до 1000 слов менее чем за месяц, а контроллер уменьшен с 65 000 до 30 000 слов
с увеличением производительности в 8 раз за 4 месяца. Здесь важно понимать, что
у программистов языков высокого уровня глобальный подход к тому, что они
делают, поэтому они гораздо быстрее могут разработать лучший алгоритм.
Однако, несмотря на все это, существует по крайней мере 4 веские причины
для изучения языка ассемблера. Во-первых, желательно уметь писать программы
на языке ассемблера, поскольку успех или неудача большого проекта может зави-
сеть от того, можно ли повысить производительность какой-то важной процедуры
в 2 или 3 раза.
Во-вторых, язык ассемблера может быть единственным возможным выходом
из-за недостатка памяти. Кредитные карты, например, содержат центральный про-
цессор, но у них нет мегабайта памяти и жесткого диска. Однако они должны вы-
полнять сложные вычисления при наличии ограниченных ресурсов. Процессоры,
встроенные в электроприборы, часто имеют минимальное количество памяти, по-
скольку они должны быть достаточно дешевыми. Различные электронные устрой-
ства, работающие на батарейках, обычно содержат очень маленькую память, по-
этому здесь тоже нужен эффективный код.
В-третьих, компилятор должен либо давать на выходе программу, которая ис-
пользуется ассемблером, либо самостоятельно выполнять процесс ассемблирова-
ния. Таким образом, понимание языка ассемблера существенно для понимания
того, как работает компьютер. И вообще, кто-то ведь должен писать компилятор
и его ассемблер.
Наконец, изучение языка ассемблера дает прекрасное представление о реаль-
ной машине. Для тех, кто изучает архитектуру компьютеров, написание програм-
мы на языке ассемблера — единственный способ узнать, что собой представляет
машина на архитектурном уровне.
Формат оператора в языке ассемблера
Хотя структура оператора в языке ассемблера отражает структуру соответствую-
щей машинной команды, языки ассемблера для разных машин и разных уровней
во многом сходны друг с другом, что позволяет говорить о языке ассемблера вооб-

5 2 2
Глава 7. Уровень языка ассемблера
ще. В таблицах 7.2-7.4 показаны фрагменты программ на языке ассемблера для
Pentium II, Motorola 680x0 и (Ultra)SPARC. Все эти программы выполняют вы-
числение N=I+J. Во всех трех примерах операторы над пропуском в таблице вы-
полняют вычисление. Операторы под пропуском — это указания ассемблеру заре-
зервировать память для переменных I, J и N. Последние не являются символьными
репрезентациями машинных команд.
Таблица 7.2.
Вычисление выражения N=l+J в Pentium II
Метка
Код операции Операнды Комментарии
EAX.I ; регистр ЕАХ=1
EAX.J ; регистр EAX=I+J
N.EAX ; N=l+J
3 ; резервируем 4 байта и устанавливаем значение 3
4 ; резервируем 4 байта и устанавливаем значение 4
О ; резервируем 4 байта и устанавливаем значение О
FORMULA:
1
J
N
MOV
ADD
MOV
DW
DW
DW
Таблица 7.3.
Вычисление выражения N=l+J в Motorola 680x0
Метка
Код операции Операнды Комментарии
FORMULA:
MOVE.L
ADD.L
MOVE.L
I,DO
J,D0
D0,N
; регистр D0=l
; регистр D0=l+J
; N=l+J
I
J
N
DC.L
DC.L
DC.L
; резервируем 4 байта и устанавливаем значение 3
; резервируем 4 байта и устанавливаем значение 4
; резервируем 4 байта и устанавливаем значение 0
Таблица 7.4.
Вычисление выражения N=l+J в SPARC
Метка Код Операнды
операции
Комментарии
FORMULA:
1:
J:
N:
SETHI
LD
SETHI
LD
NOP
ADD
SETHI
ST
.WORD3
.WORD 4
.WORD 0
%HI(I),%R1
[%R1+%LO(I)],%R1
%HI(J),%R2
[%R2+%LO(J)],%R2
%R1,%R2,%R2
%HI(N),%R1
%R2, [%R1+%LO(N)]
3
4
0
! R1 = старшие биты адреса I
! R1=l
! R2 = старшие биты адреса J
!R2=J
! ждем прибытия J из памяти
!R2=R1+R2
! R1 = старшие биты адреса N
! резервируем 4 байта и устанавливаем знач. 3
! резервируем 4 байта и устанавливаем знач. 4
! резервируем 4 байта и устанавливаем знач. 0