Книжная полка Сохранить
Размер шрифта:
А
А
А
|  Шрифт:
Arial
Times
|  Интервал:
Стандартный
Средний
Большой
|  Цвет сайта:
Ц
Ц
Ц
Ц
Ц

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

Покупка
Основная коллекция
Артикул: 778164.01.99
Учебное пособие представляет собой первую часть курса программирования на языке ассемблера, в нем рассматриваются основы языка ассемблера, а также интерфейс с языком С++. Учебное пособие предназначено для студентов, обучающихся по направлениям «Прикладная математика и информатика» и «Математическое обеспечение и администрирование информационных систем».
Лисицин, Д. В. Программирование на языке ассемблера : учебное пособие / Д. В. Лисицин. - Новосибирск : Изд-во НГТУ, 2018. - 100 с. - ISBN 978-5-7782-3679-0. - Текст : электронный. - URL: https://znanium.com/catalog/product/1866916 (дата обращения: 29.11.2024). – Режим доступа: по подписке.
Фрагмент текстового слоя документа размещен для индексирующих роботов
Министерство науки и высшего образования Российской Федерации 

НОВОСИБИРСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ 

 
 
 
 
 
Д.В. ЛИСИЦИН 
 
 
 
 
ПРОГРАММИРОВАНИЕ  
НА ЯЗЫКЕ АССЕМБЛЕРА 
 
 
 
Утверждено  
Редакционно-издательским советом университета  
в качестве учебного пособия 
 
 
 
 
 
 
 
 
 
 
 
 
 
НОВОСИБИРСК 
2018 

УДК 004.431.4(075.8) 
   Л 632 
 
 
 
Рецензенты: 
канд. техн. наук, доцент И.Л. Еланцева 
канд. техн. наук, доцент Ю.В. Тракимус 
 
 
 
Работа подготовлена на кафедре теоретической  
и прикладной информатики для студентов III курса ФПМИ 
 
 
 
 
 
Лисицин Д.В. 
Л 632  
Программирование на языке ассемблера: учебное пособие / 
Д.В. Лисицин. – Новосибирск: Изд-во НГТУ, 2018. – 100 с. 

 
ISBN 978-5-7782-3679-0 

Учебное пособие представляет собой первую часть курса программирования на языке ассемблера, в нем рассматриваются основы языка 
ассемблера, а также интерфейс с языком С++. 
Учебное пособие предназначено для студентов, обучающихся по 
направлениям «Прикладная математика и информатика» и «Математическое обеспечение и администрирование информационных систем».  
 
 
 
УДК 004.431.4(075.8) 
 
 
 
ISBN 978-5-7782-3679-0 
© Лисицин Д.В., 2018 
 
© Новосибирский государственный 
 
    технический университет, 2018 

ВВЕДЕНИЕ 

Большинство программистов пишут свои программы на языках высокого уровня, таких как С++. Подобные языки специально разработаны для того, чтобы приблизить конструкции, с помощью которых программист общается с ЭВМ, к предметной области, для которой 
предназначена конкретная программа.  
В отличие от этих языков язык ассемблера является машинноориентированным языком, это символический аналог машинного языка, позволяющий программировать на уровне машинных команд. Если 
при разработке языков высокого уровня  стремились избежать ориентации на специальные технические особенности вычислительной техники, то язык ассемблера предназначен как раз для учета специфики 
конкретного компьютера. 
Язык ассемблера имеет два достоинства: с одной стороны, он  
позволяет писать программы на уровне команд микропроцессора, а с 
другой стороны, не требует запоминания числовых кодов команд, которыми оперирует процессор. Язык ассемблера оперирует символическими мнемоническими кодами вместо машинных команд и описательными именами для полей данных и адресов памяти. 
Языки высокого уровня позволяют программисту абстрагироваться 
от особенностей компьютера. В этом смысле они похожи на автомобили с автоматической коробкой передач. Однако человек действует более гибко, более искусно, чем автоматическая коробка передач.  
Язык ассемблера представляет собой для ЭВМ эквивалент ручной 
коробки передач. Он обеспечивает программисту больший контроль 
над ЭВМ за счет большего труда, большей детализации и меньшего 
удобства.  
Прикладные программы, полностью написанные на языке ассемблера, встречаются довольно редко, поскольку на их создание, отладку 
и последующее сопровождение уходит слишком много времени.  

Поэтому язык ассемблера в основном используется при написании отдельных фрагментов прикладных программ, т. е. там, где требуется 
максимальная скорость их работы или непосредственный доступ к 
оборудованию. 
Программы на языке ассемблера часто пишут для встраиваемых 
компьютерных систем. В качестве примеров можно привести системы 
питания и зажигания автомобилей, системы управления кондиционерами, охранные системы, системы управления полетами, электронные 
записные книжки, модемы, принтеры и другие «умные» устройства, 
содержащие встроенный микропроцессор.  
Язык ассемблера часто используется в программах для систем реального времени, для игровых приставок, а также для разработки системного программного обеспечения, в том числе для разработки 
драйверов устройств (низкоуровневых системных программ, напрямую взаимодействующих с обслуживаемыми ими устройствами). 
Однако и «высокоуровневым» программистам полезно знать язык 
ассемблера, это поможет лучше понять методики взаимодействия 
между аппаратным обеспечением компьютера, операционной системой и прикладными программами.  
Подобно С++ язык ассемблера представляет собой набор слов, задающих ЭВМ действия, которые она должна выполнить. Но в отличие 
от языка С++ слова из набора команд языка ассемблера имеют непосредственное отношение к компонентам ЭВМ. Так как ассемблер требует задания действий на уровне компонент ЭВМ, то программисту необходимо понимать свойства и возможности интегральной микросхемы, 
содержащей эти компоненты, а именно микропроцессора ЭВМ.  
Важное отличие языка ассемблера от языков высокого уровня в 
том, что написанные на нем программы не являются переносимыми.  
С учетом изложенных выше моментов язык ассемблера не может быть 
переносимым по определению, поскольку он тесно связан с архитектурой микропроцессоров определенного семейства. Таким образом, на 
сегодняшний день существует много совершенно разных языков ассемблера. Каждый из них привязан либо к конкретному семейству  
процессоров, либо к конкретной архитектуре компьютера. 
В данном курсе мы будем в основном рассматривать программирование на языке ассемблера для 32-разрядных процессоров фирмы Intel 
и 32-разрядной операционной системы Microsoft Windows. Предполагается, что работа с программами ведется в Microsoft Visual C++.  

1. РЕГИСТРЫ МИКРОПРОЦЕССОРА 

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

1.1. РЕГИСТРЫ ОБЩЕГО НАЗНАЧЕНИЯ 

Имеется восемь 32-разрядных регистров общего назначения EAX, 
EBX, ECX, EDX, ESI, EDI, EBP, ESP. Они предназначены для хранения данных и адресов, программист может их использовать для реализации своих алгоритмов. Возможно обращение к младшим 16 битам 
регистров как к регистрам с именами AX, BX, CX, DX, SI, DI, BP, SP 
соответственно. В свою очередь регистры AX, BX, CX, DX позволяют 
отдельно обращаться к своим старшим и младшим восьми байтам как 
к регистрам AH/AL, BH/BL, CH/CL и DH/DL. 
Структура регистра EAX показана на рис. 1 (структура регистров 
EBX, ECX, EDX полностью идентична, а в регистрах ESI, EDI, EBP, 
ESP нельзя обращаться по отдельности к их 8-битным частям). 
 

32 бита

16 бит

8 бит + 8 бит

AX

AH
AL

EAX

0
7
15
31
 
Рис. 1 

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

1.2. СЕГМЕНТНЫЕ РЕГИСТРЫ 

Эти регистры используются в качестве базовых при  обращении к 
заранее распределенным областям оперативной памяти, которые называются сегментами. Существует три типа сегментов и соответственно 
сегментных регистров:  
 кода (CS), в них хранятся только команды процессора, т. е. машинный код программы;  
 данных (DS, ES, FS и GS), в них хранятся области памяти, выделяемые под данные;  
 стека (SS). 
Стек представляет собой область памяти, используемую для временного хранения данных и адресов. Микропроцессор использует стек 
для хранения адреса возврата из текущей подпрограммы, но стек можно использовать также для восстановления содержимого регистров, 
изменяемых при работе программы. 
Работа с памятью особенно проста, если используется плоская (несегментированная) модель памяти FLAT. Каждой программе может 
быть выделен один большой сегмент размером до 4 Гбайт (обычно 
программисту доступно около половины), а сегменты, описываемые в 
программе, являются логическими. Адреса ячеек памяти являются  
32-битными (смещение внутри сегмента), а сегментные регистры программист не использует. Мы будем использовать именно модель памяти FLAT. 
Среди описанных выше регистров общего назначения особо следует выделить регистр ESP. В нем хранится указатель на вершину стека, 
поэтому его не рекомендуется использовать для хранения каких-либо 
операндов программы. 

1.3. РЕГИСТР КОМАНДНОГО УКАЗАТЕЛЯ 

Смещение на команду, которая должна быть выполнена следующей, содержит 32-битный регистр EIP (instruction pointer), его младшие 
16 бит составляют регистр IP. Этот регистр непосредственно недосту
пен программисту, но загрузка и изменение его значения производятся 
различными командами управления, к которым относятся команды 
условных и безусловных переходов, вызова процедур и возврата из 
процедур. 

1.4. РЕГИСТР ФЛАГОВ 

Разрядность регистра флагов EFLAGS равна 32 битам. Отдельные 
биты данного регистра имеют определенное функциональное назначение и называются флагами. Младшие 16 бит регистра EFLAGS составляют регистр FLAGS. 
Флаги определяют текущее состояние машины и результаты выполнения команд. Многие арифметические команды и команды сравнения изменяют состояние флагов. Рассмотрим назначение наиболее 
важных для программиста флагов (все они находятся в регистре 
FLAGS): 
OF (overflow flag – флаг переполнения) – указывает на переполнение при командах для чисел со знаком (0 – нет переполнения, 1 – переполнение произошло); 
SF (sign flag  – флаг знака) – содержит результирующий знак после 
операций над числами со знаком (0 – плюс, 1 – минус); 
ZF (zero flag – флаг нуля) – показывает, является ли результат нулевым (0 – ненулевой, 1 – нулевой результат); 
РF (parity flag – флаг четности) – показывает четность младших  
8 бит данных (1 – четное и 0 – нечетное число); 
СF (carry flag – флаг переноса) – указывает на перенос из старшего 
бита результата после арифметических операций (0 – нет переноса, 1 – 
перенос был). 
 

2. СТРУКТУРА ПРОГРАММЫ  
НА ЯЗЫКЕ АССЕМБЛЕРА 

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

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

2.1. КОМАНДЫ 

Команда языка ассемблера в исходной программе имеет следующий вид (в квадратных скобках здесь и ниже указываются необязательные элементы): 
[Метка:] Мнемокод [Операнд(ы)] [; Комментарий] 
Метка, команда и операнд разделяются, по крайней мере, одним 
пробелом или символом табуляции. Приведем пример: 
M:     MOV      АХ, BX      ; Метка, команда, два операнда 

Метка в языке ассемблера может состоять из одного или нескольких символов и содержать латинские буквы, цифры и некоторые специальные символы: _,?, $, @. Первым символом в метке должна быть 
буква или специальный символ.  
Поле мнемокода содержит имя (мнемонический код) команды 
микропроцессора. Мнемокод указывает ассемблеру, какое действие 
должен выполнить микропроцессор. Например, MOV – имя команды 
пересылки данных. 
Если команда специфицирует выполняемое действие, то операнд 
определяет начальное значение данных или элементы, над которыми 
выполняется действие по команде. Например, в приведенной выше 
команде MOV указано, что надо скопировать содержимое регистра BX 
в регистр АХ. 
Команда может иметь один или несколько операндов, или вообще 
быть без операндов. Если операнды присутствуют, то они отделяются 
от мнемокода, по крайней мере, одним пробелом или символом табуляции, между собой операнды разделяются запятой. 
Обычно в командах с двумя операндами первый из них представляет собой приемник, а второй – источник. Операнд-источник определяет значение, которое берется микропроцессором для сложения,  

вычитания, сравнения со значением операнда-приемника или просто 
для загрузки в операнд-приемник. Поэтому при исполнении команды 
операнд-источник никогда не изменяется, в то время как операндприемник изменяется почти всегда 
В нашем случае у команды MOV операнд-приемник – регистр АХ, 
а операнд источник – регистр BX. 
Комментарий начинается на любой строке исходного модуля с 
символа «точка с запятой» (;). Ассемблер полагает в этом случае, что 
все символы, находящиеся справа от «;», являются комментарием. 
Комментарий может занимать всю строку или следовать за командой 
на той же строке.  

2.2. ДИРЕКТИВЫ 

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

2.2.1. ДИРЕКТИВА COMMENT 

Символом «точка с запятой» (;) можно задать только однострочный 
комментарий. При необходимости многострочного комментария необходимо в начале каждой строки указывать этот символ. 
Другим решением здесь будет блочный комментарий, начинающийся с директивы COMMENT, за которой следует символ комментария, определяемый программистом. При этом компилятор игнорирует 
все строки, расположенные между директивой COMMENT и символом, указанным программистом. Например:  
COMMENT !  
Это строка комментария.  
А вот еще одна строка комментария.  
! 

2.2.2. ДИРЕКТИВЫ SEGMENT И ASSUME 

Любые ассемблерные программы содержат, по крайней мере, один 
сегмент – сегмент кода.  

Директива SEGMENT служит для описания сегмента и имеет следующий формат: 
имя SEGMENT [параметры] 
. 
. 
. 
. 
. 
имя ENDS 

Имя сегмента должно непременно присутствовать и  быть уникальным. Директива ENDS означает конец сегмента. Обе директивы 
SEGMENT и ENDS должны иметь одинаковые имена. Директива 
SEGMENT может содержать параметры, которые используются в программах, имеющих сложную структуру, в том числе многомодульных. 
Мы их рассматривать не будем. 
При использовании директивы SEGMENT требуется указать  
компилятору, с каким сегментом связан тот или иной сегментный  
регистр.  
Директива ASSUME сообщает ассемблеру назначение каждого 
сегмента. Он имеет формат: 
ASSUME SS: имя_сегмента, DS: имя_сегмента, CS: имя_сегмента,  
ES: имя_сегмента, FS: имя_сегмента, GS: имя_сегмента 

Если программа не использует какой-либо регистр, то его описание 
можно опустить. 
Для модели памяти FLAT следует использовать следующее описание: 
ASSUME  SS: FLAT, DS: FLAT, CS: FLAT, ES: FLAT, FS: ERROR,  
GS: ERROR 

Здесь FLAT – это имя группы, в которую объединяются сегменты стека, данных и кода. 

2.2.3. УПРОЩЕННЫЕ ДИРЕКТИВЫ СЕГМЕНТАЦИИ 

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