Содержание

Предисловие
Введение

Глава 1. Архитектура реального режима
1.1. Память и процессор
1.2. Распределение адресного пространства
1.3. Регистры процессора
1.4. Сегментная структура программ
1.5. Стек
1.6. Система прерываний
1.7. Система ввода-вывода

Глава 2. Основы программирования
2.1. Подготовка и отладка программы
2.2. Представление данных
2.3. Описание данных
2.4. Структуры и записи
2.5. Способы адресации
2.6. Переходы
2.7. Вызовы подпрограмм
2.8. Макросредства ассемблера

Глава 3. Команды и алгоритмы
3.1. Организация приложений MS-DOS
3.2. Циклы и условные переходы
3.3. Обработка строк
3.4. Использование подпрограмм
3.5. Двоично-десятичные числа
3.6. Программирование аппаратных средств

Глава 4. Расширенные возможности современных микропроцессоров
4.1. Архитектурные особенности
4.2. Дополнительные режимы адресации
4.3. Использование средств 32-разрядных процессоров в программировании
4.4. Основы защищенного режима

Глава 5. Программирование для Windows
5.1. Приложения Windows и язык ассемблера
5.2. Дополнительные средства ассемблера
5.3. Простейшее приложение Windows
5.4. Основы программирования Windows
5.5. Изобразительные средства в главном окне
5.6. Меню и диалоги
5.7. 32-разрядные приложения Windows

Приложение А. Система команд процессоров Intel
AAA ASCII-коррекция регистра AX после сложения
AAD ASCII-коррекция регистра AX перед делением
AAM ASCII-коррекция регистра AX после умножения
AAS ASCII-коррекция регистра AL после вычитания
ADC Целочисленное сложение с переносом
ADD Целочисленное сложение
AND Логическое И
ARPL Коррекция запрашиваемого уровня привилегий селектора
BOUND Проверка индекса массива на выход за границы массива
BSF Прямое сканирование битов
BSR Обратное сканирование битов
BSWAP Обмен байтов
BT Проверка бита
BTC Проверка и инверсия бита
BTR Проверка и сброс бита
BTS Проверка и установка бита
CALL Вызов подпрограммы
CBW Преобразование байта в слово
CDQ Преобразование двойного слова в четверное
CLC Сброс флага переноса
CLD Сброс флага направления
CLI Сброс флага прерываний
CLTS Сброс флага переключения задачи в управляющем регистре 0
CMC Инвертирование флага переноса
CMP Сравнение
CMPS Сравнение строк
CMPSB Сравнение строк по байтам
CMPSW Сравнение строк по словам
CMPSD Сравнение строк по двойным словам
CMPXCHG Сравнение и обмен
CMPXCHG8B Сравнение и обмен 8 байтов
CPUID Идентификация процессора
CWD Преобразование слова в двойное слово
CWDE Преобразование слова в двойное слово с расширением
DAA Десятичная коррекция в регистре AL после сложения
DAS Десятичная коррекция в регистре AL после вычитания
DEC Декремент (уменьшение на 1)
DIV Деление целых чисел без знака
ENTER Создание стекового кадра для параметров процедуры
HLT Останов
IDIV Деление целых чисел со знаком
IMUL Умножение целых чисел со знаком
IN Ввод из порта
INC Инкремент (увеличение на 1)
INS Ввод строки из порта
INSB Ввод байта из порта
INSW Ввод слова из порта
INSD Ввод двойного слова из порта
INT Программное прерывание
INTO Прерывание по переполнению
IRET Возврат из прерывания
IRETD Возврат из прерывания в 32-разрядном режиме
Jcc Команды условных переходов
JMP Безусловный переход
LAHF Загрузка флагов в регистр AH
LAR Загрузка прав доступа
LDS Загрузка указателя с использованием регистра DS
LEA Загрузка исполнительного адреса
LEAVE Выход из процедуры высокого уровня
LES Загрузка указателя с использованием регистра ES
LFS Загрузка указателя с использованием регистра FS
LGS Загрузка указателя с использованием регистра FS
LSS Загрузка указателя с использованием регистра FS
LGDT Загрузка регистра таблицы глобальных дескрипторов
LIDT Загрузка регистра таблицы дескрипторов прерываний
LLDT Загрузка регистра таблицы локальных дескрипторов
LMSW Загрузка слова состояния машины
LOCK Запирание шины
LODS Загрузка операнда из строки
LODSB Загрузка байта из строки
LODSW Загрузка слова из строки
LODSD Загрузка двойного слова из строки
LOOP Циклическое выполнение, пока содержимое CX не равно нулю
LOOPE/LOOPZ Циклическое выполнение, пока равно/циклическое выполнение, пока нуль
LOOPNE/LOOPNZ Циклическое выполнение, пока не равно/циклическое выполнение, пока не нуль
LSL Загрузка границы сегмента
LTR Загрузка регистра задачи TR
MOV Пересылка данных
MOV Пересылка в\из специальных регистров
MOVS Пересылка данных из строки в строку
MOVSB Пересылка байта данных из строки в строку
MOVSW Пересылка слова данных из строки в строку
MOVSD Пересылка двойного слова из строки в строку
MOVSX Пересылка с расширением знака
MOVZX Пересылка с расширением нуля
MUL Умножение целых чисел без знака
NEG Изменение знака, дополнение до 2
NOP Холостая команда
NOT Инверсия, дополнение до 1, логическое отрицание
OR Логическое ВКЛЮЧАЮЩЕЕ ИЛИ
OUT Вывод в порт
OUTS Вывод строки в порт
OUTSB Вывод байта в порт
OUTSW Вывод слова в порт
OUTSD Вывод двойного слова в порт
POP Извлечение слова из стека
POPA Восстановление из стека всех регистров
POPAD Восстановление из стека всех регистров в 32-разрядном режиме
POPF Восстановление из стека регистра флагов
POPFD Восстановление из стека расширенного регистра флагов
PUSH Занесение операнда в стек
PUSHA Сохранение в стеке всех регистров
PUSHAD Сохранение в стеке всех регистров в 32-разрядном режиме
PUSHFD Занесение в стек содержимого расширенного регистра флагов
RCL Циклический сдвиг влево через бит переноса
RCR Циклический сдвиг вправо через бит переноса
RDMSR Чтение особого регистра модели
REP Повторение
REPE Повторение пока равно
REPZ Повторение пока нуль
REPNE Повторение пока равно
REPNZ Повторение пока не равно
RET Возврат из процедуры
RETN Возврат из ближней процедуры
RETF Возврат из дальней процедуры
ROL Циклический сдвиг влево
ROR Циклический сдвиг вправо
SAHF Запись содержимого регистра AH в регистр флагов
SAL Арифметический сдвиг влево
SBB Целочисленное вычитание с займом
SCAS Сканирование строки с целью сравнения
SCASB Сканирование строки байтов с целью сравнения
SCASW Сканирование строки слов с целью сравнения
SCASD Сканирование строки двойных слов с целью сравнения
SETcc Установка байта по условию
SGDT Сохранение в памяти содержимого регистра таблицы глобальных дескрипторов
SHL Логический сдвиг влево
SHLD Логический сдвиг влево двойного слова
SHR Логический сдвиг вправо
SHRD Логический сдвиг вправо двойного слова
SIDT Сохранение в памяти содержимого регистра таблицы дескрипторов прерываний
SLDT Сохранение содержимого регистра таблицы локальных дескрипторов
SMSW Сохранение слова состояния машины
STC Установка флага переноса
STD Установка флага направления
STI Установка флага прерывания
STOS Запись в строку данных
STOSB Запись байта в строку данных
STOSW Запись слова в строку данных
STOSD Запись двойного слова в строку данных
STR Сохранение содержимого регистра состояния задачи
SUB Вычитание целых чисел
TEST Логическое сравнение
VERR Проверка сегмента на чтение
VERW Проверка сегмента на запись
XADD Обмен и сложение
XCHG Обмен данными между операндами
XLAT Табличная трансляция
XLAT Табличная трансляция (386+)
XLATB Табличная трансляция (386+)
XOR Логическое ИСКЛЮЧАЮЩЕЕ ИЛИ
Приложение Б. Основные операторы ассемблера TASM
Директивы определения данных
DB Определение данных размером в байт
DW Определение данных размером в слово
DD Определение данных размером в двойное слово
DQ Определение данных размером в четверное слово
DF Определение данных размером в 6 байт
DP Определение данных размером в 6 байт
DT Определение данных размером в 10 байт
Директивы присваивания
EQU Присвоение значения указанному идентификатору
= Присвоение значения указанному идентификатору
Директивы счетчика текущего адреса
$ Идентификатор счетчика текущего адреса
LABEL Задание значения идентификатора (имени поля данных или программной метки)
ORG Установка значения счетчика текущего адреса
Операторы типа
PTR Задание типа переменной или метки
OFFSET Возвращает смещение (младшую часть адреса)
SEG Возвращает сегмент (старшую часть адреса)
Директивы организации сегментов и процедур
SEGMENT Открытие сегмента памяти
ENDS Закрытие сегмента памяти
PROC Указание начала процедуры
ENDP Указание конца процедуры
ASSUME Задание сегментного регистра, используемого по умолчанию
Директивы описания макросов
MACRO Начало определения макроса
ENDM Конец определения макроса
LOCAL Объявление временных имен меток, заменяемых в процессе повторных макрорасширений на уникальные имена
Директива повторения
REPT Повторение предложений языка
Директивы организации листинга
.XLIST Подавление дальнейшего вывода в листинг трансляции
.LIST Разрешение дальнейшего вывода в листинг трансляции
.SALL Подавление вывода в листинг трансляции текстов макрорасширений
Директивы условной трансляции
IF Трансляция по заданному условию
IFDEF Трансляция при условии, что идентификатор определен
IFNDEF Трансляция при условии, что идентификатор не определен
Директивы организации структуры программы
.MODEL Задание модели памяти
.CODE Открытие сегмента команд
.DATA Открытие сегмента данных
.FARDATA Открытие дальнего сегмента данных
.STACK Открытие сегмента стека
PROC расширенная форма описания процедуры
CALL расширенная форма вызова процедуры
LOCALS Объявление локальных идентификаторов

Предисловие
Книга рассчитана на пользователей персональных компьютеров типа IBM PC, которые хотели бы поближе познакомиться с архитектурными особенностями семейства микропроцессоров корпорации Intel и научиться понимать и составлять программы на языке ассемблера для этих процессоров.
Если существующие сегодня языки программирования (а их насчитывается не один десяток) условно разбить на две категории, то в одну из них попадет язык ассемблера, а в другую – все остальные. В то время, как языки высокого уровня ориентируются на решение тех или иных прикладных задач (математических, экономических и др.) и содержат в себе средства алгоритмического описания этих задач, язык ассемблера является отражением архитектуры процессора, т.е. его логического устройства, режимов и возможностей. Операторы языка ассемблера представляют собой фактически команды микропроцессора, только выраженные не в виде машинных кодов, а с помощью более наглядных мнемонических обозначений. Эта особенность языка ассемблера определяет его возможности и сферы использования.
Как известно, программы, написанные на языке ассемблера (если, конечно, они написаны грамотно), отличаются высокой эффективностью, т.е. минимальным объемом и максимальным быстродействием. Это обстоятельство обусловило широкое использование языка ассемблера в тех случаях, когда скорость работы программы или расходуемая ею память имеют решающее значение. Некоторые классы программ (например, программы драйверов устройств, отличающиеся жесткой структурой) требуют для своего составления обязательного использования языка ассемблера. Язык ассемблера также удобен для написания программ управления нестандартной аппаратурой, подключаемой к компьютеру с целью построения автоматизированных систем управления научными измерениями или технологическими процессами, тем более, что от таких программ часто требуется еще и максимальное быстродействие. В то же время для разработки программ математического или логического характера – по моделированию, обработке числовой или символьной информации, обслуживанию баз данных и др., лучше подходят языки высокого уровня.
Язык ассемблера является основным инструментом исследования программ, для которых нет исходных текстов. Расшифровка выполнимых (загружаемых) программ осуществляется с помощью так называемых деассемблеров – служебных программ, преобразующих коды
выполнимой программы в текст на языке ассемблера. Естественно,
Работа с этим текстом предполагает достаточно хорошее знание языка
ассемблера.
При отладке программ, написанных на языках высокого уровня типа С или С++, приходится пользоваться встроенным в систему программирования интерактивным отладчиком, всегда содержащим в себе деассемблер. В простых случаях удается отладить программу, наблюдая ход выполнения операторов языка высокого уровня; более тонкие ошибки обнаруживаются только на уровне машинных кодов путем анализа содержимого регистров процессора и конкретных ячеек памяти. Такой метод ценен еще и тем, что позволяет войти внутрь вызываемых из прикладной программы системных средств (функций DOS или Windows) и проанализировать взаимодействие отлаживаемой программы с внутренними функциями операционной системы.
Кроме потребительских качеств, язык ассемблера имеет еще значительную методическую ценность. Отражая архитектурные особенности используемого в компьютере микропроцессора, а также средства взаимодействия микропроцессора с аппаратурой компьютера и подключенными к компьютеру устройствами, язык ассемблера предоставляет уникальную возможность изучения процессора и компьютера в целом, освоения того, что и как умеет делать аппаратура компьютера и каковы ее возможности и ограничения. Знакомство с внутренними возможностями компьютера чрезвычайно полезно, в частности, для программиста, работающего на языках Паскаль или Си, так как позволяет увидеть за формализмом языка высокого уровня те реальные процессы, которые будут протекать в системе при выполнении прикладной программы и, следовательно, более осознанно подойти к разработке структуры программы и ее конкретных алгоритмов.
В целом можно сказать, что знакомство с языком ассемблера необходимо для любого специалиста, которому приходится писать программы для персонального компьютера, в какой бы прикладной области он не подвизался, и на каком бы языке программирования не работал.
Язык ассемблера, как и любой другой язык программирования, имеет массу встроенных средств, позволяющих в ряде случаев ускорить и облегчить процесс программирования и повысить качество создаваемых программ. Профессиональная работа на языке ассемблера, естественно, предполагает детальное знакомство с этими средствами. Однако для широкого круга прикладных специалистов, если только они не предполагают пользоваться языком ассемблера, как основным языком программирования, нет необходимости в детальном изучении многочисленных тонкостей языка. Для того, чтобы составлять прикладные (как правило, не очень сложные в алгоритмическом плане) программы на языке ассемблера и, что еще более важно, читать и понимать уже
составленные программы, достаточно иметь хорошее представление о системе команд и способах адресации и уметь пользоваться основными конструкциями языка. С другой стороны, столь же необходимо иметь ясное представление об архитектуре процессора и компьютера в целом. Всем этим вопросам и посвящена настоящая книга.
В главе, посвященной архитектуре реального режима, рассматриваются наиболее принципиальные элементы архитектуры процессора и компьютера в целом: понятие сегментной адресации, способы адресации, предоставляемые микропроцессором, система программных и аппаратных прерываний, распределение адресного пространства и система ввода-вывода.
Глава «Основы программирования» посвящена методике подготовки и отладки программ на языке ассемблера, типам данных и средствам их описания, взаимодействию программ с операционной системой
MS-DOS, а также использованию различных способов передачи управления внутри программы.
В следующей главе описываются типы приложений DOS (программы .EXE и .COM, резидентные программы, обработчики прерываний) и правила их составления, даются многочисленные примеры использования наиболее важных команд, конструкций и алгоритмов языка. Особо рассматриваются принципы программного управления аппаратурой, подключаемой к компьютеру.
Отдельная глава книги посвящена расширенным возможностям
современных 32-разрядных микропроцессоров типа Pentium. Здесь рассматриваются архитектурные особенности этих процессоров, возможности использования 32-разрядных операндов в прикладных программах, основные принципы и команды защищенного режима.
В настоящем издании книга дополнена большой главой «Программирование для Windows», посвященной составлению на языке ассемблера приложений Windows. Хотя приложения Windows в подавляющем большинстве случаев пишутся на языках высокого уровня, однако
общее знакомство с принципами составления Windows-приложения на языке ассемблера позволяет глубже понять структуру такого приложения и механизмы его взаимодействия с операционной системой.
Эти знания могут оказаться весьма полезными при отладке сложных Windows-приложений, написанных, например, на языке С++, поскольку при использовании интерактивного отладчика программист имеет дело фактически с ассемблерным текстом отлаживаемой программы.
Программирование в системе Windows является весьма сложным делом, требующим от программиста детального знакомства как с обширным набором функций Windows, предназначенных для разработки прикладных программ (так называемого интерфейса прикладного программирования, или API от Application Program Interface), так и понимания многообразных концепций, лежащих в основе функционирования операционной системы Windows. Естественно, в рамках этой книги изложить все эти вопросы не представляется возможным. В примерах настоящей главы проиллюстрированы лишь наиболее важные принципы, используемые практически в любой прикладной программе, предназначенной для функционирования в среде Windows.
В главу «Программирование для Windows» включен параграф, посвященный дополнительным возможностям ассемблера: заданию используемой в программе модели памяти, директивам сегментации программ, специальным средствам описания и вызова процедур, написанных по правилам языков Pascal и С. Все эти средства можно использовать и в приложениях реального режима, однако в программах для DOS их применение не особенно оправдано, так как делает программы менее наглядными и затрудняет их отладку; в прграммах же для Windows, наоборот, применение этих средств упрощает программу, повышает ее наглядность и в конечном счете оказывается весьма
эффективным.
В последующих параграфах главы последовательно описываются базовые средства Windows и их реализация в приложениях на языке ассемблера: образование главного окна приложения, цикл обработки сообщений Windows и обработка этих сообщений в оконной процедуре, вывод в окно содержательной информации, организация диалоговых окон и меню.
Особый интерес может представить заключительный параграф книги, посвященный составлению 32-разрядных приложений. Этот вопрос обычно не рассматривается в традиционных курсах языка ассемблера, потому что 32-разрядные приложения можно реализовать лишь
в 32-разрядной операционной системе, каковой является система
Windows, а основы ассемблера традиционно излагаются на примерах значительно более простых и наглядных приложений DOS.
В приложениях дается описание всех команд микропроцессоров 8086...Pentium с примерами и пояснениями, а также перечень основных операторов языка ассемблера.
Книга не требует от читателя серьезных знаний в области вычислительной техники или даже навыков программирования (во всяком случае, так кажется автору). В то же время можно надеяться, что приведенный в книге материал позволит читателям получить представление о современных микропроцессорах и их работе в составе персонального компьютера, а также приобрести знания, необходимые для составления и отладки программ на языке ассемблера.
К. Г. Финогенов