Академический Документы
Профессиональный Документы
Культура Документы
2007 - Ассемблер для Win 32. Самоучитель
2007 - Ассемблер для Win 32. Самоучитель
Галисеев
Самоучитель
АССЕМБЛЕР
для WIN32
Состав серии
1. Базылев Г.В. Photoshop CS. Самоучитель
2. Бидасюк Ю.М. Mathsoft® MathCAD 12. Самоучитель
3. Варакин А.С. AutoCAD 2006. Самоучитель
4. Галисеев Г.В. Программирование в среде Delphi 2005. Самоучитель
5. Галисеев Г.В. Программирование на языке C#. Самоучитель
6. Гусев В.С. Освоение Internet. Самоучитель
7. Гусев В.С. Поиск в Internet. Самоучитель
8. Дригалкин В.В. HTML в примерах. Как создать свой Web-сайт. Самоучитель
9. Захарченко Н.И. Бизнес-статистика и прогнозирование в MS Excel. Самоучитель
10.Козлин В.И. Музыкальный редактор Sibelius. Самоучитель
11. Курбатова Е.А. MATLAB 7. Самоучитель
12. Курбатова Е.А. Microsoft Excel 2003. Самоучитель
13. Куссуль Н.Н., Шелестов А.Ю. Использование PHP. Самоучитель
14. Меженный О.А. Microsoft Office 2003. Самоучитель
15. Меженный О.А. Microsoft Windows XP. Самоучитель, 2-е издание
16. Меженный О.А. Microsoft Word 2003. Самоучитель
17. Меженный О.А. Turbo Pascal. Самоучитель
18. Полонская Е.Л. Язык HTML. Самоучитель
19. Птицын К.А. Серверы Linux. Самоучитель
20. Сергеев А.П. Офисные локальные сети. Самоучитель
21. Сергеев А.П., Кущенко С.В. Основы компьютерной графики. Adobe Photoshop и CorelDRAW — два в
одном. Самоучитель
22. Сергеев А.П., Терен А.Н. Программирование в Microsoft Visual C++ 2005. Самоучитель
23. Сингаевская Г.И. Microsoft Project 2003. Самоучитель
24. Слепцова Л.Д. Программирование на VBA. Самоучитель
25. Смолина М.А. Adobe Illustrator CS. Самоучитель
26. Смолина М.А. CorelDraw X3. Самоучитель
27. Спека М.В. Microsoft PowerPoint 2003. Самоучитель
28. Спека М.В. Создание Web-сайтов. Самоучитель
29. Ставровский А.Б. Первые шаги в программировании. 2-е издание. Самоучитель
30. Степаненко О.С. Использование Adobe Photoshop CS2. Самоучитель.
31. Степаненко О.С. Настройка персонального компьютера. Установки BIOS. Самоучитель
32. Степаненко О.С. Первая помощь ПК. Анализ сбоев и устранение неполадок. Самоучитель
33. Степаненко О.С. Персональный компьютер. 2-е издание. Самоучитель
34. Степаненко О.С. Установка и настройка Windows XP. Самоучитель
35. Бурлаков М.А. Macromedia Flash 8. Самоучитель
36. Тимошок Т.В. Microsoft Access 2003. Самоучитель
37. Шмидский Я.К. Программирование на языке C++. Самоучитель
38. Футько В.П. ПК — это очень просто. Для тех, кто решил приобрести компьютер. Самоучитель
Самоучитель
АССЕМБЛЕР
для WIN32
www.dialektika.com
Галисеев, Г.В.
Г15 Ассемблер для Win 32. Самоучитель : М. : Издательский дом ‘‘Вильямс’’, 2007.
368 с. : ил.
ISBN 9785845911971 (рус.)
Эта книгасамоучитель поможет читателю самостоятельно освоить основы языка
ассемблера и научиться создавать программы на этом языке. Здесь подробно расска
зано о том, как начать работать с ассемблером и как писать программы на этом язы
ке. В книге рассматривается в основном 32разрядный режим работы ассемблера,
позволяющий обращаться к процедурам прикладного интерфейса (API) Windows.
Тем не менее, приведены и некоторые сведения, специфичные только для 16раз
рядного режима, например, описание работы прерываний и понятие о программи
ровании для DOS. Однако книга является лишь введением в язык ассемблера, по
этому в ней не отражены расширенные возможности языка ассемблера и команды,
разработанные для новейших версий процессоров.
Книга не является учебником по программированию для начинающих и для ра
боты с ней необходимо иметь базовые понятия о программировании, а также хотя
бы минимальное представление о том, как работает операционная система Windows.
Стр. 4
Îãëàâëåíèå
Введение 17
Глава 1. Предисловие 18
Стр. 5
Ñîäåðæàíèå
Введение 17
От издательства “Диалектика” 17
Глава 1. Предисловие 18
Что такое язык ассемблера 18
Прикладные программы на языке ассемблера 19
Машинный язык 20
Стр. 6
Архитектура памяти 47
Сегментированная модель памяти 48
Ввод&вывод 50
Резюме 51
Контрольные вопросы 51
Содержание 7
Стр. 7
Глава 4. Программирование для Windows 95
Программирование для консоли 95
Набор символов и функции Windows API 96
Уровни доступа 96
Типы данных Windows 97
Дескрипторы консоли 98
Функции консоли Win32 99
Использование функций 100
Ввод с консоли 104
Ввод одиночного символа 105
Структуры данных 106
Функция WriteConsoleOutputCharacter 106
Создание библиотеки загрузочных модулей 107
Подготовка модуля для библиотеки 108
Работа с файлами 112
Функция создания файла 112
Параметр DesiredAccess 112
Параметр CreationDisposition 112
Примеры работы с файлами 113
Функция CloseHandle 114
Функция ReadFile 114
Функция WriteFile 115
Пример записи в файл 115
Перемещение указателя файла 116
Пример чтения файла 117
Управление окном 118
Функция SetConsoleTitle 119
Функция GetConsoleScreenBufferlnfo 119
Функция SetConsoleWindowlnfo 120
Функция SetConsoleScreenBufferSize 121
Управление курсором 121
Управление цветом 121
Пример использования значений цвета 122
Функции времени и даты 123
Функции GetLocalTime и SetLocalTime 124
Функция GetTickCount 125
Функция Sleep 125
Процедура получения локального времени 125
Создание секундомера 126
Резюме 127
Контрольные вопросы 128
8 Содержание
Стр. 8
Представление строк символов 131
Оператор DUP 132
Объявление слов 132
Указатели 132
Формат перестановки в памяти 133
Объявление двойного слова 133
Символические константы 133
Директива равенства 133
Директива EQU 134
Директива TEXTEQU 134
Модели памяти 135
Выполняемые программы 136
Директивы установки типа процессора 137
Команды пересылки данных 138
Команда MOV 138
Контроль соответствия типов 139
Использование дополнительного смещения в операторах 140
Команда XCHG 140
Арифметические команды 141
Команды INC и DEC 141
Команда ADD 141
Команда SUB 142
Флаги, используемые в командах ADD и SUB 142
Операнды без знака 143
Знаковое переполнение 143
Tипы операндов 144
Регистровые операнды 144
Непосредственные операнды 145
Операнды с прямой адресацией 145
Оператор OFFSET 145
Операнды со смещением 145
Операторы и выражения 146
Арифметические операторы 149
Старшинство и ассоциативность операторов 149
Операторы OFFSET, SEG, PTR, LABEL и EVEN 150
Оператор OFFSET 150
Оператор SEG 151
Оператор PTR 151
Директива LABEL 151
Директивы EVEN и EVENDATA 152
Операторы типа и размера 152
Оператор SHORT 152
Оператор TYPE 152
Оператор LENGTH 153
Оператор SIZE 153
Команды JMP и LOOP 153
Команда JMP 154
Содержание 9
Стр. 9
Команда LOOP 154
Команды LOOP, LOOPW, LOOPD 155
Косвенная адресация 155
Косвенные операнды 155
Сегменты по умолчанию 156
Изменение значений по умолчанию 156
Использование команды LOOP для отображения строки 157
Использование команды LOOP для суммирования массива целых чисел 158
Базовые и индексные операнды 160
32&разрядные регистры 160
Базо&индексные операнды 160
Базо&индексные операнды с дополнительным смещением 161
Команды процессоров 80386 и более старших моделей 162
Команды MOVZX и MOVSX 162
Команда XADD 163
Процедуры 163
Резюме 164
Контрольные вопросы 165
10 Содержание
Стр. 10
Глава 7. Логические вычисления 188
Команды логических вычислений и сравнения 188
Команда AND 189
Команда OR 189
Команда XOR 191
Инверсия бит 191
Инверсия флагов клавиатуры 191
Команда NOT 191
Команда NEG 192
Команда TEST 192
Команды BT, BTC, BTR, BTS 192
Команды BSF и BSR 193
Команда CMP 193
Команда CMPXCHG 194
Логические структуры 195
Команда Jcond 196
Типы команд условного перехода 196
Генерация кодов для условных переходов 198
Примеры условных переходов 199
Проверка ввода букв 199
Наибольшее и наименьшее значения в массиве 200
Команда SETcond 201
Циклы с условием 202
Команды LOOPZ и LOOPE 202
Команды LOOPNZ и LOOPNE 202
Директивы для логических вычислений 203
Директива .IF 203
Директивы .REPEAT и .WHILE 205
Резюме 206
Контрольные вопросы 206
Содержание 11
Стр. 11
Директива RECORD 215
Оператор MASK 216
Оператор WIDTH 216
Сложение и вычитание больших чисел 217
Команда ADC 217
Команда SBB 218
Умножение и деление 219
Команда MUL 220
Команда IMUL 221
Команда DIV 221
Команда IDIV 222
Команды CBW, CWD, CDQ и CWDE 222
Кодировка ASCII и арифметика упакованных чисел 223
Команда AAA 224
Команда AAS 224
Команда AAM 224
Команда AAD 225
Команды DAA и DAS 225
Резюме 226
Контрольные вопросы 226
12 Содержание
Стр. 12
Директива IRP 243
Совмещение сдвигов и повторений 244
Директива IRPC 245
Резюме 245
Контрольные вопросы 246
Содержание 13
Стр. 13
ADD 277
AND 277
BOUND 277
BSF, BSR 277
BSWAP 278
BT, BTC, BTR, BTS 278
CALL 278
CBW 279
CDQ 279
CLC 279
CLD 279
CLI 279
CMC 279
CMP 280
CMPS, CMPSB, CMPSW, CMPSD 280
CMPXCHG 280
CWD 280
CWDE 281
DAA 281
DAS 281
DEC 281
DIV 281
ENTER 282
HLT 282
IDIV 282
IMUL 282
IN 283
INC 283
INS, INSB, INSW, INSD 283
INT 284
INTO 284
IRET 284
Jcondition 284
JCXZ, JECXZ 285
JMP 285
LAHF 285
LDS, LES, LFS, LGS, LSS 286
LEA 286
LEAVE 286
LOCK 286
LODS, LODSB, LODSW, LODSD 286
LOOP, LOOPW 287
LOOP 287
LOOPE, LOOPZ 287
LOOPNE, LOOPNZ 287
MOV 288
14 Содержание
Стр. 14
MOVS, MOVSB, MOVSW, MOVSD 304
MOVSX 304
MOVZX 304
MUL 305
NEG 305
NOP 305
NOT 305
OR 306
OUT 306
OUTS, OUTSB, OUTSW, OUTSD 306
POP 306
POPA, POPAD 307
POPF, POPFD 307
PUSH 307
PUSHA, PUSHAD 307
PUSHF, PUSHFD 308
PUSHW, PUSHD 308
RCL 308
RCR 308
REP 309
REPcondition 309
RET, RETN, RETF 309
ROL 309
ROR 310
SAHF 310
SAL 310
SAR 310
SBB 311
SCAS, SCASB, SCASW, SCASD 311
SETcondition 311
SHL 312
SHLD 312
SHR 312
SHRD 312
STC 313
STD 313
STI 313
STOS, STOSB, STOSW, STOSD 313
SUB 314
TEST 314
WAIT 314
XADD 314
XCHG 315
XLAT, XLATB 315
XOR 315
Содержание 15
Стр. 15
Библиотека функций для работы в 32&разрядном режиме 299
Подключаемый файл для работы с библиотекой функций 327
Подключаемый файл с макроопределениями 336
Перечень команд DOS для Windows XP 341
16 Содержание
Стр. 16
Введение
Данную книгу можно рассматривать как учебник, который позволит читателю понять
основы языка ассемблера и научиться создавать программы на этом языке. Для практиче&
ской работы необходимо использовать ассемблеры фирмы Microsoft, например MASM615
или другие, но при этом необходимо будет учитывать особенности этих ассемблеров. Хотя
во всех ассемблерах используется один и тот же базовый набор команд процессора, отличия
кроются в директивах, т.е. командах, которые исполняются самим ассемблером.
В книге в основном рассматривается 32&разрядный режим работы, т.е. используется
такой режим ассемблера, который позволяет обращаться к процедурам прикладного ин&
терфейса Windows. Приведены также некоторые сведения, специфичные только для 16&раз&
рядного режима, например, описание работы прерываний и понятие о DOS.
Программирование на языке ассемблера является низкоуровневым и позволяет обра&
щаться непосредственно к отдельным устройствам, поэтому данный язык более всего под&
ходит для разработки драйверов и таких программ, которым необходимо максимальное бы&
стродействие и код, учитывающий особенности работы отдельных устройств компьютера.
Книга является введением в ассемблер, поэтому в ней не отражены более расширен&
ные возможности языка ассемблера и последние команды, разработанные для новейших
версий процессоров. Такая задача и не ставилась, поскольку описать полностью возмож&
ности ассемблера невозможно в одной, даже очень объемной книге.
С целью сокращения объема книги в нее не включены полные тексты программ и много&
численные примеры &&&& такие программы можно в изобилии найти на специализированных
Internet&сайтах. Приведенные в книге примеры служат только для иллюстрации отдель&
ных тем и команд.
От издательства “Диалектика”
Вы, читатель этой книги, и есть главный ее критик. Мы ценим ваше мнение и хотим
знать, что было сделано нами правильно, что можно было сделать лучше и что еще вы хо&
тели бы увидеть изданным нами. Нам интересны любые ваши замечания в наш адрес.
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам бумажное или
электронное письмо либо просто посетить наш Web&сервер и оставить свои замечания там.
Одним словом, любым удобным для вас способом дайте нам знать, нравится ли вам эта книга,
а также выскажите свое мнение о том, как сделать наши книги более интересными для вас.
Отправляя письмо или сообщение, не забудьте указать название книги и ее авторов, а так&
же свой обратный адрес. Мы внимательно ознакомимся с вашим мнением и обязательно
учтем его при отборе и подготовке к изданию новых книг.
Наши электронные адреса:
E&mail: info@dialektika.com
WWW: http://www.dialektika.com
Стр. 17
Глава 1
Предисловие
Знание языка ассемблера поможет программисту не только научиться программиро&
вать, но также лучше понять взаимодействие языков высокого уровня с аппаратным
обеспечением, что в конечном итоге приводит к общему пониманию всего процесса
выполнения программы на компьютере и способствует более осмысленному использова&
нию конструкций языка высокого уровня. Язык ассемблера помогает раскрыть все секреты
аппаратного и программного обеспечения, с его помощью можно получить представле&
ние о том, как аппаратная часть взаимодействует с операционной системой и как при&
кладные программы обращаются к операционной системе. Язык ассемблера &&&& это язык
низкого уровня, в котором каждая команда непосредственно интерпретируется в коман&
ду процессора. Поэтому изучение языка ассемблера позволит понять все нюансы обра&
ботки информации в процессоре и поможет программисту в управлении процессором
и операционной системой, а также получении прямого доступа к аппаратуре.
Язык ассемблера незаменим при оптимизации критических блоков в прикладных
программах с целью повышения их быстродействия. Например, многие фрагменты ис&
ходных кодов языка Delphi написаны именно на языке ассемблера, что и определяет бы&
стродействие программ, разработанных на языке Delphi.
Язык ассемблера используется также при изучении курса аппаратного обеспечения
компьютера и операционных систем, когда есть возможность проводить эксперимен&
тальную работу.
Возможно, вам придется создавать обслуживающие программы (утилиты) или писать
драйверы для отдельных устройств. Во многих случаях это невозможно реализовать на
языках высокого уровня и только язык ассемблера позволит это сделать.
Стр. 18
отсутствует. 19
системе MS&DOS (или в режиме эмуляции MS&DOS в системах Windows), либо непосредст&
венно в Windows. Наиболее популярны для семейства процессоров Intel ассемблеры MASM
(Microsoft Assembler) и TASM (Borland Turbo Assembler). Язык ассемблера называется язы&
ком низкого уровня потому, что он очень близок к машинному языку по своей структуре
и выполняемым функциям. Каждая команда языка ассемблера имеет взаимно однозначное
соответствие с машинными командами, что отличает его от языков высокого уровня, для
которых характерно то, что один оператор транслируется во множество машинных команд.
Глава 1. Предисловие 19
Стр. 19
блок данных из файла, операционная система вызывает подпрограмму драйвера для уст&
ройства считывания.
Программируя на языке ассемблера, необходимо абсолютно точно представлять рас&
пределение данных, иначе ошибки неизбежны. Языки высокого уровня скрывают от про&
граммиста специфические детали для удобства использования и получения переносимого
кода. С другой стороны, используя язык ассемблера, можно учитывать специфику отдель&
ных устройств и почти не иметь ограничений при реализации интерфейса или драйвера.
Главный недостаток языка ассемблера состоит в том, что написанная для одного типа
компьютеров программа не может быть перекомпилирована и использована на других
типах компьютеров. Для каждого семейства процессоров используется свой язык ассемб&
лера со своим синтаксисом &&&& это следствие того, что язык ассемблера очень близок ма&
шинным командам и архитектуре процессора. Для того чтобы создаваемая программа
могла работать на различных типах компьютеров, необходимо использовать языки высо&
кого уровня, такие как С++, C#, Delphi или Java.
Машинный язык
Компьютер не воспринимает непосредственно команды на языке ассемблера, он по&
нимает только машинные команды. Машинные команды состоят из чисел, которые ин&
терпретируются процессором. Процессор обычно имеет встроенный интерпретатор,
представляющий микропрограмму и преобразовывающий машинные команды непосред&
ственно в управляющие сигналы.
Машинный язык включает примитивные операторы, которые могут исполнить про&
стые арифметические действия или переместить данные. Система команд процессора со&
стоит из машинных команд, которые он может исполнить. Машинный язык для семей&
ства процессоров Intel в своей основе имеет систему команд процессора 8086, которые
могут выполняться процессорами всех последующих модификаций. Такая концепция
называется совместимостью сверху вниз.
По мере совершенствования процессоров появляются новые команды, которые уже
не могут использоваться при написании программ для старых моделей. Такие команды
необходимо использовать для повышения скорости работы процессора и разработки
программ, учитывающих специфику современных аппаратных устройств.
Конечно, можно написать целую программу на машинном языке, вводя в память дво&
ичные числа машинных команд и заставляя процессор исполнять их. Но человеческий
мозг не приспособлен для запоминания большого количества чисел и поэтому вместо
ввода чисел используют мнемонически понятный набор команд — язык ассемблера, ко&
торый преобразует мнемонические коды в машинные команды.
20 Глава 1. Предисловие
Стр. 20
Глава 2
Архитектура компьютера
В этой главе...
Современные процессоры
Устройство компьютера
Операционные системы и память
Ввод&вывод
Резюме
Контрольные вопросы
Стр. 21
исключало их взаимное влияние. Операционная система DOS поддерживала работу
только в реальном режиме, но надстройки, подобные Microsoft Windows, уже могли ис&
пользовать защищенный режим.
В 1985 году Intel представила процессор 80386 с 32&разрядными регистрами, который мог
работать с 32&разрядной внешней шиной данных. Шина данных также стала 32&разряд&
ной, что позволило адресовать 4 Гбайта памяти. Появилась торговая марка Intel386. Бо&
лее дешевый процессор 80386&SX имел 16&разрядную шину данных.
Процессор 80386 мог работать в трех режимах: реальном, защищенном и виртуальном.
Это позволяло на одном процессоре запускать несколько программ реального режима
как отдельные ‘‘виртуальные машины’’. Другими словами, стандартные приложения
DOS могли работать одновременно, причем считалось, что каждое из них имеет доступ
ко всей памяти объемом в 1 Мбайт.
Благодаря схеме адресации памяти, названной виртуальной, вся используемая процес&
сором память могла превышать физический размер памяти компьютера. Процессор авто&
матически перемещал содержимое временно не используемых участков памяти на жесткий
диск и предоставлял эту память другим программам. Именно этот процессор помог опера&
ционной системе Microsoft Windows 3.0 достичь грандиозной популярности. Операционная
система Windows предоставляла пользователям возможность работать как в защищенном,
так и в виртуальном режимах. В виртуальном режиме в отличие от операционной системы
DOS можно было запускать несколько больших приложений одновременно.
Семейство процессоров Intel486 включало в себя процессоры 486DX, 486DX2 и 486SX.
В архитектуре процессора были использованы элементы высокопроизводительных про&
цессоров RISC. Это связано с тем, что изначально применяемая с процессорами Intel сис&
тема команд была довольно неудобной для обработки в процессоре, так как имела слож&
ную структуру, а отдельные команды имели разную длину (от одного до шестнадцати байт).
Но отказаться от этой системы команд Intel уже не могла, и поэтому был использован
блок преобразования в более удобные команды RISC. Последовательность выполнения
команд была изменена, режимы декодирования и исполнения могли работать одновре&
менно, что позволяло выполнять многие команды всего за один такт. Это была первая
серия процессоров, у которых блок вычислений с плавающей запятой (математический
сопроцессор) находился с ядром процессора на одном кристалле, что и обеспечило значи&
тельный прирост производительности. Процессор Intel486 имел внутреннюю кэш&память
первого уровня объемом 8 Кбайт, в которой хранились последние используемые коман&
ды. К ним обеспечивался очень быстрый доступ. В более дешевом Intel486SX был отклю&
чен блок вычислений с плавающей запятой.
Дальнейшим шагом Intel был процессор Pentium. Согласно проведенным тестам, про&
изводительность процессора Pentium с тактовой частотой 90 МГц почти вдвое превышала
производительность процессоров Intel486. Pentium мог выполнять больше одной команды
за такт, потому что в нем использовалась суперскалярная архитектура с двумя конвейерами
для команд. Другими словами, одновременно декодироваться и выполняться могли сразу
две команды. Конвейеры были надежным и испытанным способом повышения произво&
дительности, так как они уже давно использовались в процессорах архитектуры RISC для
рабочих и графических станций. Способствовали успеху Pentium и такие особенности:
встроенная кэш&память для команд и данных объемом по 8 Кбайт (486 имел толь&
ко одну кэш&память);
улучшенный блок вычислений с плавающей запятой;
предсказание ветвлений позволяло процессору Pentium анализировать последователь&
ность команд. Когда встречались команды условия (например, цикл или команда IF),
Стр. 22
отсутствует. 23
Ядро процессора
Не будем вдаваться в тонкости построения процессоров, хотя эта тема и интересна сама
по себе. Для программиста важно знать только общий принцип работы процессора и те его
элементы, к которым можно обратиться в процессе написания программы. На рис. 2.1 по&
казан блок управления, который распределяет задания и выбирает данные из памяти, ариф&
метико&логический блок, где непосредственно и происходят вычисления, и регистры, в кото&
рых хранятся все необходимые данные для оперативного выполнения очередной команды.
Стр. 23
рассчитывается адрес;
выбираются операнды из памяти.
Исполнение команд:
выполняются необходимые расчеты;
сохраняются результаты в памяти или регистрах;
устанавливаются необходимые состояния флагов.
Подобно системной шине, в процессоре существует внутренняя шина, которая пред&
ставляет собой множество параллельных проводников, связывающих отдельные блоки
процессора для обмена данных между ними. Если данные должны быть считаны из
внешней памяти, блок управления рассчитывает их адрес и устанавливает его на внеш&
ней шине адреса, являющейся частью системной шины. Контроллер памяти считывает
адрес с шины адреса и размещает затребованные данные на шине данных, также входящей
в состав системной шины. Затем он устанавливает сигнал готовности на одной из линий
контрольных сигналов. Процессор проверяет наличие сигнала готовности на этой линии
и после его появления считывает данные во внутреннее устройство хранения. Обращение
к внешней памяти &&&& самый длительный процесс относительно остальных этапов обра&
ботки данных в процессоре. Системная шина и память уже давно стали узким местом при
повышении общей производительности компьютера. В первых процессорах даже вводи&
ли специальные холостые циклы процессора при ожидании данных из внешней памяти.
В настоящее время эти проблемы решают за счет усложнения конструкции компьютера
и процессора, устанавливая более быстродействующую и, соответственно, более дорогую
внешнюю память поближе к ядру процессора. Емкость такой памяти не может быть
очень большой, так как это грозит значительным повышением стоимости компьютера,
поэтому такая память является буферной между основной памятью и процессором, со&
храняя только небольшое количество команд, необходимых процессору для оперативной
работы. Это позволяет значительно снизить общее время обращения к памяти и поднять
производительность компьютера. Такую память называют кэш%памятью. Разработчики
могут установить несколько уровней кэш&памяти, оптимизируя стоимость и производи&
тельность компьютера, но самыми быстродействующими участками памяти являются
регистры, которые расположены непосредственно на ядре процессора.
Регистры &&&& это участки высокоскоростной памяти для хранения данных в процессо&
ре. Они непосредственно подключены к блоку управления и арифметико&логическому
устройству, поэтому доступ к регистрам из этих блоков происходит значительно быстрее,
чем доступ к внешней памяти. Команды, использующие только обращение к регистрам,
выполняются очень быстро, в отличие от команд, требующих получения операндов из
внешней памяти, что необходимо учитывать при программировании, если программа
должна работать максимально быстро.
Каждая отдельная операция, которая выполняется внутри процессора, должна быть
синхронизирована с работой остальных устройств. Отдельное устройство синхронизации
вырабатывает все тактовые импульсы, необходимые для работы процессора. Тактовая
частота у современных процессоров может достигать нескольких гигагерц.
Программируемые регистры
Программист может не знать все детали функционирования процессора, но понимать
назначение и роль регистров в процессе обработки информации он обязан, так как
именно регистры играют основную роль при обработке отдельных данных и выполнении
Стр. 24
отсутствует. 25
Стр. 25
31 15 0 Номера битов для 32разрядных регистров
Регистры данных
EAX AH AL Аккумулятор
EBX BH BL Базовый регистр
EDX CH CL Счетчик
ECX DH DL Регистр данных
Регистры сегментов
CS Регистр сегмента команд
DS Регистр сегмента данных
SS Регистр сегмента стека
ES Регистр дополнительного сегмента
FS Регистр дополнительного сегмента
GS Регистр дополнительного сегмента
79 Регистры сопроцессора 0
ST(0)
ST(1)
ST(2)
ST(3)
ST(4)
ST(5)
ST(6)
ST(7)
Стр. 26
отсутствует. 27
Стр. 27
Флаг прерывания IF устанавливается, если возникает системное прерывание. Сис&
темные прерывания могут быть произведены различными устройствами, например кла&
виатурой, драйверами дисков или системным таймером. В некоторых программах иногда
необходимо запретить выполнение прерываний на период выполнения критичных по вре&
мени процедур. Флаг считается установленным, если соответствующий бит равен 1, и сбро&
шенным, если бит равен 0. Состояние флага изменяется командами CLI и STI.
Флаг трассировки TF заставляет процессор останавливаться после выполнения каждой
команды, что обычно используется при пошаговой отладке программы. Когда флаг уста&
новлен, программа отладки позволяет программисту последовательно просматривать выпол&
нение команд (трассировка). Флаг установлен, если бит равен 1, и сброшен при нулевом бите.
Современные процессоры
Несмотря на то что система команд первых процессоров Intel в своей основе не изме&
нялась до настоящего времени и идеология обработки команд также не претерпела суще&
ственных перестроек, тем не менее производительность современных процессоров зна&
чительно возросла и обычный настольный компьютер сейчас имеет такую же
производительность, какую десяток лет назад имели только отдельные суперкомпьюте&
ры. Росту производительности работы процессора способствовало то, что разработчики
предпринимали усилия по повышению скорости обработки данных сразу по нескольким
направлениям. Во&первых, улучшение технологического процесса производства микросхем
Стр. 28
отсутствует. 29
32*разрядные процессоры
Все процессоры Intel, начиная с 80386, имеют 32&разрядные регистры (см. рис. 2.2). Это
означает, что все программы, рассчитанные на эти процессоры, могут оперировать 32&
разрядными данными и использовать регистры индексов для адресации 4 Гбайт памяти.
Для работы с 32&разрядным форматом были добавлены новые команды.
Регистры сегментов остались 16&разрядными, но были добавлены два новых регистра &&&&
FS и GS. Не получили условного обозначения и верхние половины 32&разрядных регистров.
Для того чтобы разместить значение в верхней половине регистра, необходимо сначала
разместить это значение в нижней половине (например AX) и применить команду сдвига.
Немного подробнее рассмотрим регистры состояния и управления, так как в послед&
нее время в основном используются 32&разрядные процессоры, содержащие, соответст&
венно, 32&разрядные регистры флагов. При программировании нужно будет обращаться
именно к этим флагам.
Стр. 29
Регистры состояния и управления 32разрядного процессора
В 32разрядный процессор включены несколько регистров, которые постоянно со
держат информацию о состоянии как самого процессора, так и программы, команды ко
торой в данный момент загружены на конвейер.
К этим регистрам относятся:
регистр флагов EFLAGS;
регистр указателя команды EIP.
Используя эти регистры, можно получать информацию о результатах выполнения ко
манд и влиять на состояние самого процессора. Рассмотрим подробнее назначение и со
держимое этих регистров.
Регистр флагов EFLAGS
Регистр флагов EFLAGS содержит 32 разряда. Отдельные биты данного регистра име
ют определенное функциональное назначение и называются флагами. Младшая часть
этого регистра полностью аналогична регистру FLAGS для процессоров 8086. На рис. 2.3
показано содержимое регистра EFLAGS.
Флаг переноса
Флаг четности
Флаг вспомогательного переноса
Флаг нуля
Флаг знака
Флаг направления
Флаг переполнения
Флаг привелигерованности вводавывода
Флаг вложенности задач
31 ... 21 20 11 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 ... ID VIP VIF AC VM RF 0 NT IOPL OF DF IF TF SF ZF 0 AF 0 PF 1 CF
Флаг трассировки
Флаг прерывания
Флаг возобновления
Флаг виртуального 8086
Флаг контроля выравнивания
Флаг виртуального прерывания
Флаг отложенного прерывания
Флаг идентификации процессора
Стр. 30
отсутствует. 31
Стр. 31
Окончание табл. 2.1
Флаг Описание Бит Состояние и назначение
VM Флаг режима 17 Признак работы процессора в режиме виртуального 8086:
виртуального 8086 1 &&&& процессор работает в режиме виртуального 8086;
0 &&&& процессор работает в реальном или защищенном режиме
АС Флаг контроля 18 Предназначен для разрешения контроля выравнивания при
выравнивания обращениях к памяти. Используется совместно с битом AM
в системном регистре CR0. К примеру, Pentium разрешает размещать
команды и данные с любого адреса. Если требуется контролировать
выравнивание данных и команд по адресам, кратным 2 или 4,
то установка этих битов приведет к тому, что все обращения по
некратным адресам будут генерировать исключительную ситуацию
VIF Флаг виртуального 19 При определенных условиях (одно из которых &&&& работа процессора
прерывания в V&режиме) является аналогом флага IF. Флаг VIF используется
совместно с флагом VIP. Флаг появился в процессоре Pentium
VIP Флаг отложенного 20 Устанавливается в 1 для индикации отложенного прерывания.
виртуального Используется при работе в V&режиме совместно с флагом VIF.
прерывания Флаг появился в процессоре Pentium
ID Флаг 21 Используется для того, чтобы показать факт поддержки процессором
идентификации команды CPUID. Если программа может установить или сбросить
этот флаг, то это означает, что данная модель процессора
поддерживает инструкцию CPUID
64*разрядные процессоры
Новое семейство процессоров Intel &&&& Itanium &&&& рассчитано на использование в вы&
сокопроизводительных серверах. Для того чтобы обеспечить резкое повышение произво&
дительности, необходимой в серверах, процессоры Itanium были построены по новому
архитектурному принципу, обозначенному как явно параллельные выполнения команд
(EPIC &&&& Explicitly Parallel Instruction Computing). Система команд дополнена новыми
командами для 64&разрядных процессоров, причем совместимость с системой команд
для 32&разрядных процессоров сохранена. Применение 32&разрядных команд также по&
зволяет использовать преимущества новой архитектуры процессора и получать более бы&
стродействующие программы.
Процессоры AMD
Фирма AMD выпускает несколько семейств процессоров, которые программно
совместимы с семейством х86 и имеют логотип, указывающий на совместимость с Windows.
Однако их необходимо устанавливать только в те системные платы, в описании которых
есть явное указание на их поддержку. В противном случае возможны сбои в работе.
Стр. 32
отсутствует. 33
Устройство компьютера
Для прикладного программиста необязательно глубоко знать архитектуру компьюте&
ра, но знание базовых принципов работы компьютера и взаимодействие его основных
частей необходимо. Без таких знаний невозможно написать даже простейшую програм&
му, для чего потребуется организовать ввод и вывод информации, т.е. необходимо обра&
щаться к периферийным устройствам, таким как клавиатура и монитор. А поскольку без
аппаратных устройств не обойтись, надо иметь представление о том, как они работают,
и что нужно для того, чтобы данные обрабатывались именно так, как они должны обра&
батываться. Устройство компьютера описано в следующих разделах.
Стр. 33
Внутренние компоненты
Системная (материнская) плата &&&& основной компонент любого компьютера. Имен&
но на этой плате располагаются процессор, системный набор микросхем для обслужива&
ния процессора, системная память, соединители ввода&вывода, разъемы для подключения
питания и разъемы расширения. Все компоненты соединяются друг с другом при помо&
щи системной шины, которые упрощенно можно представить как ряд параллельных
проводников, вытравленных в проводящем слое платы и соединяющих отдельные эле&
менты платы. Однако это очень упрощенное объяснение, так как современные шины
представляют собой помимо проводников еще и управляющие микросхемы. И если в пер&
вых компьютерах можно было рассматривать системную шину как ряд параллельных на&
прямую подключенных к процессору проводников, где всеми сигналами, проходящими
по шине, управляет процессор, то к современным системным платам это не имеет ника&
кого отношения. Современную системную шину необходимо рассматривать как отдель&
ное устройство, которое может работать независимо от процессора, хотя в любом случае
общее управление всей системой осуществляет процессор, и только процессор решает,
в каком режиме будет работать отдельное устройство.
Системные платы, независимо от компании&производителя, имеют стандартный набор
функций, отличаясь лишь производительностью и возможностями модификации. Совре&
менные платы обязательно имеют базовый набор соединителей, который включает в себя:
панельку для установки процессора (она имеет различные параметры в зависимо&
сти от типа процессора);
разъемы для установки системной памяти (модули памяти SDRAM, DDRAM или
DDR);
разъем для микросхемы BIOS (часто возникает необходимость в модернизации BIOS,
которая раньше производилась путем смены микросхемы ROM; в настоящее время
все микросхемы BIOS можно перепрограммировать электрически и обычно эти
микросхемы впаяны в плату);
соединители для подключения гибких и жестких дисков, а также CD&ROM приводов;
стандартные соединители для подключения клавиатуры, мышки, джойстика, ви&
деоустройств, принтера и устройств связи.
Шинная архитектура
Как уже отмечалось, обмен информацией между отдельными устройствами на сис&
темной плате происходит через шину. В современном компьютере шина представляет со&
бой отдельный распределительный узел, к которому подключаются все (как внутренние,
так и внешние) устройства. Во многом именно от грамотного построения шины зависит
производительность компьютера. Шинная архитектура реализуется с помощью специ&
ального набора микросхем, называемого системным набором.
В первых компьютерах IBM PC использовалась 8&разрядная шина для передачи дан&
ных между процессором и другими компонентами компьютера. Скорость передачи состав&
ляла около одного миллиона битов в секунду, что позволяло процессору 8088 работать
без задержек. Однако с увеличением производительности процессоров такой скорости
для работы шины оказалось недостаточно.
Шина PC AT, представленная IBM для работы с процессором Intel 80286, имела
16 разрядов для передачи данных и 24 разряда для передачи адреса. С увеличением такто&
вой частоты процессора увеличилась и частота работы шины до 8 МГц.
Стр. 34
отсутствует. 35
В настоящее время широко распространена шина PCI, которая была разработана в 1992 году
компанией Intel для совместной работы с процессорами Pentium, требующих высокой ско&
рости передачи данных. Эта шина до сих пор остается доминирующей во всех системах, ис&
пользующих процессоры Intel. Спецификация шины учитывает особенности как 32&раз&
рядных, так и на 64&разрядных системных плат. Диапазон тактовой частоты работы шины
может быть в пределах 25&&1000 МГц, обеспечивая скорость передачи до 500 Мбит в секун&
ду. Во всех системных платах используется мост для перехода от внутренней 64&разрядной
шины процессора к внешней 64&разрядной шине. На смену шине PCI приходит шина PCI
Express, характеристики которой значительно превышают возможности шины PCI и кото&
рая будет основной шиной компьютеров в течение нескольких ближайших лет.
Видеоадаптер и монитор
Видеоадаптер рассчитан на совместную работу с дисплеями по спецификации IBM
и состоит из двух основных частей: видеоконтроллера и видеопамяти. Видеоадаптер мо&
жет быть в виде отдельной платы, вставляемой в разъем расширения, или может быть
расположен непосредственно на системной плате. Здесь необходимо уточнить некоторые
терминологические тонкости. Видеоадаптером можно считать простую графическую
плату, которая рассчитана на воспроизведение как двухмерных, так и трехмерных изо&
бражений. Однако при создании трехмерного изображения графическая плата не может
обойтись без процессора, загружая его расчетами каркаса изображения и занимая значи&
тельную часть процессорного времени. Более совершенные графические платы называют
графическими ускорителями. Такие платы почти не обращаются к процессору при рас&
чете трехмерной сцены, где они учитывают все источники света, определенные в сцене,
и создают очень реалистичные световые и цветовые переходы.
Все символы и графические изображения перед отображением на дисплее должны
быть записаны в видеопамять, откуда они и посылаются на дисплей видеоконтроллером.
Видеоконтроллер, или видеопроцессор, &&&& это процессор, который берет на себя всю ра&
боту с видеоустройствами, освобождая от этих обязанностей центральный процессор.
Обычно видеоадаптер содержит не менее 4 Мбайт видеопамяти и оптимизирован для ра&
боты с двухмерными или трехмерными графическими изображениями. Современные ви&
деоадаптеры рассчитаны на работу не менее чем с 16 млн цветов и обеспечивают разре&
шение экрана не менее 1024×768. Эти характеристики постоянно улучшаются.
Что же касается мониторов, то для получения изображения в них используется техно&
логия, называемая растровым сканированием. Электронный луч создает на экране све&
тящиеся точки &&&& пиксели. Электронная пушка управляет лучом, постоянно передвигая
его слева направо и сверху вниз, проходя через все точки экрана. Достигнув конца самой
нижней строки, электронный луч перескакивает обратно в верхний левый угол, завершая
один цикл, называемый кадром. Для уменьшения мерцания экрана используют метод
чересстрочной развертки, когда луч пробегает за одну половину кадра только четные
строки, а за вторую &&&& нечетные, хотя в современных компьютерах этот метод почти не
используется и развертка всегда является прогрессивной, или последовательной.
Современные плоские мониторы используют другой принцип отображения инфор&
мации на экране. Мельчайшая сетка экрана состоит из жидких кристаллов, которые под
воздействием электрических сигналов могут пропускать или задерживать свет. Используя
это свойство жидких кристаллов, можно применить решетку из красных, синих и желтых
ячеек для создания цветного изображения.
Есть и другие принципы создания изображения на плоском экране, но в данном слу&
чае это не принципиально. Поговорим о характеристиках монитора.
Стр. 35
Качество монитора определяют несколько факторов. Это величина шага между распо&
ложенными рядом точками. В современных мониторах эта величина не превышает 0,26 мил&
лиметра и чем она меньше, тем лучше. Также важны скорость вертикальной и горизонтальной
разверток. Горизонтальная развертка определяется временем пробега луча по одной линии
(от левого края до правого), а скорость вертикальной развертки определяет полное время
пробега всех линий. Если скорость вертикальной развертки будет меньше, чем 70 кадров в се&
кунду, то изображение на экране будет мерцать при смене кадра, что вредно для глаз.
Скорость вертикальной развертки и разрешение экрана связаны обратно&пропорцио&
нальной зависимостью. Если увеличить разрешение, то может случиться так, что монитор
или видеоконтроллер не смогут поддерживать высокую скорость вертикальной развертки.
Разрешение экрана можно изменять программно, но оно ограничено возможностями
видеоконтроллера и объемом видеопамяти. Разрешение определяется количеством пик&
селей, размещаемых по горизонтали и вертикали. Стандартным значением для режима
VGA будет 640 пикселей по горизонтали и 480 по вертикали (640×480), для режима super
VGA &&&& 800×600, для extended VGA &&&& 1024×768, 1152×864, 1280×1024 и 1600×1400.
Количество одновременно отображаемых цветов может быть от 256 до 16 млн.
Память
В компьютерах используются три основных типа памяти: динамическая RAM, стати%
ческая RAM и видео RAM. Динамическая память должна обновляться не меньше, чем раз
в миллисекунду, иначе она потеряет свое содержимое, так как электроны постоянно
‘‘просачиваются’’ из созданного для них кармана. Статическая память не требует обнов&
ления. Видеопамять используется только для хранения данных, предназначенных для
отображения на дисплее. CMOS RAM предназначена для хранения информации о сис&
темных установках и для сохранения в ней информации при выключенном компьютере
используется специальная батарейка.
Основная используемая память является динамической памятью, так как она самая деше&
вая. Ее часто называют системной, или оперативной памятью. В некоторых системах исполь&
зуют память с проверкой ошибок. Такая память способна определять ошибки в нескольких
битах и корректировать ошибки в одном бите каждого байта, но это приводит к некоторому
удорожанию системы и в настоящее время в обычных компьютерах динамическая память
не используется. Это связано также и с тем, что сбои при передаче достаточно редки.
Обычно процессор работает значительно быстрее, чем оперативная память, поэтому
разработчикам приходится усложнять конструкцию для того, чтобы согласовать работу
процессора и памяти без потери производительности. Частично эту проблему решает вы&
сокоскоростная кэш&память первого уровня, включенная в состав процессоров Intel на&
чиная с модели 80386. Для более лучшего согласования используют кэш&память второго
уровня, которая подключается непосредственно к шине процессора. Ее объем обычно
составляет не менее 512 Кбайт. В этой памяти сохраняются недавно используемые ко&
манды и данные. Например, если встречаются циклические операции, то процессору со&
всем не придется обращаться к медленной основной оперативной памяти.
Стр. 36
отсутствует. 37
Платы расширения
Платы расширения вставляются в разъемы расширения, размещенные на системной
или специальной разработанной плате, конструкция которой позволяет платам занимать
меньше места. На платах расширения обычно размещают:
графический ускоритель;
звуковую плату с портом для игровых устройств;
синтезатор звука;
контроллер SCSI;
контроллер ленточного накопителя;
устройство видеозахвата;
дополнительные параллельные и последовательные порты.
В полноразмерных конструкциях типа ‘‘башня’’ может быть от пяти до восьми разъе&
мов расширения и несколько отсеков для приводов, что позволяет подключать дополни&
тельные устройства. Массовые настольные компьютеры имеют меньше разъемов расши&
рения, так как часть функций реализована непосредственно на системной плате или
встроена в системный набор микросхем. Такое решение позволяет разрабатывать не&
большие и недорогие компьютеры.
Процессоры поддержки
Почти все системные платы имеют в своем составе системный набор микросхем,
включающий в себя две или три микросхемы с процессорами, которые выполняют от&
дельные функции (некоторые из них перечислены ниже).
Контроллер прямого доступа к памяти соединяет внешние устройства непосредст&
венно с памятью без использования центрального процессора.
Контроллер прерываний передает запросы на обслуживание от аппаратуры в цен&
тральный процессор.
Счетчик времени управляет системными часами, которые обновляются с частотой
18,2 раза в секунду.
Контроллер моста для перехода от локальной шины к шине PCI или PCIE.
Контроллер системной памяти и кэш&памяти.
Контроллер для работы с клавиатурой и мышкой.
BIOS
ROM BIOS &&&& это память только для чтения, которая содержит программы и данные,
не подлежащие стиранию или изменению. ROM BIOS можно рассматривать как часть
операционной системы. Когда компьютер включается, то процессор обращается непо&
средственно к BIOS, так как только в ней есть рабочие программы, все остальное еще
Стр. 37
предстоит загрузить в оперативную память с жесткого диска. Это программы контроля
аппаратной части и программы загрузки части операционной системы из загрузочного
сектора диска. Большинство систем копируют BIOS в более быстродействующую сис&
темную память на момент загрузки.
Наиболее важная особенность ROM BIOS &&&& возможность проверять наличие аппа&
ратных устройств и готовить операционную систему для работы с устройствами в соот&
ветствии с информацией, полученной от них. Никакой дополнительной настройки уст&
ройств при этом не требуется, поэтому такой режим назвали ‘‘подключи и работай’’.
Современные операционные системы не всегда доверяют созданным BIOS установкам
и сами производят конфигурацию отдельных или всех устройств.
Порты
Параллельные порты. Большинство принтеров подключаются к компьютеру через па&
раллельный порт. Слово ‘‘параллельный’’ означает, что 8 битов могут одновременно пе&
редаваться от компьютера к принтеру по восьми параллельным проводникам, но на не&
большие расстояния (обычно не более 10 метров). При этом данные могут передаваться
очень быстро. В компьютере имеется три параллельных порта: LPT1, LPT2 и LPT3, кото&
рые могут быть двунаправленными, позволяя компьютеру принимать информацию от
принтера о его состоянии.
Последовательные порты. Последовательные порты также называются интерфейсами
RS&232. Последовательные порты используются для подключения мышки, модема или
некоторых других устройств. Микросхема, которая управляет последовательными порта&
ми, обозначается как 16550 UART и может находиться на системной плате или плате
расширения.
В таких портах каждый бит посылается последовательно один за другим, что не спо&
собствует высокой скорости передачи, но зато становится возможной передача на большие
расстояния. Операционные системы поддерживают два последовательных порта COM1
и COM2, у которых разные типы соединителей: в одном 9 контактов, а в другом &&&& 25.
В связи с развитием технологической базы стало возможным обеспечивать большие
скорости передачи по последовательным портам. Поэтому в современных компьютерах
происходит замена параллельных портов на более удобные, позволяющие получить вы&
сокое быстродействие последовательные порты, такие как SATA или USB. Эти порты
можно использовать для подключения периферийных устройств любого типа.
Порты USB. Наиболее современные порты для ввода и вывода. Они обеспечивают
высокую скорость передачи данных и позволяют унифицировать ввод&вывод периферийных
устройств. В настоящее время наблюдается все более активный переход на порты USB,
которые в конечном итоге должны заменить все остальные порты. Порт USB помимо вы&
сокой скорости передачи предоставляет и ряд других преимуществ, среди которых удоб&
ство подключения и автоматическое распознавание вновь подключенного устройства.
Работа компьютера
Теперь, когда вы имеете общее представление о различных устройствах компьютера,
схематично рассмотрим совместную работу этих устройств. Более подробно работу от&
дельных устройств будем рассматривать в соответствующих разделах.
Итак, начнем с самого начала. В момент включения компьютера процессор обращается
к участку памяти, которую занимает BIOS, так для работы процессора нужна программа,
которую в начальный момент можно получить только из энергонезависимой памяти
BIOS. Оперативная память еще ничем не заполнена и в начальный момент неизвестно,
Стр. 38
отсутствует. 39
Стр. 39
система делает работу с компьютером такой простой и удобной, при этом она эффектив
но использует ресурсы компьютерной системы. Как и любая другая программа, операци
онная система состоит из команд, которые считывает процессор. Уникальностью этой
программы является ее назначение операционная система позволяет процессору ис
пользовать все доступные системные ресурсы и распределять время при исполнении
других программ. Однако для того, чтобы компьютер мог выполнять полезную работу,
операционная система должна периодически переключать процессор на выполнение при
кладных программ. Таким образом, операционная система передает управление другой
программе, чтобы процессор смог выполнить заданную пользователем работу, а затем во
зобновляет контроль над системой для подготовки процессора к следующей работе.
На рис. 2.4 показаны основные ресурсы, которыми управляет операционная система.
Память
Контроллер Устройство Принтер
Операционная вводавывода вводавывода Клавиатура
система Модем
Монитор
Контроллер Устройство USB
вводавывода вводавывода ...
Внешняя
память
Программы
пользователей Операционная
и данные система
Программы
Контроллер Устройство
вводавывода вводавывода Данные
Процессор
Стр. 40
отсутствует. 41
когда в Советский Союз начали поступать первые персональные компьютеры, они не мог&
ли отображать русские буквы, так как операционная система не имела соответствующих
шрифтов. Но очень скоро наши программисты ‘‘научили’’ компьютеры понимать русский
язык. Специальные программы&русификаторы встраивались в систему при инициализации
компьютера и позволяли отображать и вводить русские символы.
Здесь нет ничего удивительного, поскольку операционную систему можно изменять как
и любую другую программу. Но лучше этого не делать, так как, вероятнее всего, вы не смо&
жете улучшить то, что создавалось большими коллективами высококлассных программи&
стов. Тем более что исходные коды операционных систем закрыты и разработчики обыч&
но их никому не предоставляют. Исключение составляет операционная система Linux,
в поставку которой входят исходные коды.
Стр. 41
Эволюция операционных систем
В первых компьютерах в конце 40&х и до середины 50&х годов прошлого века про&
граммы непосредственно взаимодействовали с аппаратным обеспечением машины &&&&
операционных систем тогда еще не было. Эти компьютеры управлялись с пульта управ&
ления, состоящего из сигнальных ламп, тумблеров, устройства для ввода данных и прин&
тера. Программы в машинных кодах и данные загружались через устройство ввода дан&
ных (например, устройство ввода с перфокарт). Если из&за ошибки выполнение
программы прекращалось, о возникновении сбойной ситуации сообщали аварийные
сигнальные лампы. Чтобы определить причину ошибки, программист вручную должен
был проверить состояние регистров процессора и основной памяти. Если программа ус&
пешно завершала свою работу, ее выходные данные распечатывались на принтере. Такой
режим работы можно назвать последовательной обработкой данных. Это название отра&
жает тот факт, что пользовательские программы исполнялись на компьютере последователь&
но. Все это сопровождалось массой неудобств, а выполнение пользовательской программы
занимало слишком много времени. Для устранения этих недостатков были разработаны
различные системные инструментальные программы. К ним относятся библиотеки функ&
ций, редакторы связей, загрузчики, отладчики и драйверы ввода&вывода, существующие
в виде программного обеспечения, общедоступного для всех пользователей.
В дальнейшем для повышения эффективности работы была предложена концепция
пакетной операционной системы. Первые пакетные операционные системы для машин
типа IBM 701 были разработаны в середине 50&х годов компанией General Motors. Впо&
следствии эта концепция была усовершенствована и внедрена на машинах серии
IBM 704. В начале 60&х годов некоторые поставщики разработали пакетные операцион&
ные системы для своих компьютеров. Одна из примечательных систем того времени &&&&
IBSYS фирмы IBM, разработанная для компьютеров 7090/7094 и оказавшая значитель&
ное влияние на другие системы.
Главная идея, лежащая в основе простых пакетных схем обработки, состоит в исполь&
зовании программы, известной под названием монитор. Используя операционную сис&
тему такого типа, пользователь не имеет непосредственного доступа к машине. Вместо
этого он передает свое задание на перфокартах или магнитной ленте оператору компью&
тера, который собирает разные задания в пакеты и помещает их в устройство ввода дан&
ных. Так информация передается монитору. Каждая программа составлена таким образом,
что по завершении ее работы управление переходит к монитору, который автоматически
загружает следующую программу. Задания в пакетах выстраиваются в очередь и выпол&
няются без простоев максимально быстро. Кроме того, монитор помогает в подготовке
программы к исполнению. В каждое задание включаются простые команды языка управ&
ления заданиями. Это специальный тип языка программирования, используемый для
того, чтобы отдавать команды монитору. При этом монитор обеспечивал защиту памяти
и во время работы программа пользователя не могла внести изменения в область памяти,
в которой находился сам монитор. Монитор использовал таймер для снятия программ,
превысивших выделенное для них время работы; только он мог выполнять некоторые
привилегированные команды машинного уровня. Например, команды ввода&вывода
контролировались монитором, и если программе пользователя нужно было произвести
ввод&вывод, ей приходилось запрашивать для выполнения этих операций монитор. Но
в первых операционных системах отсутствовали прерывания, которые придают системе
большую гибкость при управлении ресурсами процессора.
Занимая часть ресурсов компьютера, монитор существенно повышал эффективность
его использования, что с учетом дорогостоящих компьютеров и дорогого процессорного
Стр. 42
отсутствует. 43
Стр. 43
Современные операционные системы
Операционные системы относят к числу самых сложных программных комплексов.
Это связано со стремлением разработчиков сделать системы максимально удовлетво&
ряющие требованиям удобства работы и эффективности, но при этом ОС не должны ут&
ратить способности к дальнейшему совершенствованию. В процессе развития операци&
онных систем были проведены исследования в нескольких основных направлениях.
Одной из главных концепций, лежащих в основе операционных систем, является
концепция процессов. Этот термин впервые был применен в 60&х годах и с тех пор широ&
ко используется. В разных операционных системах под процессом понимают несколько
отличающиеся действия операционных систем. Фактически процесс можно разделить на
три компонента:
выполняемая программа;
данные, нужные для ее работы (переменные, рабочее пространство, буферы и т.д.);
состояние программы.
Очень важен компонент состояния программы, который включает в себя всю инфор&
мацию, нужную операционной системе для управления процессом, и процессору &&&& для
его выполнения. Данные, характеризующие это состояние, включают в себя содержимое
различных регистров процессора, таких как программный счетчик и регистры данных.
Сюда же входит информация, используемая операционной системой (приоритет процес&
са и сведения о том, находится ли данный процесс в состоянии ожидания какого&то со&
бытия, связанного с вводом&выводом).
Таким образом, процесс реализуется в виде структуры данных. Он может выполнять&
ся или находиться в состоянии ожидания. Состояние процесса в каждый момент времени
заносится в специально отведенную область данных. Использование структуры позволя&
ет развивать мощные методы координации и взаимодействия процессов.
Четкое обоснование понятия процесса позволило решить проблемы, возникающие на
этапе развития операционных систем и связанных с распределением времени и синхро&
низацией.
Были разработаны системы групповой обработки нескольких программ, при этом
процессы могли запускаться в режиме разделения времени и транзакций одновременно.
В ответ на сигналы, сообщающие о завершении транзакций ввода&вывода, процессор пе&
реключается с одной программы на другую.
Следующим направлением развития являются системы разделения времени. Основ&
ная цель их разработки &&&& удовлетворение потребностей каждого пользователя при усло&
вии их одновременной работы на компьютере. В этих системах учитывается тот факт, что
пользователь реагирует на события намного медленнее, чем компьютер.
Еще одним важным направлением развития являются системы обработки транзакций
в реальном времени. При этом необходимо учитывать то, что большое число пользовате&
лей могут одновременно отправлять запросы в базу данных или вносить в нее изменения,
и при этом не должно быть большой задержки с ответом.
Прерывание стало важным инструментом, которое могли использовать системные
программисты уже на ранних стадиях развития многозадачных и многопользовательских
интерактивных систем. Выполнение любого задания может быть прервано при наступле&
нии определенного события, например завершения ввода&вывода. При этом процессор
сохраняет информацию о текущем состоянии программы и переключается на выполне&
ние программы обработки прерываний, которая выясняет причину прерывания, обраба&
тывает его, а затем возобновляет исполнение прерванной программы.
Стр. 44
отсутствует. 45
Управление памятью
Лучше всего потребности пользователя удовлетворяются вычислительной средой, под&
держивающей модульное программирование и гибкое использование данных. Эффек&
тивный контроль над размещением данных в оперативной памяти со стороны операци&
онной системы позволяет избавиться от многих проблем. При этом операционная
система должна следить за тем, чтобы ни один из независимых процессов не смог изме&
нить содержимое памяти, отведенное другому процессу. Программы должны динамически
размещаться в памяти в соответствии с определенными требованиями. Распределение па&
мяти должно происходить автоматически, независимо от программиста, хотя программист
и может управлять распределением. Таким образом, программист будет избавлен от необ&
ходимости следить за ограничениями памяти, а операционная система повышает эффек&
тивность работы вычислительной системы, выделяя заданиям только тот объем памяти,
который им необходим. Однако программист должен иметь возможность определять мо&
дули программы, а также динамически их создавать, уничтожать и изменять их размер.
При совместном использовании памяти на каждом ее иерархическом уровне одна
программа может обратиться к пространству памяти другой программы. Хотя такая воз&
можность необходима, она представляет угрозу целостности программ и самой операци&
онной системы. Операционная система должна обязательно следить за тем, каким обра&
зом отдельные пользователи осуществляют доступ к различным областям памяти.
Обычно операционные системы выполняют эти требования с помощью средств вир&
туальной памяти и файловой системы. Файловая система обеспечивает долгосрочное
хранение информации, помещаемой в именованные объекты, которые называются фай&
лами. Файл — это удобная для широкого использования структура данных, доступ к ко&
торой и ее защита осуществляются операционной системой.
Виртуальная память — это абстрактное понятие, позволяющее программистам рассмат&
ривать память с логической точки зрения, не заботясь о наличии физической памяти доста&
точного объема. Принципы работы с виртуальной памятью позволяли добиться того, чтобы
задания нескольких пользователей выполнялись параллельно и могли одновременно присут&
ствовать в основной памяти. При такой организации процессов задержка между их выполне&
нием отсутствует: как только один из процессов заносится на вспомогательное запоминающее
Стр. 45
устройство, считывается следующий процесс. Из&за различий в количестве памяти, которое
требуется для разных процессов, при переключении процессора с одного процесса на дру&
гой возникают трудности с компактным их размещением в основной памяти. Поэтому бы&
ли разработаны системы со страничной организацией памяти, при которой процесс разби&
вается на блоки фиксированного размера, или страницы. Обращение программы к слову
памяти происходит по виртуальному адресу, который состоит из номера страницы и смеще&
ния относительно ее начала. Страницы одного и того же процесса могут быть разбросаны по
всей основной памяти. Система разбивки на страницы обеспечивает динамическое соответст&
вие между виртуальным адресом, который использует программа, и реальным адресом основ&
ной памяти. При этом было исключено требование, чтобы все страницы процесса одновре&
менно находились в основной памяти; достаточно, чтобы все они хранились на диске. Во
время выполнения процесса только некоторые его страницы находятся в основной памяти.
Если программа обращается к странице, которая там отсутствует, то система управления
памятью обнаружит это и организует загрузку недостающих страниц.
Стр. 46
отсутствует. 47
Архитектура памяти
Физическая память, к которой микропроцессор имеет доступ по шине адреса (см. рис. 2.1),
называется оперативной памятью. Конструктивно память компьютера можно рассмат&
ривать как массив битов. Один бит может хранить значение 0 или 1 и для работы с ними
идеально подходят логические схемы. Однако работать с памятью на уровне битов край&
не неудобно, поэтому реально ОЗУ организовано как последовательность из восьми ячеек,
которую называют байтом. Один байт состоит из 8 бит. Каждому байту соответствует
свой уникальный адрес, называемый физическим адресом. Диапазон значений физиче&
ских адресов зависит от разрядности шины адреса микропроцессора. Для Intel486 и Pentium
Стр. 47
32
он находится в пределах от 0 до 2 &&1 (4 Гбайт). Для микропроцессоров семейства Р6 этот
36
диапазон шире &&&& от 0 до 2 &&1 (64 Гбайт).
Механизм управления памятью полностью аппаратный. Это означает, что в програм&
ме нельзя сформировать физический адрес памяти на адресной шине, необходимо учи&
тывать конструктивные особенности процессора. Такой механизм позволяет обеспечить:
компактность хранения адреса в машинной команде;
гибкость механизма адресации;
защиту адресных пространств задач в многозадачной системе;
поддержку виртуальной памяти.
Микропроцессор аппаратно поддерживает две модели использования оперативной
памяти.
Сегментированную модель. В этой модели программе выделяются непрерывные
области памяти (сегменты), а сама программа может обращаться только к данным,
которые находятся в этих сегментах.
Страничную модель. Ее можно рассматривать как надстройку над сегментирован&
ной моделью. В случае использования этой модели оперативная память рассмат&
ривается как совокупность блоков фиксированного размера (4 Кбайт). Основное
применение этой модели связано с организацией виртуальной памяти, что позво&
ляет операционной системе использовать для работы программ пространство па&
мяти большее, чем объем физической памяти. Для микропроцессоров Intel486
и Pentium размер возможной виртуальной памяти может достигать 4 Тбайт.
Особенности использования и реализации этих моделей зависят от режима работы
микропроцессора. В реальном режиме работал процессор Intel 8086, защищенный режим
позволяет максимально реализовать все архитектурные идеи, заложенные в модели мик&
ропроцессоров Intel, начиная с i80286. Программы, разработанные для Intel 8086, не мо&
гут функционировать в защищенном режиме. Одна из причин этого связана именно
с особенностями формирования физического адреса в защищенном режиме. Для того
чтобы использовать такие программы, предусмотрен режим виртуального 8086. Переход
в этот режим возможен, если микропроцессор уже находится в защищенном режиме. От&
личительной особенностью этого режима является возможность одновременной работы
нескольких программ, разработанных для Intel 8086. Несмотря на то что микропроцессор
находился в защищенном режиме, в режиме виртуального i8086 возможна работа про&
грамм реального режима. Дело в том, что процесс формирования физического адреса для
этих программ производится по правилам реального режима.
И последний новый режим работы &&&& режим системного управления. Этот режим впер&
вые появился в микропроцессоре Pentium. Он предоставляет операционной системе ме&
ханизм для выполнения машинно&зависимых функций, таких как перевод компьютера
в режим пониженного энергопотребления или выполнения действий по защите системы.
Для перехода в этот режим микропроцессор должен получить специальный сигнал от
усовершенствованного программируемого контроллера прерываний (APIC), при этом
сохраняется состояние вычислительной среды микропроцессора. Функционирование
микропроцессора в этом режиме подобно его работе в режиме реальных адресов.
Стр. 48
отсутствует. 49
Смещение
Сегментированная
+ Сегмент модель
памяти
Адрес реального
(Сегментный *4 режима
регистр)
Сегмент
Смещение
Сегментированная
+ Сегмент модель
памяти
Селектор защищенного
(Сегментный режима
регистр)
Таблица
дескрипторов Сегмент
Смещение
Плоская
+ модель
памяти
Селектор защищенного
(Сегментный режима
регистр)
Таблица
дескрипторов
Селектор
(Сегментный
регистр)
Стр. 49
ции памяти: сегментированная модель памяти реального режима, сегментированная мо&
дель памяти защищенного режима и сплошная модель памяти защищенного режима.
Для примера рассмотрим порядок формирования физического адреса в реальном ре&
жиме. Под физическим адресом понимается адрес памяти, выдаваемый на шину адреса
микропроцессора.
Формирование физического адреса в реальном режиме
В реальном режиме механизм адресации физической памяти имеет следующие харак&
теристики:
диапазон изменения физического адреса от 0 до 1 Мбайт. Эта величина определя&
ется тем, что шина адреса Intel 8086 имела 20 линий;
максимальный размер сегмента 64 Кбайт. Это объясняется 16&разрядной архитектурой
Intel 8086. Поскольку максимальное значение, которое могут содержать 16&разрядные
16
регистры, составляет 2 &&1, то это значение и определяет величину 64 Кбайт.
Для обращения к конкретному физическому адресу оперативной памяти необходимо
определить адрес начала сегмента и смещение внутри сегмента. Однако поскольку адрес
начала сегмента представляет собой всего лишь 16&битовое значение, помещенное в один
из сегментных регистров, то максимальное значение, которое при этом получается, соот&
16
ветствует 2 &&1. Если исходить из этого, то получается, что адрес начала сегмента может
быть только в диапазоне 0&&64 Кбайт от начала оперативной памяти. Но тогда как адресо&
вать остальную часть оперативной памяти вплоть до 1 Мбайт с учетом того, что размер
самого сегмента не превышает 64 Кбайт? Дело в том, что в сегментном регистре содержатся
только старшие 16 бит физического адреса начала сегмента, которые сдвигаются на 4 разря&
да влево для получения 20&битового адреса. Эта операция сдвига выполняется аппаратно
и для программного обеспечения абсолютно прозрачна. Получившееся 20&битовое значе&
ние и является настоящим физическим адресом, соответствующим началу сегмента. Что
касается второго компонента, участвующего в образовании физического адреса некоторого
объекта в памяти &&&& смещения, то оно представляет собой 16&битовое значение. Это значе&
ние может содержаться явно в команде либо косвенно в одном из регистров общего назна&
чения. В микропроцессоре эти две составляющие складываются на аппаратном уровне,
в результате чего получается физический адрес памяти размером 20 бит. Данный механизм
образования физического адреса позволяет сделать программное обеспечение перемещае&
мым, т.е. не зависящим от конкретных адресов загрузки его в оперативной памяти.
Разумеется, в такой организации памяти можно найти много недостатков, но на ран&
них этапах разработки процессоров подобная архитектура имела определенные преиму&
щества. Никто не предполагал, что для обычного компьютера может понадобиться опе&
ративная память в несколько гигабайт. Билл Гейтс при разработке операционной
системы DOS выделил для системы 640 Кбайт и при этом заявил, что он не представляет,
для чего может понадобиться такое огромное количество оперативной памяти. Появле&
ние защищенного режима вызвано желанием ввести в архитектуру процессора средства,
позволяющие избавиться от недостатков реального режима.
Ввод*вывод
Без получения и отображения информации в удобном виде вся работа компьютера не име&
ет смысла. Поэтому система ввода&вывода (BIOS) является одной из важнейших систем,
которая реализована как на самом низшем (аппаратном) уровне, так и на более высоких
уровнях. Например, вы можете написать команду для отображения данных на языке высокого
Стр. 50
отсутствует. 51
уровня, таком как С или Java. При выполнении такой программы произойдет обращение
к операционной системе в тот момент, когда возникнет необходимость отображения дан&
ных. Операционная система в свою очередь обратится к соответствующей функции базовой
системы ввода&вывода для вывода необходимых данных, так как только BIOS имеет воз&
можность произвести запись данных непосредственно в память графического адаптера. Та&
ким образом, взаимодействие с аппаратной частью производится только через BIOS.
Такой подход позволяет создавать переносимые программы, т.е. такие программы,
которые могут выполняться на различных типах компьютеров без переделки самих про&
грамм, поскольку взаимодействие с различным оборудованием производится через BIOS,
который настраивается именно на то оборудование, для которого он предназначен.
Необходимо добавить, что стандартный BIOS, который входит в состав любого ком&
пьютера, обычно ‘‘умеет’’ обращаться только к тем устройствам, которые входят в базо&
вый состав компьютера, и ничего ‘‘не знает’’ о новых устройствах, которые могут быть
дополнительно установлены в компьютер. Для работы с такими устройствами необходи&
мо расширить BIOS, т.е. дополнить его такими программами (драйверами), которые
смогут работать с новым оборудованием. Установка дополнительных драйверов произво&
дится в момент загрузки компьютера и современные операционные системы могут делать
это автоматически. Однако, к примеру, в DOS необходимо записать специальную коман&
ду в файл инициализации, такую как
DEVICE = CDROM.SYS
где файл CDROM.SYS является драйвером устройства считывания компакт&дисков.
В отличии от программ, написанных на языке высокого уровня, программы, напи&
санные на ассемблере, могут быть непереносимыми, &&&& возможно, они будут обращаться
непосредственно к аппаратной части (рис. 2.7).
Преимущество таких программ &&&& высокая скорость работы, так как программист
может не использовать стандартные подходы, которые из&за своей универсальности яв&
ляются довольно медленными, а реализовать уникальный и быстрый метод доступа.
Резюме
В данной главе кратко описаны технические средства компьютера, которые необхо&
димо знать программисту для того, чтобы писать эффективные программы. Понимание
архитектуры компьютера позволит непосредственно обращаться в отдельным блокам
и узлам компьютера и тем самым создавать очень быстро работающие программы, так как
будут исключены все промежуточные программные модули, обеспечивающие поддержку
всевозможных режимов и настроек, но при этом замедляющие работу программы.
Контрольные вопросы
1. Какова роль процессора в компьютерной системе?
2. Назовите основные компоненты компьютера, без которых невозможна работа
компьютера.
3. Как взаимосвязаны операционная система и аппаратная часть компьютера?
4. Как вы считаете, операционная система разрабатывается под определенную аппа&
ратную платформу или аппаратная часть разрабатывается для определенной опе&
рационной системы?
5. Почему операционные системы постоянно модифицируются?
Стр. 51
6. Зачем нужна оперативная память?
7. Что такое кэш&память и почему она необходима в компьютере?
8. Зачем нужны регистры в процессоре?
9. Назовите четыре регистра общего назначения.
10. Как обозначается старшая половина регистра CX? Сколько битов она может хранить?
11. Какое приоритетное назначение регистра ECX?
12. Почему доступ к оперативной памяти происходит значительно дольше по време&
ни, чем к регистрам?
13. В чем преимущество защищенного режима в процессорах? Для каких прикладных
программ необходим такой режим?
14. Можно ли в операционной системе DOS использовать защищенный режим?
15. Почему с изменением регистра AH одновременно изменяется регистр EAX?
16. В каком 16&разрядном регистре будет находиться верхняя часть результата пере&
множения чисел?
17. Какой регистр является указателем базы для всех выполняемых команд?
18. Какой регистр является указателем базы для стека?
19. Помимо указателя стека, с помощью каких других регистров можно вычислить пе&
ременную в стеке?
20. Назовите два регистра индексов.
21. Какие три флага называются флагами контроля?
22. Флаги состояния реагируют на дополнительный перенос, четность, перенос, пере&
полнение. Какие еще есть флаги состояния?
23. Какой флаг устанавливается, когда результат арифметической операции с числами
без знака становится слишком большим для размещения результата в регистре?
24. Какой флаг устанавливается, когда в результате арифметической или логической
операции возникает отрицательный результат?
25. Какой флаг отражает состояние одного бита результата операции?
Стр. 52
Глава 3
Введение
в язык ассемблера
В этой главе...
Представление данных
Основы языка ассемблера
Разработка программы на языке ассемблера
Работа в DOS под Windows NT
Инструментальные средства
Пример простой программы
Ассемблер Microsoft
Отладчик
Резюме
Контрольные вопросы
Представление данных
Поскольку общение с компьютером происходит на машинном уровне, необходимо
иметь представление о том, как сохраняется и обрабатывается информация. Для этого ис&
пользуются электрические элементы, которые могут принимать только два состояния:
‘‘включено’’ и ‘‘выключено’’. При сохранении данных в устройствах хранения, последова&
тельность электрических или магнитных зарядов также интерпретируется как состояние
‘‘включено’’ или ‘‘выключено’’, что и составляет содержимое записанной информации.
Стр. 53
Двоичные числа
Компьютер сохраняет команды и данные в оперативной памяти как последователь
ность заряженных или разряженных ячеек. Образно можно представить состояние каж
дой ячейки как переключатель с двумя состояниями: ‘‘включено и выключено’’ или ‘‘ис
тина и ложь’’. Такие ячейки идеально подходят для хранения двоичных чисел, которые
используют базовое число 2, так как отдельные биты могут принимать только два состоя
ния 0 или 1. Ячейки памяти, соответствующие единице, имеют повышенный заряд,
а соответствующие нулю почти разряжены. На рис. 3.1 условно показано соответствие
переключателей и двоичных чисел.
Включено Выключено Включено Включено Выключено Выключено Включено Выключено
1 0 1 1 0 0 1 0
байт
двойное
слово
учетверенное
слово
Стр. 54
отсутствует. 55
Команды и данные
В языках высокого уровня команды и данные имеют существенное логическое различие,
однако в машине они все представлены одинаково, как наборы нулей и единиц. Например,
следующая последовательность двоичных разрядов может включать первые три символа
алфавита, сохраненные в строковой переменной, или может быть машинной командой.
010000010100001001000011
Именно поэтому программисты, использующие язык ассемблера, должны разделять
данные и команды, чтобы процессор не ‘‘выполнял’’ переменные и не воспринимал ко&
манды как переменные.
Числовые системы
Каждая числовая система имеет основание системы счисления, или базовое число &&&&
максимальное значение, которое может быть присвоено отдельной цифре. В табл. 3.2 при&
ведены разрешенные значения для различных систем счисления. Во всех последующих
главах при отображении записей в памяти, значений регистров и адресов будут использо&
ваться шестнадцатеричные числа, для которых основанием системы счисления является
число 16. Для компактного отображения значений больше 9 используются шестнадца%
теричные символы от A до F, соответствующие десятичным значениям от 10 до 15.
Когда записывают двоичное, восьмеричное или шестнадцатеричное число, к нему до&
бавляют определенный символ, представленный строчной буквой. Например, шестна&
дцатеричное число 45 должно быть записано как 45h, восьмеричное 76 &&&& как 76o, а дво&
ичное 11010011 необходимо записать как 11010011b. Таким образом ассемблер
распознает числовые константы в исходной программе.
Двоичная 2 01
Восьмеричная 8 01234567
Десятичная 10 0123456789
Шестнадцатеричная 16 0123456789ABCDEF
Стр. 55
Но если это число послать в память видеоадаптера, то он воспримет его как символ, по&
этому на экране увидим букву А. Это происходит потому, что в соответствии с кодиров&
кой ASCII для символа А выбрано значение 01000001. Таким образом, интерпретация
данного значения зависит от определенных условий, которые и придают ему смысл.
Двоичное число &&&& сохраняется в памяти как последовательность битов, готовых к ис&
пользованию в расчетах. Целые двоичные числа сохраняются по 8, 16 или 32 разряда.
Символы стандартного набора ASCII &&&& могут быть представлены в памяти подоб&
но числовому значению, например как 123 или 65. Для отображения символов
может быть использован любой числовой формат, как показано в табл. 3.3.
0 1 8 256
2 2
1 2 9 512
2 2
2 4 10 1024
2 2
3 8 11 2048
2 2
4 16 12 4096
2 2
5 32 13 8192
2 2
6 64 14 16384
2 2
7 128 15 32768
2 2
Стр. 56
отсутствует. 57
8 + 1 + 9
0 0 0 0 1 0 0 1
Шестнадцатеричные числа
Большие двоичные числа почти невозможно прочитать, поэтому используют шестна
дцатеричные числа, которые удобно преобразовывать в двоичные числа и которые до
вольно хорошо воспринимаются при просмотре листингов. Их используют и в языке ас
семблера, и в отладчиках для отображения двоичных данных и машинных команд.
Каждое шестнадцатеричное число заменяет четыре двоичных бита, а два шестнадцате
ричных числа представляют байт.
На рис. 3.4 показано представление двоичного числа 000101100000011110010100
в шестнадцатеричном виде 160794h.
1 6 0 7 9 4
0001 0110 0000 0111 1001 0100 = 160794h
0000 0 0 1000 8 8
0001 1 1 1001 9 9
0010 2 2 1010 10 A
0011 3 3 1011 11 B
0100 4 4 1100 12 C
0101 5 5 1101 13 D
0110 6 6 1110 14 E
0111 7 7 1111 15 F
Каждая позиция шестнадцатеричного числа представляет степень числа 16, что ис
пользуется при вычислении десятичного значения числа, как показано в табл. 3.6.
Для преобразования шестнадцатеричного значения в десятичное необходимо умно
жить значение каждого разряда на соответствующий десятичный эквивалент, а потом их
просуммировать. На рис. 3.5 приведен пример преобразования числа 3BA4h. Берется
наибольшее значение 3 и умножается на десятичный эквивалент позиции 4096. Сле
дующее число B умножается на 256, A умножается на 16 и последнее число 4 умножается
на 1. Все просуммировав, получим соответствующее десятичное число 15268.
Стр. 57
Таблица 3.6. Степени числа 16
16n Десятичное 16n Десятичное
0 4
16 1 16 65 536
161 16 165 1 048 576
2 6
16 256 16 16 777 216
163 4096 167 268 435 456
Числа со знаком
Двоичные числа могут быть как со знаком, так и без знака. Числа без знака использу
ют все восемь битов для получения значения (например, 11111111 = 255). Просумми
ровав значения всех битов для преобразования в десятичное число, получим максималь
но возможное значение, которое может хранить байт без знака (255). Для слова без знака
это значение будет составлять 65535. Байт со знаком использует только семь битов для
получения значения, а старший восьмой бит зарезервирован для знака, при этом 0 соот
ветствует положительному значению, а 1 отрицательному. На представленном ниже
рис. 3.6 показано отображение положительного и отрицательного числа 10.
Знаковый бит
1 1 1 1 0 1 1 0 = 10
0 0 0 0 1 0 1 0 = +10
Дополнение до двух
Чтобы не усложнять процессор, отдельный блок для реализации операции вычитания
не делают; эту операцию выполняет блок суммирования. Перед суммированием отрица
тельные числа преобразовываются в дополнительные числа. Это такое число, которое
в сумме с исходным числом дает 0. Например, десятичное –6 будет дополнением к 6, так
как 6+(-6)=0. Таким образом, вместо операции вычитания A–B процессор суммирует
с положительным числом A дополнительное к B: A+(-B). Вместо того чтобы вычесть 4 из 6,
процессор просто складывает –4 и 6.
При работе с двоичными числами для дополнительного числа используется термин
дополнение до двух (встречается также определение двоичное дополнение). Например, для
двоичного значения 0001 двоичным дополнением до двух будет 1111. Такое число полу
чается из исходного числа после изменения всех единиц на нули, а нулей на единицы
(инверсия) и прибавления к полученному числу единицы, как показано ниже. Инверсия
Стр. 58
отсутствует. 59
битов в двоичном числе обозначается NOT(n). Поэтому двоичное дополнение может быть
представлено выражением NOT(n)+1.
число 0001
инверсированное число 1110
добавить 1 1111
Еще несколько примеров преобразования чисел приведено в табл. 3.7 (для дополне
ния до двух используем аббревиатуру NEG(n)).
Хотя процессор выполняет вычисления без учета знака числа, в программе знак операн
да необходимо обязательно указывать. Сложение операндов со значениями +16 и -23 будет
выглядеть в командах ассемблера следующим образом.
MOV AX,+16
ADD AX,-23
Стр. 59
В двоичном выражении число 16 будет выглядеть как 00010000, а -23 &&&& как
11101001. Когда процессор складывает эти числа, он получает 11111001. Это двоичное
число соответствует десятичному -7, как показано в примере.
00010000 16
+11101001 -23
=11111001 -7
Хранение символов
Компьютеры могут хранить только двоичные значения, но нам необходимо работать не
только с численными значениями, но и с символами, такими как ‘‘A’’ или ‘‘$’’. Для этого
компьютер использует схему кодирования символов, которая позволяет преобразовывать
символы в числа и наоборот. Наиболее известная система кодирования для компьютеров
обозначается аббревиатурой ASCII (American Standard Code for Information Interchange).
В ASCII каждому символу присваивается уникальный код, включая контрольные символы,
используемые при печати и передаче данных между компьютерами. Стандартный ASCII&
код использует только 7 разрядов в диапазоне 0-127. Значения от 0 до 31 заняты слу&
жебными кодами, используемыми при печати, передаче информации и выводе на экран.
В обычном режиме они не отображаются на экране. Остальные значения, допустимые в бай&
те, &&&& дополнительные, их применяют для расширения символьного ряда. В операционной
системе MS DOS значения 128-255 используются для получения графических символов
и греческих букв. В операционной системе Windows существует множество наборов симво&
лов, и в каждом из них дополнительным значениям соответствуют различные символы.
Строка символов представляет в памяти последовательность байт. Например, число&
вым кодам строки ‘‘ABC123’’ будет соответствовать последовательность значений 41h,
42h, 43h, 31n, 32h и 33h.
Таблица кодов ASCII приведена в справочном разделе. Чтобы найти шестнадцате&
ричное значение нужного символа, используйте соответствующие значения второй стро&
ки и второй колонки, на пересечении которых находится символ. Старший разряд числа
находится в строке, младший &&&& в колонке. Например, чтобы найти шестнадцатеричное
значение символа ‘‘a’’, посмотрим на соответствующее значение в строке. Это будет 6,
соответствующее значение в колонке будет 1. Таким образом, получаем шестнадцате&
ричное значение 61h.
Хранение чисел
Как наиболее эффективно сохранять числа в памяти? Это зависит от того, как эти
числа будут использоваться. Если числа используются для вычислений, должно быть
применено двоичное представление числа, и наоборот, лучше хранить коды ASCII, если
данные значения будут использоваться для отображения символов на экране. Например,
число 123 можно сохранить в памяти двумя способами: как последовательность кодов
ASCII для чисел 1, 2 и 3 или как один байт со значением 123, как показано на рис. 3.7.
Двоичное содержимое памяти всегда можно просмотреть, но по одному лишь значе&
нию нельзя определить, что оно представляет. Предположим, два байта памяти имеют
Стр. 60
отсутствует. 61
Данные, код или текст? Это невозможно узнать, 00110001 00110010 00110011 = “123”
Стр. 61
Константы и выражения
Цифровой литерал является комбинацией цифр и дополнительных символов &&&& зна&
ков, десятичных точек и экспонент:
5;
5,5;
26,E+05.
Целочисленные константы могут оканчиваться дополнительным буквенным символом,
который является указателем базы системы счисления: h — шестнадцатеричная, q (или o) &&&&
восьмеричная, d &&&& десятичная, b &&&& двоичная. Если дополнительного буквенного сим&
вола нет, то по умолчанию принимается десятичная система счисления. Буквенный символ
может быть строчным или заглавным. В табл. 3.10 приведено несколько примеров цело&
численных констант.
Если число не сопровождается дополнительным буквенным символом, то по умолчанию
принимается, что для данного числа используется десятичная система счисления.
Стр. 62
отсутствует. 63
Утверждения
В языке ассемблера утверждение состоит из имени, мнемокода, операндов и коммен&
тариев. Утверждения бывают двух типов: команды и директивы. Команды &&&& это утвер&
ждения, выполняемые в программе, а директивы &&&& утверждения, информирующие ас&
семблер о том, как создавать выполняемый код. Общая форма утверждения выглядит так:
[имя] [мнемокод] [операнды] [; комментарии]
Утверждение имеет свободную форму записи. Это означает, что его можно записы&
вать с любой колонки и с произвольным количеством пробелов между операндами. Ут&
верждение должно быть записано на одной строке и не заходить за 128&ю колонку. Мож&
но продолжить запись со следующей строки, но при этом первая строка должна
заканчиваться символом ‘‘\’’ (обратная косая черта), как показано в примере ниже.
longArrayDefinition DW l000h, 1020h, 1030h \
1040h, 1050h, 1060h, 1070h, 1080h
Команда &&&& это утверждение, которое выполняется процессором во время работы
программы. Команды могут быть нескольких типов: передачи управления, передачи дан&
ных, арифметические, логические и ввода&вывода. Команды транслируются ассемблером
непосредственно в машинные коды. Ниже приведен фрагмент листинга со всеми исполь&
зуемыми категориями команд.
CALL MySub ; Передача управления.
MOV EAX,5 ; Передача данных.
ADD EAX,20 ; Арифметическая.
JZ next1 ; Логическая (переход, если установлен флаг нуля).
IN AL,20 ; Ввод-вывод (чтение из аппаратного порта).
Стр. 63
Директива это утверждение, которое выполняется ассемблером во время трансля
ции исходной программы в машинные коды. Например, директива DB заставляет ас
семблер выделить память для однобайтовой переменной, названной count, и поместить
туда значение 50.
count DB 50
Следующая директива .STACK заставляет ассемблер зарезервировать пространство
памяти для стека.
.STACK 4096
Имена
Имена определяют метки, переменные, символы или ключевые слова. Они могут со
стоять из символов, приведенных в табл. 3.11.
Стр. 64
отсутствует. 65
слова не могут использоваться программистом для каких&либо других целей, например как
имена. Если использовать ключевое слово ADD как метку, это будет синтаксической ошибкой.
ADD: MOV EAX,10 ; Синтаксическая ошибка!
Стр. 65
которые потом можно собрать вместе с помощью компоновщика. При этом можно исполь
зовать модули, написанные другими программистами, или ранее написанные и отлажен
ные модули. Если есть набор подходящих модулей, то разработка сложной программы мо
жет занять не так уж и много времени. Надо только объединить уже существующие и вновь
написанные модули и получить один исполняемый модуль что и сделает компоновщик.
Компоновщик должен вызываться для любой написанной программы, даже если она
состоит только из одного объектного модуля. Одномодульные программы компоновщик
сразу преобразует в перемещаемый модуль. Если программа состоит из двух или боль
шего количества модулей, то компоновщик сначала объединяет их, а затем преобразовы
вает результат в перемещаемый модуль.
Завершенную программу можно вызвать для выполнения двумя способами:
набрать ее имя в качестве команды или щелкнуть на имени программы мышкой;
выполнить ее под управлением программы DEBUG.
Обычно программу следует выполнять только в том случае, если есть уверенность в ее
безошибочной работе. Пока она не будет полностью отлажена, необходимо вызывать
программу только под управлением отладчика DEBUG. Это связано с тем, что непрове
ренная программа может нанести системе непоправимые повреждения. При программи
ровании под Win32 это маловероятно, но если вы программируете под DOS, то это пра
вило необходимо соблюдать.
С помощью отладчика DEBUG можно управлять процессом выполнения программы.
Наряду с другими функциями, DEBUG позволяет отображать и изменять значения пере
менных, останавливать выполнение программы в заданной точке или выполнять про
грамму по шагам. Таким образом, DEBUG является основным инструментом для поиска
и исправления ошибок в программе.
На рис. 3.8 показаны этапы разработки программ с помощью ассемблера. В скобках
для каждого модуля указаны расширения файлов, в которых модули сохраняются на диске.
Библиотека
загрузочных
модулей
Текстовый редактор
Исходная программа Ассемблер Объектный модуль Загрузчик Исполняемый модуль
Отладчик
(.asm) (.obj) (.exe)
Стр. 66
отсутствует. 67
деталями. Набросок должен представлять собой ряд строк, в которых описаны действия
программы. Например, при разработке программы, которая выполняет одну из несколь&
ких функций по выбору пользователя, набросок может выглядеть следующим образом.
; Изобразить меню возможных функций
; Запросить у пользователя выбор из меню
; Прочитать ответ пользователя
; Проверить допустимость ответа
; Если ответ допустим, выполнить требуемую функцию
Точки с запятой означают, что эти строки представляют собой не команды, а коммен&
тарии, которые необходимы только разработчику. Ассемблер пропускает все до конца
строки после точки с запятой.
Затем, с учетом этого текста, производится вставка необходимых команд между стро&
ками. Так как каждая строка описывает относительно небольшую задачу, проще всего
выполнить каждую задачу в отдельности, проверяя решение перед тем, как двинуться
дальше. Иначе говоря, начните со вставки первой группы команд (в представленном
примере с команд для изображения меню), затем запишите полученную программу на
диск и завершите все последующие этапы (трансляцию, компоновку и выполнение).
Реализация такой частичной программы покажет, правильно ли она работает. Если она
работает неправильно, отладьте ее и попробуйте еще раз. После того как первая часть от&
лажена, перейдите ко второй части, затем к третьей и т.д.
Может показаться, что это очень медленный метод разработки программы, но только
так можно разрабатывать программы, которые содержат мало ошибок и которые впо&
следствии можно модифицировать. При этом достигаются следующие цели:
четко просматривается логика программы;
производится документирование программы с помощью комментариев, впослед&
ствии программу будет легко модифицировать;
обеспечивается правильность работы каждой части до того, как произойдет пере&
ход к разработке следующей части программы.
Стр. 67
и экрана, работа с загружаемыми драйверами устройств, обработка запросов на ввод&
вывод с диска). Windows NT может эмулировать определенные операционные системы
с использованием модулей, которые называются подсистемами среды, и DOS &&&& одна из
таких операционных систем, работающая как программа под управлением Windows NT.
Подсистема среды DOS предоставляет весь системный сервис, как это обычно делает
DOS, но эти функции интегрированы в Windows NT, а не расположены отдельно от нее.
Windows NT имеет специальное окно для DOS, которое называется командная кон%
соль, и работа в этом окне очень похожа на сеанс MS DOS в Windows 3x или Windows 9x.
Интерпретатор команд содержит богатый набор команд, размеры окна можно изменять,
окно имеет полосу прокрутки и не ограничено 24 строками, как в DOS.
Эмуляцию DOS обеспечивает 32&разрядное приложение с именем cmd.exe, которое яв&
ляется расширенной версией MS DOS и не только обеспечивает совместимость с MS DOS,
но и позволяет запускать приложения Windows, OS/2 и POSIX в режиме командной строки.
Сеанс DOS, который создается при запуске приложения DOS из командного окна,
можно сконфигурировать с помощью загружаемых драйверов устройств, TSR и т.д.
При запуске программы DOS из Windows NT создается виртуальная машина DOS,
благодаря которой программа DOS работает как бы на отдельном компьютере. Windows NT
создает отдельную виртуальную машину для каждого запускаемого приложения DOS.
Каждая такая машина имеет весь сервис, необходимый для работы как с 16&разрядными,
так и с 32&разрядными вызовами DOS в соответствии с требованиями DOS 6. Этой вир&
туальной машине выделяется 16 Мбайт памяти. При необходимости могут поддержи&
ваться диспетчеры памяти. Есть и ограничения. В целях безопасности и защиты ядра
приложения DOS должны быть изолированы. Для этого подсистема среды DOS перехва&
тывает все процессы ввода&вывода, проверяет их, а затем направляет данные по назначе&
нию. Этот процесс обслуживается перехватчиками ввода&вывода, которые, в свою оче&
редь, передают данные программе NT Executive для доставки по назначению.
Любые традиционно написанные программы DOS, которые выполняют ввод&вывод
с помощью стандартных системных вызовов DOS, будут работать под Windows NT без
проблем. А программы, выполняющие запись непосредственно на устройство, для кото&
рого драйверы не разрешают прямого доступа (например, драйверы жестких дисков), бу&
дут прерваны программой контроля безопасности, что приведет к сообщению об ошибке
и завершению опасной программы.
Если программа пытается записывать и читать из портов СОМ и LPT, с экрана или
клавиатуры, то Windows NT выполнит это безупречно, однако другие виды прямого
управления памятью, диском или системным устройством разрешены не будут.
Большинство программ DOS, которые используют драйвер мыши или клавиатуры, бу&
дут работать, поскольку строки ввода драйвера мыши и клавиатуры эмулируются (их ис&
пользуют очень многие программы DOS).
Операционная система DOS использует командную строку для ввода отдельных ко&
манд. Перечень всех доступных команд можно получить, если ввести команду help.
Список всех команд приведен в табл. 3.13.
Инструментальные средства
Термины ‘‘трансляция’’, ‘‘компоновка’’, ‘‘отладка’’ и другие, связанные с этапами ра&
боты ассемблера, уже упоминались в этой книге, но о самом ассемблере пока ничего не
говорилось. В этом разделе будут даны начальные сведения о наиболее популярных ас&
семблерах TASM и MASM.
Стр. 68
отсутствует. 69
Стр. 69
Пример простой программы
В представленном листинге приведена простая программа для Win32, которая отобра&
жает на экране традиционное приветствие ‘‘Hello, world!’’. В этой программе показаны
основные особенности приложений на языке ассемблера. В первой строке использована
директива Title, остальные символы строки трактуются как комментарий, подобно всем
символам во второй строке. Исходный код этой программы написан на языке ассемблера
и должен быть оттранслирован в машинные коды перед запуском программы.
Сегменты являются строительными блоками программы. Сегмент кодов определяет
место, где хранятся коды программы, сегмент данных включает все переменные, а сегмент
стека включает исполнительный стек. Стек &&&& это специальное пространство в памяти,
которое обычно используется программой при вызове и возврате подпрограмм.
ExitProcess PROTO,
x:dword
WaitMsg PROTO
WriteString PROTO
Crlf PROTO
.data
strHello BYTE "Hello Word!",0
.code
main PROC
mov EDX, OFFSET strHello ; Аргументы для WriteString.
invoke WriteString ; Вывод строки на консоль.
invoke Crlf ; Перевод каретки.
invoke WaitMsg ; Запрос для нажатия клавиши.
invoke ExitProcess,0 ; Корректное окончание программы.
main ENDP
END main
Стр. 70
отсутствует. 71
Стр. 71
Директива .STACK устанавливает размер пространства для стека емкостью 100h
(256) байт. Директива .DATA отмечает начало сегмента данных, где сохраняются
переменные. Здесь под именем strHello сохраняется строка ‘‘Hello, world!’’, за
которой следуют два служебных символа перехода на новую строку (0dh,0ah).
Символ ‘‘$’’ используется как символ конца строки для подпрограмм, которые бу&
дут считывать эту строку.
Директива .CODE отмечает начало сегмента кодов, где должны находиться выпол&
няемые команды. Директива PROC объявляет начало процедуры. В этой программе
объявлена процедура с именем main.
Первые две команды процедуры main копируют адрес сегмента данных (@data)
в регистр DS. Команда MOV всегда сопровождается двумя операндами: первый ука&
зывает, куда поместить данные, а второй &&&& откуда эти данные взять.
Затем в процедуре main на экран выводится строка символов. При этом вызыва&
ется функция DOS, которая непосредственно выводит на экран строку символов,
начиная с адреса, который указан в регистре DX. Номер этой функции предвари&
тельно должен быть помещен в регистр AH.
Последние две команды процедуры main (MOV AX,4C00h и INT 21h) заканчи&
вают программу и передают управление операционной системе.
Утверждение main ENDP использует директиву ENDP, которая отмечает конец
главной процедуры.
В самом конце находится директива END, заканчивающая программу, которая долж&
на быть оттранслирована. Следующая за ней метка main говорит об окончании
главной процедуры, или программы. Эта директива необходима для компилятора.
Как видите, отличие есть, и оно заключается в том, что вместо процедур Windows
здесь используются прерывания DOS и номера функций, которые выполняют аналогич&
ные действия, но менее удобны в использовании, так как приходится запоминать боль&
шое число номеров функций и прерываний и знать, как передавать и получать из них
рассчитанные значения.
В табл. 3.12 приведен список наиболее часто используемых директив ассемблера.
После того как программа ‘‘Hello World’’ написана в текстовом редакторе, ее необхо&
димо сохранить на диске под именем hello.asm. После этого ее можно компилировать.
Стр. 72
отсутствует. 73
Стр. 73
процедуре и признаком окончания строки служит нулевой символ. Именно поэтому при
объявлении строки в конце поставлено значение нуль.
strHello BYTE "Hello Word!",0
Следовательно, для использования процедуры WriteConsole предварительно необ&
ходимо рассчитать и длину строки.
Полностью программа Hello, которая использует только процедуры Windows и не
обращается к сторонним библиотекам, будет выглядеть так, как показано в листинге 3.1.
.data
strHello BYTE "Hello Word!",13,10,0
.code
main PROC
invoke Initialize
mov EDX, OFFSET strHello ; Аргументы для WriteString.
invoke WriteString ; Вывод строки на консоль.
invoke WaitMsg ; Запрос для нажатия клавиши.
invoke ExitProcess,0 ; Корректное окончание программы.
main ENDP
Стр. 74
отсутствует. 75
;---------------------------------------------------------
Initialize PROC private
; Получить стандартные дескрипторы консоли для входа и выхода
;----------------------------------------------------
.data
consoleOutHandle DWORD ?
consoleInHandle DWORD ?
.code
pushad
popad
ret
Initialize ENDP
;---------------------------------------------------------
Str_length PROC USES edi,
pString:PTR BYTE ; Указатель на строку
; Возвращает длину строки с нулевым окончанием.
; Принимает: pString - указатель на строку
; Возвращает: EAX = длина строки
;---------------------------------------------------------
mov edi,pString
mov eax,0 ; Счетчик символов.
L1:
cmp BYTE PTR [edi],0 ; Конец строки? (сравниваем с нулем)
je L2 ; Да: выход. (переход на метку L2)
inc edi ; Нет: выбираем следующий символ.
inc eax ; Добавляет в счетчик 1.
jmp L1 ; Переход на метку L1.
L2: ret ; Возврат из процедуры.
Str_length ENDP
;---------------------------------------------------------
WriteString PROC
; Записывает строку с нулевым окончанием в стандартный выход.
; Принимает: EDX указывает на строку.
;--------------------------------------------------------
pushad ; Сохраняем регистры.
INVOKE Str_length,edx ; Возвращает длину строки в EAX.
cld ; Необходимо выполнить до WriteConsole.
INVOKE WriteConsoleA,
consoleOutHandle, ; Дескриптор выхода.
edx, ; Указывает на строку.
eax, ; Длина строки.
OFFSET strHello, ; Число записанных байт.
0
popad ; Восстанавливаем регистры.
ret
WriteString ENDP
;------------------------------------------------------
Стр. 75
WaitMsg PROC
; Отображает запрос и ожидает нажатия клавиши <Enter>.
;------------------------------------------------------
.data
waitmsgstr DB "Press [Enter] to continue...",0
localBuf BYTE 5 DUP(?)
bytesRead DWORD ?
.code
pushad ; Сохраняем регистры.
mov edx,OFFSET waitmsgstr
call WriteString
w1: INVOKE FlushConsoleInputBuffer,consoleInHandle
INVOKE ReadConsoleA,
consoleInHandle, ; Дескриптор устройства ввода.
OFFSET localBuf, ; Указатель на буфер.
5, ; Размер буфера.
OFFSET bytesRead,
0
cmp bytesRead,2 ; Сравнение с 2.
jnz w1 ; Повторение, пока не считано 2 байта
END main
Итак, основная часть программы, которая находится в сегменте данных и кодов, поч&
ти не изменилась. В сегменте данных несколько изменилось объявление строки
strHello, где в конце добавлены два байта со значениями 13 и 10. Эти байты будут вос&
приниматься устройством вывода на консоль как перевод каретки, что аналогично ис&
пользованию в первом случае процедуры Crlf. Это не принципиально и сделано для по&
каза различных возможностей ассемблера.
Также не принципиально и появление новой процедуры Initialize, которая полу&
чает и присваивает значения дескрипторов соответствующим переменным, используе&
мым при инициализации устройств ввода&вывода. Эта процедура использовалась и ра&
нее, только это было не так явно.
Теперь для компиляции этой программы необходимо использовать команды, как по&
казано на рис. 3.10
Стр. 76
отсутствует. 77
Давайте последовательно пройдем шаги, которые необходимы для того, чтобы вернуть
эту программу в исходное состояние, т.е. представим ее в удобном для программиста и тех,
кто ее анализирует, виде. Для этого существуют такие возможности, как создание биб&
лиотек и заголовочных файлов, в которые и будет вынесено все, что не используется при
описании логики программы, но что необходимо для того, чтобы компилятор мог реали&
зовать эту логику.
Сначала создадим собственную библиотеку с именем MyLib и вынесем в нее все, что
не составляет суть программы. Это будут все объявления и процедуры, которые описаны
после конца процедуры main: Initialize, WriteString, WaitMsg и Str_length.
Создадим отдельный файл и перенесем в него все эти объявления и процедуры. Этот
файл может выглядеть так
TITLE Библиотека необходимых процедур (MyLib.asm)
.386
.model flat, stdcall
.code
;---------------------------------------------------------
Initialize PROC
; Получить стандартные дескрипторы консоли для входа и выхода.
;----------------------------------------------------
.data
consoleOutHandle DWORD ? ; Дескриптор устройства вывода.
consoleInHandle DWORD ? ; Дескриптор устройства ввода.
.code
Стр. 77
pushad
popad
ret
Initialize ENDP
;---------------------------------------------------------
Str_length PROC USES edi,
pString:PTR BYTE ; Указатель на строку
; Возвращает длину строки с нулевым окончанием.
; Принимает: pString - указатель на строку
; Возвращает: EAX = длина строки
;---------------------------------------------------------
mov edi,pString
mov eax,0 ; Счетчик символов.
L1:
cmp BYTE PTR [edi],0 ; Конец строки? (сравниваем с нулем)
je L2 ; Да: выход. (переход на метку L2)
inc edi ; Нет: выбираем следующий символ.
inc eax ; Добавляет в счетчик 1.
jmp L1 ; Переход на метку L1.
L2: ret ; Возврат из процедуры.
Str_length ENDP
;---------------------------------------------------------
WriteString PROC
; Записывает строку с нулевым окончанием в стандартный выход.
; Принимает: EDX указывает на строку.
;--------------------------------------------------------
.data
wsBuf DWORD ?
.code
pushad ; Сохраняем регистры.
INVOKE Str_length,edx ; Возвращает длину строки в EAX.
cld ; Выполнить до WriteConsole.
INVOKE WriteConsoleA,
consoleOutHandle, ; Дескриптор выхода.
edx, ; Указывает на строку.
eax, ; Длина строки.
OFFSET wsBuf, ; Число записанных байт.
0
popad ; Восстанавливаем регистры.
ret ; Возврат из процедуры.
WriteString ENDP
;------------------------------------------------------
WaitMsg PROC
; Отображает запрос и ожидает нажатия клавиши <Enter>.
;------------------------------------------------------
.data
waitmsgstr DB "Press <Enter> to continue...",0
localBuf BYTE 5 DUP(?) ; Буфер.
Стр. 78
отсутствует. 79
Initialize PROTO
Стр. 79
ExitProcess PROTO,
x:DWORD
WaitMsg PROTO
WriteString PROTO
.data
strHello BYTE "Hello Word!",13,10,0
.code
main PROC
invoke Initialize
mov EDX, OFFSET strHello ; Аргументы для WriteString.
invoke WriteString ; Вывод строки на консоль.
invoke WaitMsg ; Запрос для нажатия клавиши.
invoke ExitProcess,0 ; Корректное окончание программы.
main ENDP
END main
При компиляции будем использовать вновь созданную библиотеку MyLib.obj, т.е. бу&
дем использовать команды, как показано на рис. 3.12.
Здесь также можно наблюдать некоторое отличие от первой программы Hello, что
наглядно демонстрирует гибкость ассемблера &&&& вы можете так настроить программу, что она
будет полностью отвечать вашим запросам.
Например, в библиотеке необходимо четко разделить процедуры и объявления. Все
объявления следует вынести в отдельный заголовочный файл, который постоянно будет
использоваться с различными библиотеками, а в самой библиотеке оставить только описа&
ния процедур, для чего она и предназначена. Обо всем этом и пойдет речь в дальнейшем.
Ассемблер Microsoft
Как уже не раз отмечалось, операционная система DOS сохранена для работы с множе&
ством все еще существующих программ, написанных под DOS. Ассемблер Microsoft &&&& од&
на из таких программ. Для этой программы нет соответствующего оконного интерфейса
под Windows, и приходится работать в менее удобном консольном окне. Поэтому для нача&
ла работы с ассемблером необходимо запустить интерпретатор команд DOS, для чего выби&
рается команда Start All Programs Accessories Command Prompt в системном меню. За&
тем необходимо установить пути для удобного запуска программ ассемблера. Для этого
существует команда PATH из набора команд DOS. Рассмотрим подробнее работу в окне DOS.
Стр. 80
отсутствует. 81
Работа с DOS
Для работы с DOS необходимо запустить интерпретатор команд DOS. Для операци&
онной системы Windows XP это делается так, как описано в предыдущем разделе. Полу&
чим окно, показанное на рис. 3.13.
Стр. 81
Продолжение табл. 3.13
Команда Описание
DIR Отображает список файлов и подкаталогов
DISKCOMP Сравнивает содержимое двух флоппи&дисков
DISKCOPY Копирует содержимое флоппи&диска на другой диск
DOSKEY Редактирует командную строку, повторяет команды и создает макросы
ECHO Отображает сообщения или выключает и включает отображение вводимых
символов на экране (эхо)
ENDLOCAL Завершает изменения локализации Windows в командных файлах
ERASE Удаляет один или несколько файлов
EXIT Закрывает интерпретатор команд
FC Сравнивает содержимое нескольких файлов и отображает различие между ними
FIND Выполняет поиск текстовой строки в одном или нескольких файлах
FINDSTR Осуществляет поиск строк в файлах
FOR Запускает указанную команду для каждого файла из набора нескольких файлов
FORMAT Выполняет форматирование диска для работы с Windows
FTYPE Осуществляет отображение или модификацию типов файлов, используемых
в перечне соответствий типов и расширений
GOTO Выполняет переход к помеченной строке в командных файлах
GRAFTABL Разрешает системе Windows отображать расширенный набор символов
в графическом режиме
HELP Используется для получения справочной информации
IF Используется для создания условного утверждения в командных файлах
LABEL Создает, изменяет или удаляет метки томов на диске
MD Создает каталог
MKDIR Создает каталог
MODE Конфигурирует системные устройства
MORE Отображает часть выходных данных на экран за один раз (чтобы получить
продолжение, необходимо еще раз выполнить эту команду)
MOVE Перемещает один или несколько файлов из одного каталога в другой
PATH Отображает или устанавливает путь поиска файлов для операционной системы
PAUSE Приостанавливает обработку команд в командных файлах и отображает сообщение
POPD Восстанавливает предыдущее значение текущего каталога, сохраненного командой PUSHD
PRINT Распечатывает текстовый файл
PROMPT Изменяет вид запроса
PUSHD Сохраняет текущий каталог перед его изменением
RD Удаляет каталог
RECOVER Восстанавливает доступную информацию с запорченных дисков
REM Отмечает комментарии в командных файлах или файле CONFIG.SYS
REN Переименовывает один или несколько файлов
RENAME Переименовывает один или несколько файлов
Стр. 82
отсутствует. 83
Как видите, команд операционной системы DOS не так уж и много и запомнить их не со&
ставляет особого труда. Но даже этого делать не надо (те несколько команд, которые необ&
ходимы для запуска и работы с ассемблером, будут рассмотрены подробно далее, а аналоги
всех команд есть в системе Windows, так как режим DOS просто эмулируется системой
Windows и в конечном итоге производится обращение к процедурам Windows). В Windows 98
и в более ранних версиях можно работать и непосредственно в DOS, так как эти системы
являются только надстройками над DOS, но начиная с версии Windows NT все обстоит
по&другому. Здесь системы DOS в чистом виде уже нет и вся работа производится только
через процедуры Windows.
Чтобы получить более подробное описание каждой команды, необходимо набрать в ко&
мандной строке имя команды и дополнить ее символами ‘‘/?’’. Например, если набрать
cd /?, то получим следующую справку.
Отображает или изменяет имя текущего каталога.
CHDIR [/D] [drive:][path]
CHDIR [..]
CD [/D] [drive:][path]
CD [..]
Две точки (..) указывают, что необходимо перейти к каталогу, включающему данный
каталог.
Если набрать CD без параметров, то отобразится обозначение текущего тома и каталог.
Если набрать CD с обозначением тома, то отобразится текущий каталог.
Параметр /D используется для переключения текущего тома на указанный том с не&
обходимым каталогом.
Команда CHDIR используется в тех случаях, когда имена файлов или каталогов со&
держат пробелы.
Стр. 83
А вот командные файлы, которые существовали еще в первых системах DOS, могут
принести пользу даже при работе в системе Windows. Рассмотрим подробнее команд&
ные файлы.
Командный файл
Как уже отмечалось, в среде DOS работать довольно неудобно, так как приходится вруч&
ную набирать довольно длинные команды и пути файлов, что часто приводит к ошибкам.
Командные файлы созданы для облегчения работы в среде DOS, но и в среде Windows
они тоже приносят довольно ощутимую пользу.
Командный файл &&&& это обычный текстовый файл с последовательностью команд, ко&
торые должна выполнить система. Командный файл можно написать в обычном тексто&
вом редакторе и сохранить с расширением .bat. Файлы с таким расширением операци&
онная система трактует как командные и вызывает для их выполнения интерпретатор
команд, а не текстовый редактор.
В командный файл можно включать все команды DOS, а также команды условного пе&
рехода for, goto и if, которые позволяют реализовывать различную последовательность
выполнения команд в зависимости от наличия определенных условий. Еще несколько
команд позволяют контролировать ввод&вывод и вызывать другие командные файлы.
Контролировать процесс выполнения можно по возвращаемым приложениями кодам
ошибок, которые могут быть равны 0 (ошибок нет) или 1 (большие значения при нали&
чии ошибок).
Командный файл может иметь параметры, которые дописываются в командный файл
при его запуске на выполнение. Для подстановки параметров в команды используются
переменные от %0 до %9. Если используется переменная %0, то вместо ее при запуске
подставляется имя командного файла, а переменные от %1 до %9 заменяются соответст&
вующими аргументами. Для доступа к аргументу за пределами %9 используется команда
shift. Переменная %* ссылается на все аргументы, за исключением %0.
Например, для копирования содержимого каталога Folder1 в каталог Folder2
можно создать командный файл Mybatch.bat, содержимое которого будет представлять
одну строку
xcopy %1\*.* %2
и при вызове этого файла как
Mybatch.bat C:\folder1 D:\folder2
вместо переменной %1 будет подставлен каталог Folder1, а вместо переменной %2 &&&&
каталог Folder2.
Тот же самый результат можно получить, если в среде DOS выполнить команду
xcopy C:\folder1\*.* D:\folder2
Дополнительно с параметрами командного файла можно применять модификаторы.
Модификаторы используют информацию о текущем диске и каталоге для расширения
параметров. При использовании модификаторов сначала поставьте символ процента (%),
затем тильду (~), а за ней &&&& требуемый модификатор.
Все возможные модификаторы перечислены в табл. 3.14.
Стр. 84
отсутствует. 85
Стр. 85
Таблица 3.15. Опции компилятора MASM
Опция Описание
/AT Разрешена тонкая модель памяти (.COM)
/Bl<linker> Использовать альтернативный компоновщик
/c Компилировать без компоновки
/Cp Сохранять регистры пользовательских идентификаторов
/Cu Все идентификаторы в верхнем регистре
/Cx Сохранять регистры для открытых и внешних идентификаторов
/coff Генерировать объектный файл в формате COFF
/D<name>[=text] Задать макроопределение
/EP Вывести листинг препроцессора
/F <hex> Задать размер стека в байтах
/Fe<file> Ввести имя исполняемого файла
/Fl[file] Генерировать листинг
/Fm[file] Генерировать карту распределений
/Fo<file> Ввести имя объектного файла
/Fpi Эмулировать код 80×87
/Fr[file] Генерировать ограниченную обзорную информацию
/FR[file] Генерировать полную обзорную информацию
/G<c|d|z> Использовать вызовы Pascal, C или Stdcall (по умолчанию Stdcall)
/H<number> Установить максимальную длину внешних имен
/I<name> Добавить пути для подключаемых файлов
/link Задать <Опции компоновщика и библиотеки>
/nologo Не выводить авторские права
/omf Генерировать объектный файл в формате OMF
/Sa Задать максимальный размер листинга
/Sc Генерировать временные метки
/Sf Генерировать листинг первого прохода
/Sl<width> Задать длину строки
/Sn Не выводить таблицу перекрестных ссылок
/Sp<length> Задать длину страницы
/Ss<string> Задать подзаголовок
/St<string> Задать заголовок
/Sx Выводить ошибочные условия
/Ta<file> Компилировать файл с не .ASM&расширением
/w То же самое, что и /W0 /WX
/WX Трактовать предупреждения как ошибки
/W<number> Задать уровень предупреждений
/X Игнорировать путь к INCLUDE
/Zd Добавить номера строк в отладочную информацию
Стр. 86
отсутствует. 87
TITLE (.asm)
.386
.model flat, stdcall
ExitProcess PROTO,
x:dword
WaitMsg PROTO
WriteString PROTO
Crlf PROTO
00000000 .data
00000000 48 65 6C 6C 6F strHello BYTE "Hello Word!",0
20 57 6F 72 64
21 00
00000000 .code
Стр. 87
00000000 main PROC
00000000 BA 00000000 R
mov EDX, OFFSET strHello ; Аргументы для WriteString.
invoke WriteString ; Вывод строки на консоль.
invoke Crlf ; Перевод каретки.
invoke WaitMsg ; Запрос для нажатия клавиши.
invoke ExitProcess,0 ; Корректное окончание программы.
0000001B main ENDP
END main
Microsoft (R) Macro Assembler Version 6.15.8803 08/15/06 16:28:02
(.asm) Symbols 2 - 1
Segments and Groups:
N a m e Size Length Align Combine Class FLAT GROUP
_DATA . . 32 Bit 0000000C DWord Public 'DATA'
_TEXT . . 32 Bit 0000001B DWord Public 'CODE'
Symbols:
0 Warnings
0 Errors
В этом листинге приведена вся информация о созданной программе, начиная с ис&
ходных команд и их машинных эквивалентов и заканчивая распределением имен, памяти
и сегментов. При профессиональном программировании приходится довольно часто об&
ращаться к файлам листингов, но на первом этапе изучения языка в этом большой необ&
ходимости нет.
Синтаксические ошибки
Очень немного найдется программистов, которые сразу смогут написать даже неболь&
шую программу без ошибок. Поэтому ассемблер проверяет написанные команды и вы&
водит на экран все строки, в которых есть ошибки, причем с их объяснением. Например,
если в программе Hello первую команду MOV ошибочно набрать как MIV, то появится
следующее сообщение:
Стр. 88
отсутствует. 89
Assembling: Hello.asm
Hello.asm(17) : error A2008: syntax error : edx
Иными словами, компилятор зафиксирует синтаксическую ошибку перед операндом
edx в строке номер 17.
Компоновка программы
После компиляции будет получен объектный файл с расширением .obj, который не&
обходимо преобразовать в исполняемый модуль. На данном этапе используется про&
грамма&компоновщик, которая использует объектный файл (в данном случае
hello.obj) в качестве входного и создает выполняемый файл, называя его hello.exe.
Командная строка для компоновщика имеет следующий формат:
LINK [options] [files] [@commandfile]
Здесь link &&&& имя программы компоновщика, files &&&& файлы библиотек.
Не будем рассматривать все опции компоновщика, так как на первом этапе они не приго&
дятся. Необходимо знать только опцию /DEBUG (она заставляет компоновщик вставлять
в создаваемую программу нужные для отладчика коды) и опцию /SUBSYSTEM:CONSOLE
(приводит к созданию формата файла, который ‘‘понимает’’ Windows и рассчитан на ра&
боту в консольном режиме). Чтобы просмотреть все опции 32&разрядного отладчика,
просто наберите в командном окне имя отладчика link32. Также можно использовать
опцию /MAP, которая приводит к созданию файла распределения памяти с расширением
.map. Попробуйте создать этот файл и проанализируйте его. От его использования тоже
может быть ощутимая польза.
После работы компоновщика будет создан исполняемый файл с расширением .exe,
который уже можно запускать на выполнение как любую программу Windows.
Использование программы ML позволяет выполнять компиляцию и компоновку одной
командой. Но можно использовать отдельно компилятор (MASM) и 32&разрядный компо&
новщик (LINK32). Также необходимо отметить, что версии 32&разрядного компоновщика,
которые Microsoft использует в более поздних версиях, чем MASM615, называются просто
LINK, без цифр 32 (в версии MASM615 так называется 16&разрядный компоновщик). На&
чиная с этой версии, Microsoft уже не разрабатывает 16&разрядные компоновщики.
Запуск программы
Для запуска программы наберите в командной строке имя выполняемой программы
hello или просто щелкните на ней мышкой в среде Windows.
Отладчик
Основной инструмент, с которым приходится работать при создании программ на ас&
семблере, &&&& отладчик. В дальнейшем будут рассматриваться небольшие примеры про&
грамм на языке ассемблера, и лучшим способом для их изучения является использование
отладчика. Отладчик &&&& это программа, позволяющая отображать на экране значения необ&
ходимых переменных, получать состояние всех регистров и ячеек памяти при пошаговом
выполнении программы, вносить изменения в программу, указывать точки останова
и многое другое. Это необходимо при проверке написанных на языке ассемблера программ.
Существует несколько хороших отладчиков, но мы будем использовать универсаль&
ный и довольно удобный отладчик Microsoft, который распространяется бесплатно и на&
зывается dbg_x86_6.5.3.8.exe. Этот отладчик имеет оконный интерфейс и привыч&
ные для работающих в среде Windows функции.
Стр. 89
Простая программа
Для знакомства с отладчиком напишем на языке ассемблера небольшую программу
Sum, которая складывает три числа и сохраняет сумму в памяти. Оттранслируем и вы
полним компоновку программы с использованием опций отладки, как показано на
рис. 3.14, а затем начнем работу с отладчиком.
Программа Sum
TITLE Программа суммирования (sum.asm)
.386
.MODEL flat, stdcall ; Задаем модель памяти.
WaitMsg PROTO ; Прототип функции WaitMsg.
ExitProcess PROTO, ; Прототип функции ExitProcess.
x: DWORD
Стр. 90
отсутствует. 91
Стр. 91
пользователя, создавая удобную для себя среду отладки. Все окна по умолчанию стараются
пристыковаться к ближайшей границе (dock) окна, хотя их можно сделать и плавающими
(floating). Потребуется некоторая практика, чтобы быстро создавать удобный рабочий стол от&
ладчика. Щелчок правой кнопкой мыши на заголовке окна приводит к появлению контекст&
ного меню, которое позволяет установить необходимый режим работы окна.
Окна можно открывать либо воспользовавшись пунктом меню Window, либо с помо&
щью отдельных кнопок, расположенных на управляющей панели. Названия кнопок, ко&
торые появляются в момент задержки курсора на кнопке, перечислены в табл. 3.16.
Стр. 92
отсутствует. 93
выполняя команды Step into, Step over, Run to cursor и другие. Результаты, получаемые
в процессе работы программы, будут отображаться в соответствующем консольном окне.
В окнах отладчика можно видеть как команды ассемблера, так и коды машинных ко&
манд. Все это не только значительно помогает в отладке программы, но и удобно при изу&
чении языка ассемблера, а также в процессе исследования операционной системы и аппа&
ратной части компьютера. Фрагмент дисассемблированного текста показан в табл. 3.17.
Sum!main:
00401038 b805000000 mov eax,0x5
0040103d 83c00a add eax,0xa
00401040 83c00f add eax,0xf
00401043 66a300404000 mov [Sum!sum (00404000)],ax
00401049 e8cbffffff call Sum!ILT+20(_WaitMsg (00401019)
0040104e 6a00 push 0x0
00401050 e8b7000000 call Sum!ExitProcess (0040110c)
Резюме
В этой главе даны общие сведения о языке ассемблера, используемых форматах дан&
ных и принципах разработки программ на языке ассемблера. Описана работа с ассембле&
ром и основные этапы разработки программ с использованием ассемблера. Акцент сде&
лан на разработке библиотек, использование которых значительно облегчает работу
с программой. Отмечена важность этапа отладки программ и приведены базовые сведе&
ния о работе с отладчиком. Для лучшего понимания материала вниманию читателя пред&
ставлены простейшие примеры разработки и отладки программ.
В данной главе приводились примеры довольно длинных последовательностей ко&
манд, которые необходимы для ассемблирования исходного файла. Конечно, команды
можно набирать вручную, но лучше использовать командные файлы, которые позволяют
значительно облегчить работу. Существенно сэкономить время позволит использование
специального интерфейса пользователя (который можно найти на специальных сайтах
или разработать самому, если вы знаете язык высоко уровня). Такой интерфейс может
создавать необходимые последовательности команд при нажатии определенных кнопок
и запускать их на выполнение.
Четкое представление последовательности шагов, выполняемых ассемблером при об&
работке исходной программы и получении исполняемого файла, значительно облегчит
вашу работу в дальнейшем.
Стр. 93
Контрольные вопросы
1. Существует ли взаимно однозначное соответствие между командами языка ас&
семблера и машинными кодами?
2. Можно ли написать программу в машинных кодах?
3. Сколько бит находится в байте, слове, двойном и учетверенном слове?
4. Подсчитайте диапазон значений для слова без знака.
5. Что такое команды и что такое данные?
6. Что такое основание системы счисления?
7. Почему удобно использовать шестнадцатеричную систему счисления для отобра&
жения данных?
8. Как представлены в памяти числа без знака и со знаком?
9. Что такое дополнение до двух?
10. Как сохраняется в памяти строка символов? Как подсчитать размер занимаемой
памяти для отдельной строки?
11. Как сохраняется в памяти числовое значение? Как подсчитать размер занимаемой
памяти для числового значения?
12. Что такое константа? Чем константа отличается от переменной?
13. Какие типы утверждений используются в языке ассемблера?
14. Как разрабатывается программа на языке ассемблера?
15. Назовите основные этапы выполнения программы.
16. Назовите основные опции компилятора.
17. Для чего нужен отладчик?
18. Каковы особенности работы DOS в операционной системе Windows NT?
19. Можно ли использовать все возможности языка ассемблера при работе в DOS под
управлением Windows NT?
20. Чем отличаются ассемблеры Microsoft и Borland?
Стр. 94
Глава 4
Программирование
для Windows
В этой главе...
В этой главе рассмотрен процесс создания 32&разрядных приложений для работы только
с консолью. Разработку графических окон в стиле Windows вы изучите после того, как
ознакомитесь с основными командами и синтаксисом ассемблера. В этой главе также бу&
дут представлены некоторые функции операционной системы Windows и приведены от&
дельные примеры. Для более полного знакомства с библиотеками функций Windows
можно установить Microsoft Visual Studio, Delphi или Borland C++ и использовать соот&
ветствующую документацию. Справочную информацию по Windows вы найдете на сайте
www.msdn.microsoft.com.
Стр. 95
Под термином интерфейс прикладного программирования (API &&&& Application Programming
Interface) понимается набор типов, констант и функций, которые позволяют непосредст&
венно управлять объектами из программы. В дальнейшем мы будем использовать интер&
фейс Win32 API, который обеспечивает доступ к 32&разрядным версиям объектов плат&
формы MS&Windows.
С Win32 API непосредственно связан набор инструментальных средств разработки
платформы Microsoft (SDK &&&& Software Development Kit), содержащий прикладные про&
граммы, библиотеки, примеры кода и документацию, которые помогают программисту
создавать программы. Слово платформа означает операционную систему или группу тес&
но взаимосвязанных операционных систем.
При запуске приложения для Windows оно создает или консольное окно (консоль),
или графическое окно.
Для того чтобы получить консольное приложение, работающее в защищенном режи&
ме, необходимо при вызове компоновщика использовать опцию
/SUBSYSTEM:CONSOLE.
Программа для консоли ведет себя точно так, как и программа для MS&DOS, за ис&
ключением небольших улучшений. Чтение и запись происходят в стандартные входы
и выходы. Ошибки также записываются в стандартный файл ошибок. Консоль имеет
один входной буфер и один или несколько экранных буферов.
Входной буфер содержит последовательность входных записей, каждая из которых
включает данные о входных событиях (например, нажатие клавиш, щелчок мыши
или изменение размеров окна).
Экранный буфер содержит двумерный массив символов и данных о цвете, кото&
рые необходимы для отображения символов на экране.
Уровни доступа
Можно использовать два уровня доступа: высокий и низкий, для простоты доступа
или полноты контроля соответственно.
Функции высокого уровня считывают поток символов из входного буфера консо&
ли. Выходные данные записываются в экранный буфер консоли. Как вход, так и вы&
ход может быть перенаправлен для чтения или записи в текстовый файл.
Стр. 96
отсутствует. 97
Стр. 97
Дескрипторы консоли
Почти все функции для работы с консолью требуют передачи дескриптора в качестве
первого аргумента. Дескриптор является 32&разрядным целым числом без знака, которое
уникально идентифицирует объект, такой как растровое изображение (bitmap) или чер&
тежное перо, либо входные и выходные устройства. В дальнейшем в главе по мере изло&
жения материала будут использоваться дескрипторы для следующих устройств:
STD_INPUT_HANDLE &&&& стандартный вход;
STD_OUTPUT_HANDLE &&&& стандартный выход;
STD_ERROR_HANDLE &&&& стандартный выход ошибок.
Будут также представлены дескрипторы для записи в экранные буферы.
Функция GetStdHandle возвращает дескриптор потока для консоли: вход, выход
или выход ошибок. Дескриптор необходим для создания входа&выхода в программах, ра&
ботающих с консолью. Ниже показан прототип функции.
GetStdHandle PROTO, nStdHandle:DWORD
Функция GetStdHandle может принимать значения STD_INPUT_HANDLE,
STD_OUTPUT_HANDLE или STD_ERROR_HANDLE. Эти константы описаны в стандартных
заголовочных файлах и имеют значения &&10, &&11 и &&12 соответственно. Функция возвра&
щает значение дескриптора в регистр EAX, которое должно быть сохранено для дальней&
шего использования. Ниже показан небольшой пример.
.DATA
inputHandle DWORD ?
.CODE
INVOKE GetStdHandle, STD_INPUT_HANDLE
MOV inputHandle,EAX
Поскольку константа STD_INPUT_HANDLE определяет устройство, для которого
в Windows используется численное значение –10, то в данном случае вместо идентифика&
тора STD_INPUT_HANDLE можно подставить число –10. Однако использование понятного
идентификатора значительно облегчает работу и освобождает от запоминания абсолютно
не информативных чисел. К тому же идентификатору STD_INPUT_HANDLE где&то должно
быть присвоено значение –10. Обычно это делается в заголовочных (подключаемых)
файлах, которые имеют расширение .inc и которые должны быть подключены к вашей
библиотеке или программе с помощью ключевого слова INCLUDE. Например, так:
INCLUDE G:/Masm/MyLib.inc
А в самом подключаемом файле должна находиться следующая строка
STD_INPUT_HANDLE = -10
или
STD_INTPUT_HANDLE EQU -11
Поэтому вы должны создать собственный заголовочный файл, в котором будете опре&
делять часто используемые константы, и подставлять его в свои программы. Такие про&
граммы будут более читабельны, поскольку в них будет отсутствовать информация, не
имеющая отношения к логической сути программы. А компилятор просто подставит все,
что находится в этом файле, вместо строки со словом INCLUDE.
Стр. 98
отсутствует. 99
Стр. 99
Окончание табл. 4.2
Функция Описание
ReadConsoleOutputCharacter Копирует указанное число последовательных символов
из экранного буфера консоли
ScrollConsoleScreenBuffer Перемещает блок данных в экранный буфер
SetConsoleActiveScrecnBuffer Устанавливает указанный экранный буфер как текущий
экранный буфер консоли
SetConsoleCP Устанавливает входную кодовую страницу, используемую
консолью для вызвавшего процесса
SetConsoleCtrlHandler Добавляет или удаляет определенные дескрипторы в приложении
HandlerRoutine из списка обработчиков функций для
вызываемого процесса
SetConsoleCursorlnfo Устанавливает размер и видимость курсора для экранного
буфера консоли
SetConsoleCursorPosition Устанавливает позицию курсора в экранном буфере консоли
SetConsoleMode Устанавливает режим ввода для входного буфера консоли
или режим вывода для экранного буфера консоли
SetConsoleOutputCP Устанавливает используемую консолью кодовую страницу
для вывода, связанную с вызываемым процессом
SetConsoleScreenBufferSize Изменяет размер экранного буфера консоли
SetConsoleTextAttribute Устанавливает цветовые атрибуты текста и фона символов,
записываемых в экранный буфер консоли
SetConsoleTitle Устанавливает заголовок текущего окна консоли
SctConsoleWindowInfo Устанавливает текущий размер и положение окна для
экранного буфера консоли
SetStdHandle Устанавливает дескриптор для стандартного входа,
стандартного выхода и стандартного выхода ошибок
WriteConsole Записывает символьную строку в экранный буфер консоли,
начиная с текущего положения курсора
WriteConsoleInput Записывает данные непосредственно в выходной буфер
консоли
WriteConsoleOutput Записывает символ и цветовые атрибуты в указанный
прямоугольный блок в экранном буфере консоли
WriteConsoleOutputAttribute Копирует несколько цветовых атрибутов для текста и фона
в последовательные ячейки экранного буфера консоли
WriteConsoleOutputCharacter Копирует несколько символов в последовательные ячейки
экранного буфера консоли
Использование функций
Для того чтобы лучше понять, как пишутся программы для платформы Win32, подробно
рассмотрим небольшой пример вывода на консоль текста. Ниже приведен полный листинг
программы, который можно ассемблировать, скомпоновать и выполнить. Номера строк
в программу не входят и добавлены для дальнейших ссылок из текста на отдельные строки.
Стр. 100
отсутствует. 101
Стр. 101
которого они будут браться и который должен быть объявлен при компоновке. В нашем
случае это модуль KERNEL32.LIB. Этот модуль входит в поставку ассемблера и названия
указанных функций написаны в модуле следующим образом:
_GetStdHandle@4
_WriteConsoleA@20
_ExitProcess@4
Написание функций отличается от того, что приведено в таблице функций для консо&
ли, но ассемблер автоматически приводит их к виду, используемому в библиотеке функ&
ций, которые создавались для языка C. В соответствии с соглашениями для этого языка,
системные функции должны иметь в начале имени символ подчеркивания ‘‘_’’, а в конце
должно указываться количество байт, отводимых для параметров. Именно этим объясня&
ется такое несколько странное написание, хотя можно использовать макроопределения
и писать имена функций так, как они приведены в таблице (см. далее).
После описания функций идет описание того сообщения, которое должно быть выве&
дено на экран.
message BYTE "-------------- Console1.asm --------------------"
BYTE endl,endl
BYTE "Программа демонстрирует вывод на консоль сообщения,",endl
BYTE "которое вы сейчас видите. Использованы функции",endl
BYTE "Windows GetStdHandle и WriteConsole ",endl
BYTE "---------------------------------------------------------"
Перед этим сообщением в строке 11 объявляется переменная endl, определяющая
два символа, которые приводят к переходу на новую строку и помещению курсора в на&
чало строки (возврат каретки).
После объявления данных идет непосредственно раздел кодов, в котором функции
вызываются с помощью директивы CALL. При этом перед каждым вызовом происходит
помещение необходимых аргументов в стек в порядке справа налево, приведенном в опи&
сании функции. Например, функция WriteConsoleA представлена в справочнике сле&
дующим образом.
BOOL WriteConsole(
HANDLE hConsoleOutput, // Дескриптор экранного буфера консоли.
CONST VOID *lpBuffer, // Указатель на исходный текст.
DWORD nNumberOfCharsToWrite, // Количество символов для записи.
LPDWORD lpNumberOfCharsWritten, // Указатель на количество
// записанных символов.
LPVOID lpReserved // Зарезервировано.
);
Как видно из листинга 4.1, для данной функции сначала в стек помещается 0, затем
указатель на количество записанных символов и т.д.
Теперь запишем ту же программу с использованием макроопределений. Выглядеть
она будет так (листинг 4.2).
Стр. 102
отсутствует. 103
.DATA
endl EQU <0dh,0ah> ; Конец строки.
message \
BYTE "-------------------- Console1.asm -----------------------"
BYTE endl,endl
BYTE "Программа демонстрирует вывод на консоль сообщения,",endl
BYTE "которое вы видите. Использованы функции Windows",endl
BYTE "GetStdHandle и WriteConsole ",endl
BYTE "---------------------------------------------------------"
BYTE endl,endl,endl
messageSize = ($-message)
consoleHandle DWORD 0 ; Дескриптор стандартного выходного устройства.
bytesWritten DWORD ? ; Количество записанных байт.
.CODE
main PROC
; Получить дескриптор стандартного выходного устройства.
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
MOV consoleHandle,EAX
; Закончить выполнение.
INVOKE ExitProcess,0
main ENDP
END main
Стр. 103
Этот файл подключается к основной программе с помощью директивы INCLUDE.
INCLUDE E:\MASM615\INCLUDE\MyLib.inc
В таком варианте уже не надо искажать оригинальные имена функций &&&& все это
будет сделано автоматически. Вызов функций производится с помощью директивы
INVOKE. В справочном отделе приведены полная библиотека необходимых функций
и заголовочные файлы.
Дальше рассмотрим, как можно произвести ввод информации с консоли.
Ввод с консоли
Консоль Win32 имеет входной буфер, содержащий массив входных записей. Каждое
событие, такое как нажатие клавиши, перемещение мыши или щелчок кнопкой мыши,
создает запись во входном буфере консоли. Функции высокого уровня ReadConsole
фильтруют и обрабатывают входные данные, возвращая только поток входных символов.
Функция ReadConsole
Функция ReadConsole используется для считывания текстовых данных и помеще&
ния их в буфер. Ниже приведен прототип функции.
ReadConsole PROTO,
handle:DWORD, ; Дескриптор входа.
pBuffer:PTR BYTE, ; Указатель на буфер.
maxBytes:DWORD, ; Количество символов для чтения.
pBytesRead:PTR DWORD, ; Указатель на считанные символы.
notUsed:DWORD ; Не используется.
Параметр handle &&&& это дескриптор, возвращаемый функцией GetStdHandle. Па&
раметр pBuffer представляет адрес символьного массива. Параметр maxBytes является
32&разрядным целым числом и указывает максимальное число символов, которые долж&
ны быть считаны. Параметр pBytesRead указывает на двойное слово, которое означает
число считанных символов. Последний параметр не используется и его можно заменять
любым числом, например нулем.
Ниже приведен пример программы для считывания данных с консоли.
Стр. 104
отсутствует. 105
MOV consoleHandle,EAX
; Отобразить буфер.
INVOKE WriteConsoleA, consoleHandle, ADDR buffer, BufSize-2,
ADDR bytesRead, 0
INVOKE Crlf
INVOKE WaitMsg
EXIT
main ENDP
END main
Стр. 105
тановка всех флагов в нуль, как показано в примере ниже, где выполняется ввод одиноч&
ного символа.
.DATA
saveFlags DWORD ? ; Копия флагов.
.CODE
Структуры данных
Некоторые функции консоли Win32 используют предопределенные структуры дан&
ных, такие как COORD и SMALL_RECT. Структура COORD содержит координаты экрана X и Y,
измеряемые в символах. Соответственно они могут принимать значения от 0 до 79 и от 0 до 24.
COORD STRUCT
X WORD ?
Y WORD ?
COORD ENDS
Функция WriteConsoleOutputCharacter
Эта функция, прототип которой представлен ниже, копирует массив символов в по&
следовательные ячейки экранного буфера консоли, начиная с указанного места.
WriteConsoleOutputCharacter PROTO,
handleScreenBuf:DWORD, ; Дескриптор консоли.
pBuffer:PTR BYTE, ; Указатель на буфер.
Стр. 106
отсутствует. 107
Стр. 107
Листинг 4.5. Вывод опций для Microsoft LIB
Microsoft (R) Library Manager Version 3.20.010
Copyright (C) Microsoft Corp 1983-1992. All rights reserved.
Usage: LIB library [options][commands][.listfile[.newlibrary]]
Options:
/? ; отобразить опции LIB
/HELP ; отобразить HELP для LIB
/IGNORECASE ; игнорировать регистр в именах
/NOEXTDICTIONARY ; не создавать расширенный словарь
/NOIGNORECASE ; не игнорировать регистр в именах
/NOLOGO ; не отображать поле заголовка
/PAGESIZE:n ; установить размер страницы n
Commands:
+name ; добавить объектный файл
-name ; стереть объектный файл
-+name ; заменить объектный файл
*name ; скопировать объектный файл
-*name ; переместить объектный файл
Если ввести команду lib /help, то появится окно с перечнем всех доступных спра&
вочных данных, как показано на рис. 4.1.
Стр. 108
отсутствует. 109
WriteString PROC
; Записывает строку с нулевым окончанием в стандартный
; выход. Входной параметр EDX указывает на строку.
PUSHAD
; Получаем дескриптор консоли.
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
MOV [consoleOutHandle],EAX
; Получаем длину строки.
INVOKE Str_length,EDX ; Возврат длины строки в EAX.
CLD ; Сброс флага направления.
; Выводим строку на консоль.
INVOKE WriteConsoleA,
consoleOutHandle, ; Дескриптор консоли.
EDX, ; Указывает на строку.
EAX, ; Длина строки.
OFFSET bytesWritten, ; Число записанных байт.
0
POPAD
RET
WriteString ENDP
END
Данный модуль включает две процедуры: процедуру для подсчета длины строки
Str_length, которая возвращает количество символов в строке, и процедуру вывода
строки на консоль WriteString, в которой используется значение, возвращаемое про&
цедурой Str_length.
Все команды, используемые в этих процедурах, будут подробно описаны в дальней&
ших главах. Изучив их, всегда можно вернуться к этим программам и понять все тонко&
сти использования команд ассемблера. А пока отметим только тот факт, что ассемблерную
программу удобно разбить на фрагменты, которые легко разрабатывать и тестировать от&
дельно от основной программы. В дальнейшем все эти фрагменты быстро состыковывают&
ся и из отдельных процедур, которые не требуют дополнительной отладки, можно собрать
большую программу. В этом случае время, затрачиваемое на разработку программы, бу&
дет минимальным.
Для того чтобы получить объектный файл, нужно ассемблировать созданный модуль
c необходимыми процедурами. Для этого можно использовать команду
..\Masm615\ML -c -coff
Стр. 109
или написать командный файл, при условии, что ассемблер находится в каталоге
E:\Masm615, со следующей строкой:
E:\Masm615\ML -c -coff %1.asm
В данном случае ассемблер находится на диске E. На вашем компьютере все может
быть установлено по&другому, поэтому внесите соответствующие изменения.
После ассемблирования будет получен объектный файл с именем WriteString.obj,
который необходимо расположить в каталоге с библиотеками.
Теперь можно использовать этот файл в процессе компоновки модулей. При этом ес&
ли раньше командный файл выглядел следующим образом:
ML -Zi -c -Fl -coff %1.asm
if errorlevel 1 goto terminate
LINK32 %1.obj E:\MASM615\LIB\KERNEL32.LIB /SUBSYSTEM:CONSOLE /DEBUG
if errorLevel 1 goto terminate
dir %1.*
:terminate
pause
то в данном случае строку с командой LINK32 запишем так:
LINK32 %1.obj E:\MASM615\LIB\KERNEL32.LIB
Е:\MASM615\LIB\WRITESTRING.OBJ /SUBSYSTEM:CONSOLE /DEBUG
Лучше переименовать этот файл и назвать его MyLib.obj. В дальнейшем мы будем
его дополнять и создавать собственную библиотеку функций, необходимых для работы.
Например, возьмем программу вывода заранее подготовленного текста на консоль.
Без использования созданных нами процедур она будет выглядеть так:
Стр. 110
отсутствует. 111
Как видно, вторая программа занимает меньше места и ее назначение понятнее, чем в пер&
вом случае. В этой программе обратите внимание на нуль, который появился в конце тек&
ста, предназначенного для вывода на консоль. Дело в том, что процедура WriteString ра&
ботает только со строками с нулевым окончанием, и подсчет количества символов
производится до появления байта с нулевым значением. Для вызова процедур здесь исполь&
зуются директивы CALL и INVOKE. Их назначение приблизительно одинаковое, но дирек&
тива INVOKE более мощная и с ее помощью в процедуры можно передавать параметры.
Теперь объектный файл WriteString.obj можно преобразовать в библиотечный файл
с расширением .lib. Но здесь возникает определенная сложность, так как утилита LIB,
которая входит в поставку ассемблера, рассчитана на работу только с 16&разрядными про&
граммами. Поэтому для того чтобы создавать библиотеки с расширением .lib для 32&раз&
рядных программ, используйте утилиту LIB из поставки Microsoft Visual Studio, кото&
рую можно найти в каталоге ...\Microsoft Visual Studio\VC98\BIN\LIB\ или
...\MSVNET\VC7\BIN\.
Стр. 111
Работа с файлами
Функция создания файла
Функция CreateFile или создает новый файл, или открывает уже существующий.
При успешном выполнении операции возвращается дескриптор открытого файла, в про&
тивном случае возвращается константа INVALID_HANDLE_VALUE. Ниже показан прото&
тип функции.
CreateFile PROTO,
pFilename:PTR BYTE, ; Указатель на имя файла.
desiredAccess:DWORD, ; Режим доступа.
shareMode:DWORD, ; Режим совместной работы.
IpSecurity:DWORD, ; Указатель на атрибуты защиты.
creationDisposition:DWORD, ; Опции создания файла.
flagsAndAttributes:DWORD, ; Атрибуты файла.
htemplate:DWORD ; Дескриптор шаблона файла.
Первый параметр указывает на строку с нулевым окончанием, содержащую или частич&
ное, или полностью определенное имя (drive:\path\filename). Параметр desiredAccess
указывает, как файл будет использоваться (чтение или запись). Параметр shareMode
определяет возможность доступа к файлу из нескольких программ, когда файл открыт.
Параметр creationDisposition указывает, какие действия необходимо предприни&
мать, если файл уже существует или не существует. Параметр flagsAndAttributes со&
стоит из отдельных флагов, которые определяют такие атрибуты файла, как архивный, шиф&
рованный, скрытый, обычный, системный и временный. Параметр htemplate включает
дополнительный дескриптор для временного файла, который содержит атрибуты файла
и расширенные атрибуты для создания файла. Несмотря на то что этот параметр не ис&
пользуется, он должен передаваться в функцию со значением 0.
Параметр DesiredAccess
Устанавливая этот параметр, можно получить режимы только для чтения, только для
записи, для записи/чтения или для устройства последовательного доступа. В табл. 4.3
приведено краткое описание всех режимов.
Параметр CreationDisposition
Это параметр действия, которое будет выполняться в зависимости от того, существует
или не существует файл с заданным именем. Параметр может иметь одно из приведенных
в табл. 4.4 значений.
Стр. 112
отсутствует. 113
Стр. 113
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0
Создание нового файла с нормальными атрибутами, удаление существующего файла
с указанным именем.
INVOKE CreateFile,
ADDR filename,
GENERIC_WRITE,
DO_NOT_SHARE,
NULL,
CREATE_ALWAYS, ; Переписать существующий файл
FILE_ATTRIBUTE_NORMAL,
0
Создать файл, если файла с указанным именем еще не существует.
INVOKE CreateFile,
ADDR filename,
GENERIC_WRITE,
DO_NOT_SHARE,
NULL,
CREATE_NEW, ; Не удалять существующий файл
FILE_ATTRIBUTE_NORMAL,
0
При этом в подключаемом файле должны быть определены следующие константы:
DO_NOT_SHARE = 0
NULL = 0
Функция CloseHandle
Эта функция используется для открытия и закрытия дескриптора файла. Ее прото&
тип такой:
CloseHandle PROTO, handle:DWORD
Функция ReadFile
Эта функция считывает текст из входного файла. Функция ReadFile может исполь&
зоваться в асинхронном режиме. Это означает, что программе не нужно ожидать, когда
считывание всего файла будет завершено. Прототип функции следующий:
ReadFile PROTO, ; Считывание в буфер из входного файла.
handle:DWORD, ; Дескриптор файла.
pBuffer:PTR BYTE, ; Указатель на буфер.
nBufsize:DWORD, ; Количество байт для считывания.
pBytesRead:PTR DWORD, ; Количество реально считанных байт.
pOverlapped:PTR DWORD ; Указатель на информацию асинхронного режима.
Параметр handle является дескриптором открытого файла, возвращаемого функци&
ей CreateFile. Параметр pBuffer указывает на буфер, в который будут записываться
данные из файла. Параметр nBufsize определяет максимальное число байт для считы&
вания из файла. Параметр pBytesRead указывает на целочисленную переменную, кото&
рая содержит число реально считанных байт. Дополнительный параметр pOverlapped
указывает на структуру, которая определяет, как функция может считывать файл в асин&
хронном режиме. Для оператора синхронизации по умолчанию передается указатель на
нулевое значение.
Стр. 114
отсутствует. 115
Функция WriteFile
Эта функция записывает данные в файл, используя дескриптор выходного устройст&
ва. Дескриптором может быть и дескриптор экранного буфера или дескриптор одного из
текстовых файлов. Функция начинает запись в файл с позиции, определяемой внутрен&
ним указателем файла. По окончании записи указатель устанавливается на позицию по&
сле последнего записанного байта. Прототип функции следующий:
WriteFile PROTO,
fileHandle:DWORD, ; Дескриптор выходного устройства.
pBuffer:PTR BYTE, ; Указатель на буфер.
nBufsize:DWORD, ; Размер буфера.
pBytesWritten:PTR DWORD, ; Число записанных байт.
pOverlapped:PTR DWORD ; Указатель на информацию асинхронного режима.
Стр. 115
0 ; Флаг перекрытия исполнения.
INVOKE CloseHandle, fileHandle
QuitNow:
INVOKE ExitProcess,0 ; Конец программы.
main ENDP
END main
Стр. 116
отсутствует. 117
Стр. 117
INVOKE WaitMsg
INVOKE ExitProcess,0 ; Конец программы.
main ENDP
END main
Управление окном
Интерфейс Win32 API предоставляет некоторые возможности по управлению окном кон
соли и экранным буфером, который содержит отображаемые на экране данные. На рис. 4.2
показано соответствие экранного буфера и отображаемого на консоли текста. Информа
ция, сохраняемая в экранном буфере, значительно превышает размер отображаемого
текста, который является небольшой частью экранного буфера.
Стр. 118
отсутствует. 119
Функция SetConsoleTitle
Эта функция позволяет изменить название окна консоли. Например, можно назвать
окно ‘‘Моя программа’’.
.DATA
titleStr BYTE "Моя программа",0
.CODE
INVOKE SetConsoleTitle, ADDR titleStr
Функция GetConsoleScreenBufferlnfo
Эта функция возвращает информацию о текущем состоянии окна консоли. Функция
содержит два параметра: дескриптор экранного буфера консоли и указатель на структуру,
которая заполняется функцией.
GetConsoleScreenBufferlnfo PROTO,
outHandle:DWORD, ; Дескриптор экранного буфера консоли.
pBufferlnfo:PTR CONSOLE_SCREEN_BUFFER_INFO
Структура CONSOLE_SCREEN_BUFFER_INFO, с которой работает функция, имеет
следующий вид:
CONSOLE_SCREEN_BUFFER_INFO STRUCT
dwSize COORD <>
dwCursorPos COORD <>
wAttributes WORD ?
srWindow SMALL_RECT <>
maxWinSize COORD <>
CONSOLE_SCREEN_BUFFER_INFO ENDS
Параметр dwSize возвращает размер экранного буфера в количествах символов по длине
и ширине. Параметр dwCursorPos возвращает положение курсора. Оба поля имеют тип COORD.
Параметр wAttributes возвращает цвета символов и фона символов, записанных функ&
циями, такими как WriteConsole. Параметр srWindow возвращает координаты отобра&
жаемого текста относительно экранного буфера. Параметр maxWinSize возвращает мак&
симально допустимый размер окна консоли на основе размера экранного буфера, размера
шрифта и отображаемых размеров. Ниже показан пример использования функции.
.DATA
consolelnfo CONSOLE_SCREEN_BUFFER_INFO <>
.CODE
INVOKE GetConsoleScreenBufferInfo, outHandle, ADDR consoleInfo
Стр. 119
Функция SetConsoleWindowlnfo
Эта функция позволяет установить размеры и положение окна консоли относительно
экранного буфера. Ее прототип следующий:
SetConsoleWindowInfo PROTO, ; Установить положение окна консоли.
nStdHandle:DWORD, ; Дескриптор экранного буфера.
bAbsolute:DWORD, ; Тип координат.
pConsoleRect:PTR SMALL_RECT ; Указатель на границы окна.
Параметр bAbsolute определяет, на что указывают координаты структуры, на кото&
рую указывает параметр pConsoleRect. Если значение bAbsolute равно true, то ко&
ординаты определяют верхний левый и нижний правый углы окна, в противном случае
координаты должны быть добавлены к текущим оконным координатам.
Ниже показана программа, которая записывает пятьдесят строк текста в экранный
буфер. Затем размеры их изменяются и строки перемещаются в зону отображения в окне
консоли. Используется функция SetConsoleWindowInfo.
Листинг 4.11. Прокрутка окна консоли
TITLE Прокрутка окна консоли (Scroll.asm)
.386
.MODEL flat, stdcall
.DATA
STD_OUTPUT_HANDLE EQU -11
message BYTE ": Эта строка будет записана "
BYTE "в экранный буфер",0dh,0ah
messageSize = ($-message)
outHandle DWORD 0 ; Дескриптор стандартного выхода.
bytesWritten DWORD ? ; Число записанных байт.
lineNum DWORD 0
windowRect SMALL_RECT <0,0,60,11> ; Левый, верхний, правый, нижний.
.CODE
main PROC
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
MOV outHandle,EAX
.REPEAT
MOV EAX,lineNum
CALL WriteDec ; Отобразить каждый номер.
INVOKE WriteConsoleA,
outHandle, ; Дескриптор консоли.
ADDR message, ; Указатель на строку.
messageSize, ; Длина строки.
ADDR bytesWritten, ; Количество записанных байт.
0 ; Зарезервировано.
inc lineNum ; Следующий номер строки.
.UNTIL lineNum > 50
; Изменяет размеры и положение окна консоли относительно
; экранного буфера.
INVOKE SetConsoleWindowInfo,
outHandle,
TRUE,
ADDR windowRect ; Размеры окна.
CALL Readchar ; Ожидание нажатия клавиши.
CALL Clrscr ; Очистка экранного буфера.
CALL Readchar ; Ожидание нажатия клавиши.
INVOKE WaitMsg
INVOKE ExitProcess,0
main ENDP
END main
Стр. 120
отсутствует. 121
Функция SetConsoleScreenBufferSize
Эта функция позволяет установить размеры экранного буфера с количеством колонок X
и количеством строк Y. Прототип функции следующий:
SetConsoleScreenBufferSize PROTO,
outHandle:DWORD, ; Дескриптор экранного буфера.
dwSize:COORD ; Новые размеры буфера.
Управление курсором
Интерфейс Win32 API предоставляет несколько функций для установки размера
курсора, видимости и размещения. Необходимые данные собраны в структуре
CONSOLE_CURSOR_INFO.
CONSOLE_CURSOR_INFO STRUCT
dwSize DWORD ?
bVisible BYTE ?
CONSOLE__CURSOR_INFO ENDS
Поле dwSize устанавливает процент (1...100) символьных ячеек, заполняемых курсо&
ром. Если значение поля bVisible равно true, то курсор видим.
Функция GetConsoleCursorlnfo
Эта функция возвращает текущие размеры и видимость курсора консоли. Возвраща&
ется указатель на структуру CONSOLE_CURSOR_INFO.
GetConsoleCursorlnfo PROTO,
outHandle:DWORD, ; Дескриптор выходного устройства.
pCursorInfo:PTR CONSOLE_CURSOR_INFO ; Информация о курсоре.
По умолчанию размер курсора имеет значение 25 (т.е. 25% символьных ячеек запол&
нено курсором).
Функция SetConsoleCursorlnfo
Эта функция устанавливает текущие размеры и видимость курсора консоли. Переда&
ется указатель на структуру CONSOLE_CURSOR_INFO.
SetConsoleCursorlnfo PROTO,
outHandle:DWORD, ; Дескриптор выходного устройства.
pCursorlnfo:PTR CONSOLE_CURSOR_INFO ; Информация о курсоре.
Функция SetConsoleCursorPosition
Эта функция устанавливает координаты положения курсора X, Y. Передается струк&
тура COORD и дескриптор входного устройства.
SetConsoleCursorPosition PROTO,
outHandle:DWORD, ; Дескриптор.
coords:COORD ; Координаты X,Y.
Управление цветом
Устанавливать цвета окна консоли можно двумя путями. Можно изменять текущий
цвет символов с помощью функции SetConsoleTextAttribute, которая действует на
все последующие символы, выводимые на консоль, или устанавливать атрибуты отдель&
ных ячеек, вызывая функцию WriteConsoleOutputAttribute.
Стр. 121
Функция SetConsoleTextAttribute
Эта функция позволяет установить цвет символов и цвет фона для выводимого на
консоль текста. Ее прототип следующий:
SetConsoleTextAttribute PROTO,
outHandle:DWORD, ; Дескриптор консоли.
nColor:DWORD ; Атрибуты цвета.
Значения цвета сохраняются в младшем байте параметра nColor.
Функция WriteConsoleOutputAttribute
Эта функция копирует массив значений атрибутов последовательных ячеек экранного
буфера консоли начиная с указанной позиции. Ее прототип следующий:
WriteConsoleOutputAttribute PROTO,
outHandle:DWORD, ; Дескриптор выхода.
pAttribute:PTR WORD, ; Записываемые атрибуты.
nLength:DWORD, ; Количество ячеек.
xyCoord:COORD, ; Координаты первой ячейки.
IpCountr:PTR DWORD ; Число записанных ячеек.
Параметр pAttribute указывает на массив атрибутов, где каждый младший байт
включает информацию о цвете. Параметр nLength определяет длину массива. Параметр
xyCoord определяет начальную ячейку. Параметр IpCountr указывает на переменную,
которая содержит число записанных ячеек.
Стр. 122
отсутствует. 123
На рис. 4.3 показан результат работы программы, где все 20 символов отображаются
различным цветом.
Стр. 123
Окончание табл. 4.6
Функция Описание
LocalFileTimeToFileTime Преобразовывает локальное время в характеристику файла
на основе UTC
SetFileTime Устанавливает дату и время создания файла, время
последнего обращения и время последней модификации
SetLocalTime Устанавливает текущие локальные дату и время
SetSystemTime Устанавливает текущую системную дату и время
SetSystemTimeAdjustment Разрешение или запрет периодической настройки
внутреннего системного генератора времени
SetTimeZoneInformation Устанавливает текущие параметры временной зоны
SystemTimeToFileTime Преобразовывает системное время в характеристику файла
SystemTimeToTzSpecificLocalTime Преобразовывает время в формате UTC для определенной
временной зоны в соответствующее локальное время
Стр. 124
отсутствует. 125
Функция GetTickCount
Эта функция возвращает число миллисекунд, прошедших со времени старта компьютера.
GetTickCount PROTO ; Значение возвращается в регистре EAX
Поскольку возвращаемое значение имеет тип двойного слова, то значение будет пе&
риодически обнуляться с периодом 49,7 дней. Эту функцию можно использовать для
контроля прошедшего времени и выполнять определенные действия после превышения
порогового времени. Например, в следующей программе каждые 100 миллисекунд на эк&
ране отображается точка и время контролируется каждые 5000 миллисекунд. Этот код
можно использовать в различных программах.
Функция Sleep
Эта функция приостанавливает выполнение текущей программы на заданное число
миллисекунд.
Sleep PROTO,
dwMilliseconds:DWORD
Стр. 125
Вызовите функцию, подобную GetLocalTime, для заполнения структуры
SYSTEMTIME.
Преобразуйте структуру SYSTEMTIME в структуру FILETIME с помощью функции
SystemTimeToFileTime.
Скопируйте структуру FILETIME в учетверенное слово.
Структура FILETIME формирует 64&разрядное значение из двух 32&разрядных значений.
FILETIME STRUCT
loDateTime DWORD ?
hiDateTime DWORD ?
FILETIME ENDS
Процедура принимает указатель на 64&разрядную переменную и сохраняет текущие
дату и время в формате FILETIME.
GetDateTime PROC,
pStartTime:PTR QWORD
LOCAL sysTime:SYSTEMTIME, flTime:FILETIME
; Получить и сохранить текущее локальное время и дату как
; 64-разрядное целочисленное значение (формат FILETIME Win32)
; Получить системное локальное время
INVOKE GetLocalTime,
ADDR sysTime
; Преобразовать формат SYSTEMTIME в формат FILETIME
INVOKE SystemTimeToFileTime,
ADDR sysTime,
ADDR flTime
; Скопировать FILETIME в 64-разрядное целочисленное значение
MOV ESI,pStartTime
MOV EAX,flTime.loDateTime
MOV DWORD PTR [ESI],EAX
MOV EAX,flTime.hiDateTime
MOV DWORD PTR [esi+4],EAX
ret
GetDateTime ENDP
Создание секундомера
Функцию GetTickCount можно использовать при разработке двух процедур, кото&
рые совместно будут действовать как секундомер. Одна из процедур, назовем ее Timer-
Start, записывает текущее время. Вторая процедура TimerStop возвращает число мил&
лисекунд, прошедших с момента последнего вызова процедуры TimerStart.
Программа Timer.asm использует обе процедуры и создает некоторую задержку, вы&
зывая функцию Sleep.
Стр. 126
отсутствует. 127
TimerStop PROTO,
pSavedTime: PTR DWORD
.DATA
msg BYTE " milliseconds have elapsed",0dh,0ah,0
timerl DWORD ?
.CODE
main PROC
INVOKE TimerStart, ; Запуск таймера ADDR timerl.
ADDR timerl
INVOKE Sleep, 5000 ; Задержка 5 секунд.
INVOKE TimerStop, ; Количество миллисекунд timerl в EAX
ADDR timerl
CALL WriteDec ; Отобразить время.
MOV EDX,OFFSET msg
CALL WriteString
INVOKE WaitMsg
exit
main ENDP
Резюме
В данной главе описаны возможности создания программ для Win32 на языке ассемб&
лера. Все программы рассчитаны на работу с консолью, но несмотря на это точно так же
можно обращаться и к графическим окнам Windows, о чем будет сказано в дальнейшем.
Большое количество демонстрационных программ способствует лучшему пониманию
специфики разработки программ для Windows и позволит создать собственную библио&
теку необходимых процедур и функций. Основное отличие разработки программ для
Стр. 127
Windows от программ для DOS связано с графическим интерфейсом пользователя. Это
значит, что все команды и директивы ассемблера те же самые (о чем речь пойдет далее),
а изменения связаны с необходимостью обмена информацией с системой через интерфейс
Windows, для чего используются функции операционной системы Windows. Также необ&
ходимо четко представлять, что в защищенном режиме, в котором работает Windows NT,
можно использовать только плоскую модель памяти и нельзя непосредственно обра&
щаться к отдельным устройствам.
Контрольные вопросы
1. Какую опцию компоновщика необходимо использовать, чтобы получить консоль&
ное приложение для Win32?
2. Чем отличаются названия функций для работы с 16&разрядными символами, та&
кими как Unicode?
3. В каких операционных системах набор символов Unicode является встроенным?
4. Что такое тип данных? Какие типы данных вы можете назвать?
5. Что такое дескриптор?
6. С помощью какой функции можно получить дескриптор стандартного входа?
7. Что такое прототип функции?
8. Что записывается в подключаемом файле?
9. Какие структуры данных можете описать?
10. С помощью какой функции можно произвести считывание данных с консоли?
11. Что такое библиотека загрузочных модулей?
12. Какие расширения может иметь файл библиотеки загрузочных модулей?
13. Что такое инкапсуляция?
14. Как можно создать файл с помощью функций Win32?
15. Какие данные необходимы для отображения символа в окне консоли?
16. Какие форматы времени вы можете назвать?
Стр. 128
Глава 5
Фундаментальные понятия
языка ассемблера
В этой главе...
В данной главе будут даны понятия о типах и операциях, которые над ними можно
производить. Также будут рассмотрены директивы ассемблера, т.е. те команды, которые
должен выполнить ассемблер и которые не преобразовываются в машинные коды.
Стр. 129
DB, DW и DD, остальные будут описаны в последующих главах. В табл. 5.1 представлены
все директивы с кратким описанием. В скобках приводятся синонимы для данных директив.
При использовании последних версий MASM лучше писать директивы как BYTE,
WORD и т.д. Это расширенные директивы и с их помощью можно задавать отрицательные
значения, например использовать директиву SBYTE (знаковый байт). Эти директивы
можно трактовать как директивы задания типа. Базовые типы, которые можно использо&
вать для распределения данных, перечислены в табл. 5.2.
Объявление байтов
Директива объявления байта DB выделяет память для одного или нескольких 8&раз&
рядных значений. Из определения синтаксиса видно, что имя не является обязательным,
но инициализация, по крайней мере, одного значения должна быть проведена. Если зна&
чений больше одного, они разделяются запятыми.
[имя] DB инициализатор[, инициализатор] . . .
Каждое значение может быть константным выражением, включающим цифровые ли&
тералы, определенные символы и заключенные в кавычки символы или строки символов.
Стр. 130
отсутствует. 131
Если значение представлено числом со знаком, то оно может изменяться от -128 до +127;
если числом без знака &&&& от 0 до 255. В следующем фрагменте листинга приведено не&
сколько объявлений байт.
char1 DB 'A' ; Символ в кодировке ASCII.
char2 DB 'A'-10 ; Выражение.
signed1 DB -128 ; Наименьшее значение числа со знаком.
signed2 DB +127 ; Наибольшее значение числа со знаком.
unsigned1 DB 255 ; Наибольшее значение числа без знака.
Значение переменной можно не указывать, поставив знак вопроса на месте инициа&
лизатора.
myval DB ?
Множественная инициализация
Иногда имя переменной указывает на начало последовательности байтов. В таком
случае множественная инициализация используется для присвоения значений отдельным
байтам. В следующем примере переменная с именем list имеет смещение 0000. Это
означает, что для значения 10 смещением будет 0000, для 20 &&&& 0001, для 30 &&&& 0002
и для 40 &&&& 0003.
list DB 10,20,30,40
Символы и целочисленные значения могут присваивать одно и то же значение. Пере&
менные получат одинаковое значение в любом из случаев, приведенных в листинге ниже.
char DB 'A' ; Символ (ASCII 41h).
hex DB 41h ; Шестнадцатеричное число.
dec DB 65 ; Десятичное число.
bin DB 010000001b ; Двоичное число.
oct DB 101q ; Восьмеричное число.
Каждый инициализатор может использовать любое основание системы счисления
при последовательном присвоении значений. Цифры, символы и строковые константы
могут находиться в одном ряду. Если шестнадцатеричное значение начинается с буквы (A-F),
перед ней необходимо поставить нуль (0), чтобы ассемблер не принял это число за метку.
Например, переменные list1 и list2 получат одинаковые значения.
list1 DB 10, 32, 41h,00l000l0b
list2 DB 0Ah,20h,'A',22h
Стр. 131
LongString DB "Это пример длинной последовательности "
DB "символов, которые занимают несколько "
DB "строк при их объявлении",0
Ассемблер может автоматически подсчитать пространство памяти, занятое перемен&
ной, вычитая смещение переменной из смещения следующей переменной. Оператор ‘‘$’’
возвращает текущее состояние счетчика, как показано в примере.
0000 mystring DB "Это заданная строка"
0010 mystring_len = ($ - mystring)
В данном примере значение mystring_len будет равно 10h или 16.
Оператор DUP
Оператор DUP используется после таких директив, как DB или DW. С помощью этого
оператора можно продублировать одно или несколько значений для размещения их в па&
мяти. Это особенно эффективно при размещении строк символов или массивов.
DB 20 DUP(0) ; 20 байт, все равны нулю.
DB 20 DUP(?) ; 20 байт, инициализированы.
DB 4 DUP("ABC") ; 12 байт: "ABCABCABCABC".
DB 4096 DUP(0) ; Буфер размером 4096 байт, все нули.
Операторы DUP можно вкладывать друг в друга. В первом примере ниже показано
создание в памяти строки символов 000ХХ000ХХ000ХХ000ХХ. Второй пример можно
рассматривать как таблицу, состоящую из трех строк и четырех колонок.
aTable DB 4 DUP(3 DUP(0),2 DUP('X'))
aMatrix DW 3 DUP(4 DUP(0))
Объявление слов
Директива определения слова DW выделяет пространство памяти для одного или не&
скольких 16&разрядных значений. Ее синтаксис такой:
[имя] DW инициализатор[, инициализатор] . . .
Каждый целочисленный инициализатор может принимать значения от 0 до 65535
(FFFFh). Если инициализатор выражается числом со знаком, то его значения будут нахо&
диться в диапазоне от -32768 (8000h) до +32767 (7FFFh). Символьная константа мо&
жет быть сохранена в нижней половине слова. В качестве инициализатора можно ис&
пользовать знак вопроса (?), как показано в приведенных примерах.
DW 0,65535 ; Наименьшее/наибольшее число без знака.
DW -32768,+32767 ; Наименьшее/наибольшее число со знаком.
DW 256 * 2 ; Рассчитываемое выражение.
DW 1000h,4096,'AB',0 ; Множественная инициализация.
DW ? ; Инициализировано неопределенным значением.
DW 5 DUP(1000h) ; Инициализировано 5 слов, каждое равно 1000h.
DW 5 DUP(?) ; 5 слов, значение не определено.
Указатели
Смещение переменной или подпрограммы может быть сохранено в другой перемен&
ной, называемой указателем. В следующем примере ассемблер инициализирует указа&
тель P смещением переменной list.
list DW 256,257,258,259 ; Объявлено 4 слова.
P DW list ; P указывает на переменную list.
Стр. 132
отсутствует. 133
Символические константы
Директивы установления адреса позволяют присваивать символические имена кон&
стантам и литералам. Константа может быть определена в начале программы и в некото&
рых случаях позже переопределена.
Директива равенства
Директива равенства (=) создает константу, присваивая имени числовое значение.
имя = выражение
В отличие от директив DB и DW, директива равенства не распределяет память. Во вре&
мя трансляции программы все соответствующие имена будут заменены выражениями.
В результате вычисления выражения должно получиться 32&разрядное число со знаком
или без него (для процессоров 386 и более старших моделей), как показано в примере.
prod = 10 * 5 ; Вычисление выражения.
maxInt = 7FFFh ; Максимальное значение 16-разрядного числа со знаком.
minInt = 8000h ; Минимальное значение 16-разрядного числа со знаком.
maxUInt = 0FFFFh ; Максимальное значение 16-разрядного числа без знака.
Стр. 133
string = 'XV' ; Допустимо до двух символов.
count = 500
endvalue = count + 1 ; Можно использовать предопределенное имя.
maxLong = FFFFFFFh ; Максимальное значение 32-разрядного числа со знаком.
minLong = 8000000h ; Минимальное значение 32-разрядного числа со знаком.
maxULong = 0FFFFFFFFh ; Максимальное значение 32-разрядного числа без знака.
Символическое имя, определенное с помощью директивы равенства, может быть переоп&
ределено сколь угодно много раз. В следующем примере имя count изменяет значение не&
сколько раз. С правой стороны показано, как ассемблер использует эту константу (табл. 5.3).
Директива EQU
Директива EQU присваивает символическое имя строке символов или цифровой кон&
станте. Это повышает удобочитаемость программ и позволяет изменить многочисленные
вхождения констант в одном месте. Однако директива EQU имеет одно важное ограниче&
ние &&&& определенное один раз имя нельзя переопределить.
Выражения, включающие целочисленные значения, определяют цифровую констан&
ту, но выражения с вещественными числами интерпретируются как строка символов.
Строки символов могут быть заключены в угловые скобки (<...>) для подтверждения
того, что это строка символов. Таким образом исключается неоднозначность при опреде&
лении имен, как показано в примере ниже (табл. 5.4).
Директива TEXTEQU
Можно присвоить строке символов имя и потом использовать строку под этим име&
нем. Синтаксис присваивания может быть следующим.
имя TEXTEQU <текст>
имя TEXTEQU макроопределение текста
имя TEXTEQU %константное выражение
Стр. 134
отсутствует. 135
; Исходный текст:
move BX,address value1
move AL,20
; Ассемблируется как:
MOV BX,OFFSET value1
MOV AL,20
В следующем примере TEXTEQU используется для определения указателя p1 на стро&
ку. Позже p1 будет определять символ '0'.
.DATA
myString DB "A string",0
.code
p1 TEXTEQU <OFFSET myString>
MOV BX,p1 ; BX = OFFSET myString.
p1 TEXTEQU <0>
MOV SI,p1 ; SI = 0.
Модели памяти
С помощью директивы .MODEL можно выбрать одну из стандартных моделей памяти
для программ на языке ассемблера. Модель памяти можно рассматривать как конфигура&
цию, которая определяет, каким образом надо комбинировать используемые сегменты.
Каждая модель имеет свои ограничения по максимальному размеру памяти, выделяемой
для команд и данных. Здесь важно понимать, как в каждой модели происходит вызов
подпрограмм и данных.
При программировании для 32&разрядного режима используется только плоская модель
памяти.
Выбирая модель памяти, обычно делают выбор между скоростью, гибкостью и разме&
рами программы. Модель памяти, которая ограничивает размеры всех данных одним
сегментом в 64 Кбайт, гарантирует, что все вызовы будут только ближними и, соответст&
венно, потребуются только 16&разрядные значения. Таким образом, доступ к данным бу&
дет более быстрым, потому что 16&разрядный адрес может быть загружен значительно
Стр. 135
быстрее, чем 32&разрядный адрес ‘‘сегмент&смещение’’. Модель памяти, которая разме&
щает подпрограммы в различных сегментах, также требует для их вызова использования
двух регистров CS и IP.
Различные модели памяти используют различное число байтов для команд и данных.
Например, когда длина сегмента кодов ограничена объемом в 64 Кбайт, то нет необходимо&
сти в командах использовать большие числа. В табл. 5.2 показано различие между всеми
моделями памяти. При использовании всех моделей памяти, кроме тонкой, создаются вы&
полняемые программы типа .exe. При использовании тонкой модели памяти создаются
программы .com. Все модели, исключая плоскую модель, рассчитаны на функционирова&
ние в реальном режиме процессора. Плоская модель памяти может использоваться только
для работы в защищенном режиме. Например, операционные системы Windows NT,
Windows 2000 и более поздние являются 32&разрядными и работают в защищенном режиме.
Выполняемые программы
Надо хорошо представлять себе, как выглядят выполняемые программы после загруз&
ки их в память. Сегмент кодов и сегмент памяти находятся по разным адресам памяти,
что можно увидеть с помощью отладчика.
На рис. 5.1 показано состояние процессора и распределение памяти для сегментов.
Три сегмента имеют совместное пересечение, однако пересечение физически зани&
маемой памяти отсутствует. Каждый программный сегмент (иногда называемый логиче%
ским сегментом) получается очень небольшим и не заходит в пространство другого сег&
мента. Регистр IP способен принимать и большие значения, это может случиться, когда
программист забудет поставить заключительную команду окончания кодовой последова&
тельности. Если будет превышена граница сегмента данных при реальном режиме работы
процессора, то разрушится стек. В защищенном режиме произойдет прерывание при по&
пытке выйти за границы сегмента данных.
Стр. 136
отсутствует. 137
Обратите внимание, что при программировании для плоской модели памяти в сег&
ментных регистрах сохраняются не адреса сегментов, а элементы таблицы дескрипторов,
которые и указывают на адреса сегментов. Это необходимо для того, чтобы операцион&
ная система могла заниматься распределением памяти при многозадачном режиме работы.
Стр. 137
Окончание табл. 5.6
Директива Описание
.286 Доступны непривилегированные команды для 80286, команды для более поздних
процессоров не используются
.386 Доступны непривилегированные команды для 80386, команды для более поздних
процессоров не используются
.486 Доступны непривилегированные команды для 80486, команды для процессоров Pentium
не используются
.586 Доступны непривилегированные команды для процессоров Pentium
.287 Используются команды вычислений с плавающей запятой для математического
сопроцессора 80287
.387 Используются команды вычислений с плавающей запятой для математического
сопроцессора 80387
.686 Доступны непривилегированные команды для процессоров Pentium Pro
.686P Доступны привилегированные команды для процессоров Pentium Pro
Стр. 138
отсутствует. 139
Необходимо отметить, что в команде MOV нельзя использовать два операнда памяти.
Для этого приходится применять регистры для копирования байта, слова или двойного
слова из одного места памяти в другое. Следующие команды скопируют слово из пере&
менной var1 в var2.
MOV AX,var1
MOV var2,AX
Ниже приведены примеры команды MOV со всеми тремя типами операндов.
.DATA
count DB 10
total DW 4126h
bigVal DD 12345678H
.code
MOV AL,BL ; 8-разрядный регистр в регистр.
MOV BL,count ; 8-разрядный блок памяти в регистр.
MOV count,26 ; 8 разрядов непосредственно в память.
MOV BL,1 ; 8 разрядов непосредственно в регистр.
MOV DX,CX ; 16-разрядный регистр в регистр.
MOV BX,8FE2h ; 16 разрядов непосредственно в регистр.
MOV total,1000h ; 16 разрядов непосредственно в память.
MOV EAX,EBX ; 32-разрядный регистр в регистр.
MOV EDX,bigVal ; 32-разрядный блок памяти в регистр.
Ошибка несоответствия типов возникает даже при перемещении значения меньшего типа
в больший.
При необходимости можно использовать директиву LABEL для создания нового име&
ни с тем же смещением, но другими атрибутами, благодаря чему те же самые данные уже
не вызовут ошибки.
.DATA
countB LABEL byte ; Атрибут байт.
countW DW 20h ; Атрибут слово.
.code
MOV AL,countB ; Извлекает младший байт из countB.
MOV CX,countW ; Извлекает все из countW.
Стр. 139
Использование дополнительного смещения в операторах
К имени операнда памяти можно добавить дополнительное смещение (сдвиг), ис&
пользуя метод, называемый адресацией с прямым смещением. Это позволяет обеспечить
доступ к значениям в памяти, у которых нет собственных имен (например, к массивам
байтов, слов и двойных слов).
arrayB DB 10h,20h
arrayW DW 100h,200h
arrayD DD 10000h,20000h
Обозначение arrayB+1 ссылается на место в памяти за первым байтом, на который
указывает идентификатор arrayB, обозначение arrayW+2 ссылается на третье двойное
слово от arrayW. Операнды можно также записывать в команде MOV, используя такой
вид обозначений для копирования данных из памяти. В следующем примере показано
значение регистра AL после каждого изменения переменной.
MOV AL,arrayB ; AL = 10h.
MOV AL,arrayB+1 ; AL = 20h.
Когда выполняются команды с 16&разрядными операндами, смещение каждого по&
следующего члена массива увеличивается на два байта.
MOV AX,arrayW ; AX = 100h.
MOV AX,arrayW+2 ; AX = 200h.
Члены массива учетверенных слов смещаются на четыре байта от предыдущего.
MOV EAX,arrayD ; EAX = 10000h.
MOV EAX,arrayD+4 ; EAX = 20000h.
Команда XCHG
Команда обмена значениями XCHG обменивает содержание двух регистров или со&
держание регистра и переменной. Она имеет следующий синтаксис.
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
Команда XCHG наиболее эффективна при обмене значений двух операндов, поскольку
нет необходимости использовать дополнительный регистр для временного значения. Эта
команда значительно повышает скорость работы в приложениях сортировки данных.
Один или оба операнда могут быть регистрами, регистр можно также скомбинировать
с операндом в памяти, но нельзя использовать два операнда памяти. Например:
XCHG AX,BX ; Обменивает два 16-разрядных регистра.
XCHG AH,AL ; Обменивает два 8-разрядных регистра.
XCHG var1,BX ; Обменивает 16-разрядный операнд в памяти с регистром BX.
XCHG EAX,EBX ; Обменивает два 32-разрядных регистра.
Если необходимо обменять содержание двух переменных, можно временно использо&
вать регистр для хранения операнда. В листинге 5.1 производится обмен содержимого
двух переменных.
Стр. 140
отсутствует. 141
INCLUDE G:\MyASM\MASM\INCLUDE\Study32.inc
.DATA
value1 DWORD 0Ah
value2 DWORD 14h
.code
main PROC
MOV EAX,value1 ; Загрузить в регистр EAX.
INVOKE WriteInt ; Отобразить содержимое EAX.
XCHG value2,EAX ; Обменять EAX с value2.
INVOKE Crlf ; Перевод каретки (курсора)
INVOKE WriteInt ; Отобразить содержимое EAX.
MOV value1,EAX ; Сохранить EAX в value1.
INVOKE Crlf
INVOKE WaitMsg ; Ожидание нажатия <Enter>.
main ENDP
END main
Арифметические команды
Набор команд процессора 8086 включает команды для выполнения целочисленных
арифметических операций с 8&разрядными и 16&разрядными операндами. Для арифме&
тических операций с вещественными числами используется специальное программное
обеспечение или дополнительная микросхема арифметического сопроцессора 8087. Про&
цессор 80386 может выполнять целочисленные операции с 32&разрядными целыми чис&
лами. Процессор Intel 486 содержит встроенный блок сопроцессора для вычислений с
вещественными числами, что характерно и для всех последующих моделей процессоров.
В этой главе будут рассмотрены только операции сложения и вычитания. Операции
умножения и деления будут подробно описаны в дальнейшем.
Команда ADD
Команда ADD складывает операнд&отправитель и операнд&получатель одинакового
размера. Команда имеет следующий синтаксис.
ADD операнд-получатель, операнд-отправитель
Стр. 141
Исходный операнд&отправитель не изменяется в процессе выполнения команды,
а целевому операнду&получателю присваивается значение суммы. Размеры операндов
должны быть одинаковыми, и только один операнд может быть операндом памяти. Ре&
гистр сегмента не может быть целевым операндом. Используются все флаги состояния.
ADD CL,AL ; Суммирует два 8-разрядных регистра.
ADD EAX,EDX ; Суммирует 32-разрядные регистры.
ADD BX,1000h ; Суммирует непосредственное значение с
; 16-разрядным регистром.
ADD var1,AX ; Суммирует 16-разрядный регистр с операндом в памяти.
ADD DX,var1 ; Суммирует 16-разрядный операнд в памяти с регистром.
ADD varl,10 ; Суммирует непосредственную величину с операндом
; в памяти.
ADD DWORD PTR memVal, ECX
Оператор DWORD PTR определяет 32&разрядный операнд в памяти.
Команда SUB
Команда вычитания SUB вычитает операнд&отправитель из операнда&получателя.
Синтаксис этой команды следующий:
SUB операнд-получатель, операнд-отправитель
Размеры обоих операндов должны быть одинаковыми, и только один операнд может
быть операндом памяти. В процессоре исходный операнд сначала инвертируется, а затем
складывается с целевым операндом. Например, 4 – 1 будет выполняться как 4 + (–1).
Вспомните, что двоичное дополнение используется для отрицательных чисел, поэтому –1
будет представлено как 11111111. Приведем следующий пример вычитания:
00000100 ( 4 )
+11111111 (-1 )
---------
00000011 ( 3 )
Стр. 142
отсутствует. 143
Знаковое переполнение
Флаг переполнения устанавливается, когда в результате арифметической операции
получается число со знаком, превышающее максимально допустимое для операнда&
получателя. Установленный флаг переполнения говорит о том, что операнд&получатель
содержит некорректное число.
MOV AL,01111110b ; +126.
ADD AL,00000010b ; 126 + 2 => 10000000b, OF = 1.
MOV AL,10000000b ; -128.
ADD AL,11111110b ; -128 + (-2) => 01111110b, OF = 1.
Знаковое переполнение возникает, если суммируются два положительных числа, а ре&
зультат получается отрицательный, а также если суммируются два отрицательных числа,
а в результате получается положительное число. Процессор сравнивает значение флага
переноса со значением, которое переносится в знаковый бит операнда&получателя. Если
они не равны, устанавливается флаг переполнения. Например, при сложении двоичных
Стр. 143
76543210 чисел 10000000 и 1111111 нет переноса из бита 6 в бит 7,
CF = 1 10000000 но производится перенос из бита 7 во флаг переноса и, соот
Нет переноса + 11111110 ветственно, устанавливается флаг переноса (рис. 5.2).
из бита 6 = 01111110
в бит 7
Рис. 5.2. Пример знакового переполнения
Tипы операндов
Существует три основных типа операндов: непосредственный операнд, регистр
(регистровый операнд) и значение памяти (память). Непосредственный операнд является
константой. Регистровый операнд представляет значение одного из регистров процессо
ра. Операнд ‘‘память’’ ссылается на значение в памяти.
В системе команд Intel используется много способов представления операндов для
значений памяти, чтобы облегчить управление массивами и сложными структурами дан
ных. Шесть типов операндов памяти показаны в табл. 5.7. В этой главе будут рассмотрены
первые три типа операндов: прямой, прямое смещение и косвенный регистр.
Регистровые операнды
Регистровым операндом может быть любой регистр. Обычно режим регистровой адре
сации наиболее эффективный, потому как регистры находятся непосредственно в процес
соре и нет необходимости обращаться к памяти. Ниже приведены некоторые примеры ис
пользования команды MOV с регистровыми операндами.
Стр. 144
отсутствует. 145
MOV EAX,EBX
MOV CL,20h
MOV SI,OFFSET var1
Непосредственные операнды
Непосредственные операнды являются константным выражением, таким как число,
символьная константа, арифметическое выражение или символическая константа. Ас&
семблер определяет значение непосредственного операнда во время трансляции. Это
значение вставляется непосредственно в машинные команды. Пример использования
непосредственных операндов:
MOV AL,10
MOV EAX,12345678h
MOV DL,'X'
MOV AX,(40 * 50)
Оператор OFFSET
Оператор смещения OFFSET возвращает 16&разрядное значение смещения перемен&
ной. Ассемблер определяет смещение каждой переменной во время трансляции про&
граммы. В следующем примере переменная aWord имеет смещение 0000, команда MOV
перемещает 0 в регистр BX.
.DATA
aWord DW 1234h
.code
MOV BX,OFFSET aWord ; BX = 0000
Операнды со смещением
Особенно удобно использовать операторы сложения и вычитания (+,-) для доступа
к списку переменных. Оператор + добавляет значение к смещению переменной. В пред&
ставленной последовательности команд первый байт массива array перемещается в ре&
гистр AL, второй &&&& в регистр BL, третий &&&& в CL и четвертый &&&& в DL. Значение каждого
регистра после перемещения показано в комментарии.
Стр. 145
.DATA
array DB 0Ah,0Bh,0Ch,0Dh
.code
MOV AL,array ; AL = 0Ah
MOV BL,array+l ; BL = 0Bh
MOV CL,array+2 ; CL = 0Ch
MOV DL,array+3 ; DL = 0Dh
Можно вычитать из смещения метки. В следующем примере значение метки endlist раз&
мещается сразу за последним байтом списка list. Чтобы переместить последний байт из
list в регистр AL, необходимо написать следующий код. Напомню использование ди&
рективы LABEL. Если она расположена после объявления переменной, как показано ни&
же, то эта переменная будет ссылаться на то место в памяти, где предполагается разме&
щать переменную. Тип переменной определяется в соответствии с указанным типом.
.DATA
list byte 1,2,3,4,5
endlist LABEL byte
.code
MOV AL,endlist-1 ; переместить 5 в AL
Если в списке имеются 16&разрядные числа, то к смещению каждого последующего
элемента добавляется значение 2, что и показано в следующем примере.
.DATA
wvals DW 1000h,2000h,3000h,4000h
.code
MOV AX,wvals ; AX = 1000h
MOV BX,wvals+2 ; BX = 2000h
MOV CX,wvals+4 ; CX = 3000h
MOV DX,wvals+6 ; DX = 4000h
Операторы и выражения
В языке ассемблера выражением является комбинация операторов и операндов, кото&
рые ассемблер преобразует в числовую константу. Ассемблер выполняет арифметические
и логические операции во время трансляции, а не во время выполнения программы.
Выражения для числовых констант вычисляются во время трансляции, а не во время вы&
полнения программы.
Если выражение связано с регистром или операндом памяти, его значение должно
быть не больше, чем размер операнда&получателя. Например, разрешены следующие
присваивания.
.386
.code
MOV DL,3 * 5
MOV AX,100h + 500h
MOV EAX,(1000h * 50h) + 26
В табл. 5.8 приведен полный набор операторов, распознаваемых большинством ас&
семблеров, с их кратким описанием. (Более полное описание для некоторых операторов
будет приведено в соответствующих разделах.) Из этого набора видно, что ассемблер &&&&
достаточно мощный инструмент даже для профессионального программирования.
Стр. 146
отсутствует. 147
Стр. 147
Продолжение табл. 5.8
Оператор Описание
MAKEFLAG Оптимизированная форма команды AND, которая сбрасывает биты максимально
быстро. Применяется в случае, если флаги не используются
MOD Возвращает остаток от деления двух выражений
NE Возвращает значение истина, если одно выражение не равно другому
NEAR Преобразует адресное выражение в ближний указатель
NEAR PTR Преобразует адресное выражение в ближний указатель
NOT Выполняет поразрядное дополнение (инвертирование) выражения
OFFSET Возвращает смещение выражения в текущем сегменте (а также в группе, которой
принадлежит сегмент, если используются упрощенные директивы определения
сегментов)
OR Выполняет поразрядную логическую операцию ИЛИ
PROC PTR Преобразует адресное выражение в ближний или дальний указатель
PTR Устанавливает размер операнда, особенно в тех случаях, когда это неясно из контекста
PWORD PTR Преобразует адресное выражение в 32&разрядный дальний указатель
QWORD PTR Преобразует адресное выражение в размер учетверенного слова
SEG Возвращает сегмент выражения, независимо от того, является ли оно переменной,
именем сегмента или группы, меткой или другим символом
SETFIELD Генерирует код, присваивающий значение полю записи. Устанавливает значение поля
(целевой регистр или адрес памяти) в соответствии с содержимым регистра&источника
SETFLAG Оптимизированная форма команды OR, которая устанавливает биты максимально
быстро. Применяется в случае, если флаги не используются
SHL Сдвигает выражение влево на число бит, заданных счетчиком. Отрицательное
значение счетчика задает сдвиг данных в противоположном направлении
SHORT Преобразует выражение в указатель короткого типа (не больше &&128 или +127 байт
от текущего адреса программы). Часто используется в командах JMP, например,
JMP SHORT Label1
SHR Сдвигает значение выражения вправо на число бит, заданных значением счетчика.
Отрицательное значение приводит к тому, что данные будут сдвигаться
в противоположную сторону
SIZE Возвращает общее число байтов, выделенных для переменной. В режиме MASM
оператор SIZE рассчитывается как длина (LENGTH), умноженная на тип (TYPE)
SMALL Задает для смещения выражения размер 16 бит
TBYTE PTR Преобразует адресное выражение в размер 10 байт
TESTFLAG Оптимизированная форма инструкции TEST, которая проверяет биты
максимально быстро
THIS Создает операнд, адресом которого будет текущий сегмент и счетчик адреса.
Тип описывает размер операнда и то, представляет ли он собой код или данные
.TYPE Возвращает байт, который определяет тип и область видимости выражения.
Результирующие биты различают метку и переменную в программе, определяют
данные, внутренний и внешний идентификатор
TYPE Применяет тип существующей переменной или элемента структуры к другой
переменной или элементу структуры
Стр. 148
отсутствует. 149
Арифметические операторы
Арифметические операторы могут использоваться только с целыми числами, за исклю&
чением унарных плюс (+) и минус (-), которые также могут функционировать и с реальны&
ми числами. Примеры использования арифметических операторов показаны в табл. 5.9.
Стр. 149
Таблица 5.10. Старшинство операторов
Оператор Уровень Описание
( ) 1 Круглые скобки
+, - 2 Знаки плюс и минус (унарные)
*, / 3 Умножение, деление
mod 4 Деление по модулю
+, - 5 Сложение, вычитание
Стр. 150
отсутствует. 151
Оператор SEG
Оператор SEG возвращает сегментную часть адреса метки или переменной. Это его
свойство обычно используется, когда переменная находится в сегменте, отличном от того,
на который указывает регистр DS. В следующем примере текущее значение сегмента DS
помещается в стек, значение изменяется на значение сегмента, в котором находится пе&
ременная array, а затем восстанавливается исходное значение DS.
PUSH DS ; Сохранение DS.
MOV AX,seg array ; В DS помещается значение сегмента массива array.
MOV DS,AX
MOV BX,OFFSET array ; Получение смещения array.
... ; Работа с массивом array.
POP DS ; Восстановление DS.
Оператор PTR
Оператор PTR изменяет размер операнда, принятый по умолчанию. В командах, где
размер операнда не определен однозначно, оператор PTR может явно установить размер. PTR
может использоваться в сочетании со стандартными типами данных: BYTE, SBYTE, WORD,
SWORD, DWORD, SDWORD, FWORD, QWORD и TBYTE. Например:
MOV AL,BYTE PTR count
MOV AX,WORD PTR newVal
MOV EAX,DWORD PTR listPointer ; Требует директивы .386.
Часто размер операнда нельзя установить, исходя из контекста команды. Рассмотрим
следующую команду, которая приводит к ошибке ‘‘операнд должен иметь размер’’ (Operand
must have size).
INC [BX] ; Косвенный операнд.
Ассемблер не может определить, является ли указатель BX байтом или словом. В дан&
ной ситуации оператор PTR поможет внести ясность.
INC BYTE PTR [BX]
Оператор PTR также полезен, когда необходимо изменить размеры операнда, уста&
новленные по умолчанию. Предположим, есть 32&разрядное двойное слово и необходимо
загрузить старшую половину в регистр DX, а младшую &&&& в регистр AX (для двойного сло&
ва младшая часть имеет меньший адрес). В приведенном ниже примере будет зафиксиро&
вана синтаксическая ошибка.
.DATA
val32 DD 12345678h
.code
MOV AX,val32 ; Получить младшую часть слова (Ошибка).
MOV DX,val32+2 ; Получить старшую часть слова (Ошибка).
Однако в следующем случае ассемблер сочтет все правильным, так как оператор PTR
изменит значение по умолчанию переменной val32.
MOV AX,WORD PTR val32 ; AX = 5678h.
MOV DX,WORD PTR val32+2 ; DX = 1234h.
Директива LABEL
Директива LABEL разрешает установить метку и присвоить атрибут размера без раз&
мещения в памяти. Любой из стандартных атрибутов размера (например, BYTE, WORD,
DWORD, QWORD и т.д.) может использоваться с LABEL.
Стр. 151
В общем случае LABEL создает альтернативное имя и атрибут размера для сущест&
вующих переменных в сегменте данных. Это весьма удобный путь удовлетворения требо&
ваний языка ассемблера для переменных, которые должны соответствовать другим опе&
рандам в команде. В следующем примере метка val16 объявлена перед переменной
val32 и имеет атрибуты слова.
.DATA
val16 LABEL WORD
val32 DD 12345678h
.code
MOV AX,val16 ; AX = 5678h.
MOV DX,vall6+2 ; DX = 1234h.
Обратите внимание, что команда MOV ссылается на val16, которая является только
псевдонимом для того же пространства памяти, в котором находится переменная val32.
Оператор TYPE
Оператор TYPE возвращает размер в байтах одного элемента переменной. Например,
8&разрядная переменная имеет размер, равный 1, а 32&разрядная переменная возвратит
размер 4, массив байтов возвратит 1, а массив из 16&разрядных целых чисел возвратит 2.
Тип ближней метки будет определять значение FFFFh, а тип удаленной &&&& FFFEFFFFh.
На примерах показано типичное использование этих операторов.
.DATA
varl DB 20h
var2 DW 1000h
Стр. 152
отсутствует. 153
var3 DD ?
var4 DB 10,20,30,40,50
msg DB 'Файл не найден',0
.code
L1: MOV AX,TYPE var1 ; AX = 0001.
MOV AX,TYPE var2 ; AX = 0002.
MOV AX,TYPE var3 ; AX = 0004.
MOV AX,TYPE var4 ; AX = 0001.
MOV AX,TYPE msg ; AX = 0001.
MOV AX,TYPE L1 ; AX = FFFF.
Оператор LENGTH
Оператор LENGTH подсчитывает число отдельных элементов в переменной, которая
может быть определена с помощью оператора DUP. Если DUP не используется, то опера&
тор LENGTH возвращает значение 1. Если используются вложенные операторы DUP, то
учитывается только внешний оператор DUP, как это показано ниже.
.DATA
var1 DW 1000h
var2 DB 10,20,30
array DW 32 DUP(0)
array2 DW 5 DUP(3 DUP(0))
message DB 'Файл не найден',0
.code
MOV AX,LENGTH var1 ; LENGTH = 1.
MOV AX,LENGTH var2 ; LENGTH = 1.
MOV AX,LENGTH array ; LENGTH = 32.
MOV AX,LENGTH array2 ; LENGTH = 5.
MOV AX,LENGTH message ; LENGTH = 1.
Оператор SIZE
Оператор SIZE возвращает значение, получаемое после умножения значений LENGTH
и TYPE. Например, массив из 16&разрядных значений имеет TYPE, равный 2, и LENGTH &&&& 32.
Тогда значение SIZE будет 64.
intArray DW 32 DUP(0) ; SIZE = 64
Следующая строка имеет SIZE, равный 1, так как ее LENGTH и TYPE оба равны 1.
message DB 'Это сообщение',0
Стр. 153
Команды условного перехода. Программа начинает выполнение с новой ветви
только в том случае, если определенное условие является истиной. Intel предос&
тавляет широкий диапазон команд условного перехода, с помощью которых мож&
но создать сложные логические структуры. Процессор определяет условия
ложь/истина исходя из анализа регистра CX и регистра флагов.
Команда JMP
Команда JMP заставляет процессор продолжать выполнение с нового места програм&
мы. Новое место может быть отмечено меткой, которую процессор преобразовывает
в адрес. Например, с помощью команды JMP легко создавать циклические конструкции:
top:
...
...
jmp top
Команда JMP может совершать переход из текущей процедуры, от одной процедуры
к другой, от одного сегмента к другому, полностью выходя за пределы текущей програм&
мы или в любое место памяти RAM или ROM. Принципы структурного программирования
не поддерживают такие переходы, но они иногда необходимы при прикладном програм&
мировании на системном уровне.
При программировании для 32&разрядного режима нет необходимости думать о сег&
ментах, из которых состоит программа, что упрощает написание кода. Но при програм&
мировании для 16&разрядного режима необходимо учитывать расстояние, на которое
может происходить переход. (Подробнее об этом читайте в главе 11.)
Команда LOOP
Команда LOOP &&&& самый удобный способ, с помощью которого осуществляется по&
вторение блока команд определенное количество раз. Регистр CX автоматически исполь&
зуется как счетчик, который декрементируется при каждом повторении блока команд.
Синтаксис команды будет такой:
LOOP пункт назначения
При использовании команды LOOP сначала из регистра CX вычитается единица. Если
результат не равен нулю, контроль передается в пункт назначения. При программирова&
нии для 16&разрядного режима пункт назначения должен находиться на расстоянии от
-128 до +127 от текущего положения (ассемблер использует внутренний счетчик для
определения расстояния). В следующем примере цикл повторяется пять раз.
MOV CX,5 ; В регистре CX считаются циклы.
start:
.
LOOP start ; Переход к метке start.
Если значение CX становится равным нулю, то блок циклических команд не выпол&
няется и управление передается команде, следующей сразу за блоком. Флаг нуля не ис&
пользуется, хотя CX = 0.
В следующем примере к регистру AX добавляется 1 при каждом цикле. Когда цикл за&
канчивается, будут получены значения AX = 5 и CX = 0.
MOV AX,0 ; Установить AX в 0.
MOV CX,5 ; Счетчик циклов.
Стр. 154
отсутствует. 155
top:
INC AX ; Добавить 1 к AX.
LOOP top ; Повторять до CX = 0.
Следующий фрагмент программы выдает на печать букву А 960 раз, используя коман&
ду LOOP. Значение, помещенное в счетчик CX, является произведением 12 строк экрана
с шириной 80 символов.
MOV EX,12*80 ; Установить в счетчике значение 960.
next:
MOV AH,2 ; Функция: отобразить символ.
MOV DL,'A' ; Отображение на экране буквы A.
INT 21h ; Вызов функции DOS.
LOOP next ; Декремент CX и повторение.
Начинающие программисты часто забывают установить значение счетчика CX, в итоге
там оказывается значение 0. В результате этой ошибки цикл повторяется 65535 раз, так
как после декремента нуля в CX получится значение FFFFh.
Будьте внимательны и не модифицируйте регистр CX во время исполнения цикла. В сле&
дующем примере CX инкрементируется внутри повторяющегося блока команд. Значение
счетчика никогда не станет равным нулю, и цикл никогда не закончится.
MOV AH,2 ; Функция DOS: отображение символа.
MOV CX,10 ; Счетчик циклов.
MOV DL,'*' ; Отображение символа.
top:
INT 21h ; Вызов функции DOS.
INC СX ; Добавляем 1 к CX (Этого делать нельзя!).
LOOP top ; Повторение до CX = 0.
MOV ECX,A0000000h
L1:
.
LOOPD L1 ; Использование ECX как счетчика циклов.
Если программа транслируется в 32&разрядном режиме, команда LOOP автоматически
использует регистр ECX в качестве счетчика циклов. Команду LOOPW можно также ис&
пользовать для отмены установок по умолчанию с применением регистра CX.
Косвенная адресация
Косвенные операнды
Косвенный операнд &&&& это регистр, который содержит смещение данных в памяти.
Когда смещение переменной помещено в регистр, оно становится указателем. Для пере&
менной, имеющей одно значение, это не дает никаких преимуществ, однако для массивов
Стр. 155
указатель можно инкрементировать, выбирая последовательные элементы. 16&разрядные
регистры SI, DI, BX и BP можно использовать в качестве косвенных операндов. Директи&
вы .386, .486 или .586 позволяют использовать любой 32&разрядный регистр общего
назначения в качестве косвенного операнда (исключение составляет ESP, о котором речь
пойдет далее).
Например, если в памяти создается строка по адресу 0200, то, помещая в регистр BX
ее смещение, можно обеспечить доступ к любому элементу строки. Символ F в следую&
щем примере имеет смещение 5 от начала строки.
.DATA
aString DB "ABCDEFG"
.code
MOV BX,OFFSET aString ; BX = 0200.
ADD BX,5 ; BX = 0205.
MOV DL,[BX] ; DL = 'F'.
Сегменты по умолчанию
Смещение, получаемое с использованием косвенных операндов, отсчитывается от
начала сегмента данных (при 16&разрядном программировании его значение находится
в регистре DS), за исключением ситуации, когда регистры BP и EBP являются частью кос&
венного операнда. В этом случае используется сегмент стека (регистр SS). Полагая, что
сегмент данных и сегмент стека занимают в памяти различные места, третья и вторая ко&
манды MOV будут иметь доступ к различным местам памяти.
MOV SI,BP ; SI и BP равны.
MOV DL,[SI] ; Смотрим в сегменте данных.
MOV DL,[BP] ; Смотрим в сегменте стека.
Стр. 156
отсутствует. 157
Стр. 157
.code
...
MOV СX,COUNT ; Счетчик циклов.
MOV SI,OFFSET string
L1:
MOV AH,2 ; Вывод символа на экран.
MOV DL,[SI] ; Получить символ.
INT 21h
INC SI ; Указать на следующий символ.
LOOP L1 ; Декремент CX, повторение.
В листинге 5.4 цикл используется для вывода каждого символа, составляющего стро&
ку. Эта же программа для 32&разрядного режима будет выглядеть следующим образом.
Стр. 158
отсутствует. 159
На рис. 5.3 показано окно CPU отладчика, где можно увидеть исходный текст, со&
стояние регистров процессора и дамп памяти.
Стр. 159
Базовые и индексные операнды
Базовый и индексный операнды по существу аналогичны. Значение регистра (базового
или индексного) добавляется к смещению для получения эффективного адреса перемен&
ной. Постоянное значение дополнительного смещения или сдвига может быть, напри&
мер, смещением переменной. Различие между базовым и индексным операндом состоит
в том, что для базового операнда используются регистры базы EBX или EBP, а для ин&
дексного &&&& ESI или EDI. Ассемблер допускает различные формы записи, как показано
в табл. 5.12 для 16&разрядных регистров.
32*разрядные регистры
Если команды для процессора 386 доступны (применяется директива .386), то мож&
но использовать любой регистр общего назначения как базовый или индексный операнд.
.386 ; Доступны 32-разрядные регистры.
MOV AX,[EBX+3] ; 16-разрядный операнд.
MOV DL,string[EDX] ; 8-разрядный операнд.
MOV ECX,6[ESI] ; 32-разрядный операнд.
MOV EAX,myList[EDI] ; 32-разрядный операнд.
При программировании в 16&разрядном режиме будьте внимательны и не используйте
32&разрядные значения индексных регистров, которые могут превысить допустимый
размер сегмента (0 -FFFFh).
MOV ESI,10000h
MOV AL,[ESI] ; выход за диапазон 16-разрядного режима
Базо*индексные операнды
Базо&индексные операнды добавляют значение регистра базы к значению индексного
регистра для получения смещения. Такой тип операнда часто используется при необхо&
димости доступа к двумерным массивам, когда базовый регистр содержит смещение
строки, а индексный &&&& смещение столбца. Программа, представленная в листинге 5.7,
использует массив 8&разрядных целых чисел. Если этот массив размещен по адресу 0150
Стр. 160
отсутствует. 161
array BX = 5
Стр. 161
rowsize = 5
MOV BX,rowsize ; Выбираем вторую строку.
MOV SI,2 ; Выбираем третий столбец.
MOV AL,array[BX+SI] ; Получаем величину смещения 0157.
На рис. 5.5 показаны значения регистров BX и SI для двумерного массива.
0150 0155 0157
10 20 30 40 50 60 70 80 90 A0
[BX] [BX+SI]
Стр. 162
отсутствует. 163
Команда XADD
Команда XADD для процессора 80486 суммирует значения операндов отправителя
и получателя и сохраняет значение в операнде&приемнике. В то же время исходное зна&
чение операнда&приемника перемещается в операнд&получатель. Используются флаги
переполнения, знака, нуля, дополнительного переноса, четности и переноса. Например,
можно добавить значение регистра BX к AX, сохранив значение, которое было в AX.
MOV AX, 1000h
MOV BX, 2000h
XADD AX, BX ; AX = 3000h, BX = 1000h.
Одной командой XADD можно заменить следующие три команды.
PUSH AX
ADD AX,BX
POP BX
Процедуры
Представленные в книге примеры содержали последовательности команд, выполняв&
шиеся один раз для решения определенной задачи. Однако если возникает необходимость
выполнить ту же последовательность команд в другом месте программы, то придется или
продублировать последовательность команд, или использовать обращение к процедурам.
Обычно дублирование неэффективно и значительно удлиняет программу, поэтому лучше
оформить повторяющуюся последовательность команд как процедуру. Процедура (или под&
программа) представляет собой совокупность команд, которая написана один раз, но при
необходимости может быть использована в любом месте программы.
Процесс передачи управления из основной части программы в процедуру называется
вызовом. При вызове процедуры микропроцессор исполняет ее команды, а затем воз&
вращается к тому месту, откуда был сделан вызов.
Команды, обеспечивающие исполнение процедур, должны выполнить три функции:
сохранить содержимое указателя команд IP. Когда процедура исполнена, находя&
щееся в этом указателе значение (адрес возврата) используется микропроцессором
для возврата к месту вызова;
заставить микропроцессор начать исполнение процедуры;
использовать сохраненное содержимое указателя команд IP для возврата в про&
грамму и обеспечить продолжение ее исполнения с этого места.
Все эти функции выполняются двумя командами: CALL (вызвать процедуру) и RET (возв&
ратиться из процедуры). Команда CALL осуществляет функции запоминания адреса возвра&
та и передачи управления процедуре.
Команда CALL имеет следующий формат.
CALL имя,
где имя &&&& имя вызываемой процедуры (т.е. адрес ее начала).
После сохранения адреса возврата команда CALL загружает смещение адреса метки
имя в указатель команд IP.
Команда RET заставляет микропроцессор возвратиться из процедуры в программу,
вызвавшую эту процедуру, восстанавливая все, что сохранила команда CALL. Команда
RET обязательно должна быть последней командой процедуры, исполняемой микропро&
цессором, но это не значит, что команда RET должна стоять в конце процедуры &&&& она
должна исполняться последней. Команда RET извлекает из стека адрес возврата.
Стр. 163
Например, для вызова процедуры my_proc из некоторого места вашей программы
может использоваться следующая последовательность команд.
04F0 CALL my_proc ; Вызвать процедуру.
04F3 NEXT: MOV AX,BX ; Вернуться из процедуры.
0500 my_proc PROC ; Начало процедуры.
0500 MOV CL,6 ; Первая команда процедуры.
О51Е RET ; Вернуться в вызвавшую программу.
051F my_proc ENDP ; Конец процедуры.
При исполнении команды CALL микропроцессор помещает в стек смещение адреса
метки NEXT (04F3H), затем загружает смещение адреса процедуры my_proc (0500H) в ука&
затель команд IP.
Содержимое регистра IP изменилось, поэтому микропроцессор продолжит исполне&
ние команды с новым смещением адреса. В нашем примере такой командой будет MOV CL,6.
Когда микропроцессор обнаруживает команду RET, он извлекает адрес возврата из
стека и помещает его в указатель команд IP. Это заставляет его возобновить исполнение
команды, имеющей метку NEXT.
Помимо прямого вызова команды CALL, о котором говорилось выше, можно делать
косвенные вызовы процедуры через регистр или ячейку памяти. При косвенных вызовах
через ячейку памяти микропроцессор извлекает значение указателя команд IP для про&
цедуры из сегмента данных, если только не используется регистр ВР или не указана заме&
на сегмента. Если для адресации ячейки памяти используется регистр ВР, то микропро&
цессор извлекает значение указателя команд IP из сегмента стека.
Процедуру можно вызвать косвенно, используя переменную размером в двойное сло&
во, например:
CALL DWORD PTR[BX]
CALL mem_dword
CALL DWORD PTR SS:variable_mame[SI]
Здесь первые две команды CALL извлекают адреса процедур из сегмента данных, а по&
следняя &&&& из сегмента стека.
Процедура может сама вызывать другие процедуры. Например, процедура чтения сим&
вола с клавиатуры может его декодировать и в зависимости от результата вызвать одну из
дополнительных процедур. Вызов одной процедуры из другой называется вложением проце%
дур. Так как каждая команда CALL помещает в стек два или четыре байта адреса, то число
уровней вложения ограничено только размером сегмента стека, но поскольку сегмент стека
может иметь размер до 64 Кбайт, то возможности вложения практически не ограничены.
Помимо команды CALL можно использовать директиву INVOKE, которая расширяет воз&
можности этой команды и позволяет удобным способом передавать в процедуру аргументы.
Более подробно использование процедур будет рассмотрено в следующей главе.
Резюме
В данной главе рассмотрены основные понятия языка ассемблера. Необходимо четко
представлять, какие бывают типы операндов, знать все операторы, основные директивы
и команды, без которых нельзя составить даже простейшую программу. В главе также
описаны базовые принципы построения программы и приведено несколько примеров
полностью работоспособных программ, внимание акцентируется на тех моментах, кото&
рые обычно не учитывают начинающие программисты.
Стр. 164
отсутствует. 165
Контрольные вопросы
1. Что такое переменная и как ее можно интерпретировать?
2. Для чего используются директивы размещения данных?
3. Сколько значений можно инициализировать с помощью одной директивы раз!
мещения данных?
4. Как можно представить строку символов?
5. Для чего используется оператор DUP?
6. Что такое указатель?
7. Как ассемблер сохраняет данные в памяти?
8. Что такое константа и как ее можно создать?
9. Можно ли использовать имя для строки?
10. Что определяет директива .MODEL?
11. Назовите шесть различных моделей памяти.
12. Для чего нужно разбивать программу на сегменты?
13. При какой модели памяти сегменты не используются?
14. Каково назначение оператора SIZE?
15. Зачем нужны директивы установки типа процессора?
16. Предположим, что указатель SP инициализирован значением 0100. Какой будет
абсолютный адрес первого слова, помещенного в стек, если сегмент кодов загру!
жен по абсолютному адресу 18400h?
17. Когда возникает ошибка несоответствия типов?
18. Что такое знаковое переполнение?
19. Какие типы операндов используются в ассемблере?
20. Что такое косвенный операнд?
21. Что такое выражение?
22. Какие группы операторов вы знаете?
23. Для каждой из приведенных переменных укажите значения TYPE, LENGTH и SIZE.
24. Почему оператор WORD PTR необходим в команде ADD WORD PTR [SI],5?
Стр. 165
Глава 6
Процедуры и прерывания
В этой главе...
Операции со стеком
Процедуры
Параметры процедур
Прерывания
Ввод на уровне BIOS
Видеоконтроль на уровне BIOS
Резюме
Контрольные вопросы
В предыдущей главе уже было кратко рассказано о процедурах и показан пример их ис&
пользования. Необходимость использования процедур возникает, как только программа
становится большой и трудно понимаемой, и поэтому ее приходится разбивать на от&
дельные логические фрагменты. В хорошо разработанной программе каждая процедура
выполняет отдельную задачу независимо от остальной части программы. Каждая проце&
дура подобна отдельной прикладной программе, которую можно использовать в разных
ситуациях и вызывать много раз.
Программу любого размера сложно писать как единую последовательность команд от
начала до конца, поэтому необходимо разбить ее на отдельные, компактные и хорошо пони&
маемые модули, что значительно облегчит работу по написанию, отладке и сопровождению.
Язык ассемблера не навязывает создание структурированных программ, но использование
принципов структурного программирования при написании программ на языке ассемблера
позволяет создавать простые для понимания и легко модернизируемые программы.
Большая часть этой главы посвящена специальному типу процедур, или подпро&
грамм, позволяющих программе взаимодействовать с другими устройствами. Эти под&
программы оказывают существенную помощь при работе с консолью, диском, принте&
ром при вводе&выводе или контроле времени и размещении памяти. Эти подпрограммы
позволяют не вдаваться в детали программирования на уровне аппаратуры и обеспечи&
вают работу программы на любых устройствах с процессорами архитектуры x86.
Операции со стеком
Стек &&&& это специальный буфер в оперативной памяти, который используется для вре&
менного хранения адресов и данных. Стек размещается в сегменте стека. К каждой 16&раз&
рядной (32&разрядной) ячейке стека можно обратиться с помощью регистра SP (ESP),
Стр. 166
отсутствует. 167
называемого указателем стека. Указатель стека содержит адрес последнего элемента, по
мещенного в стек. Последнее значение, добавленное в стек, является также первым значе
нием, которое может быть извлечено из стека. Для обозначения таких структур используют
аббревиатуру LIFO (Last In First Out), что означает ‘‘последним зашел первым вышел’’.
Команда PUSH
Эта команда копирует значение в стек. Когда новое значение помещается в стек, указа
тель стека (E)SP декрементируется до того, как значение будет помещено в стек (SP всегда
должен указывать на последнее помещенное значение). Использование команды PUSH
показано в следующем примере.
MOV AX,0A5h
PUSH AX
Команда PUSH не изменяет содержимое регистра AX, она только копирует значение
AX в стек.
Предположим, что значения регистров BX и CX равны 0001 и 0002. Ниже приведены
команды помещения их в стек.
PUSH BX ; Помещает BX в стек.
PUSH CX ; Помещает CX в стек.
Значения 0001 и 0002 будут помещены в стек, как показано на рис. 6.1.
Старшие адреса
006A
00B8
0001
0002 SP (указатель стека)
Младшие адреса
Команда PUSH декрементирует указатель стека (E)SP и копирует 16 или 32разряд
ные регистры или операнды памяти в стек по адресу, на который указывает указатель
стека. При использовании процессора 80286 и более поздних моделей в стек можно по
местить непосредственное значение. Ниже приведено несколько примеров.
PUSH AX ; Поместить в стек 16-разрядный регистр.
PUSH ECX ; Поместить в стек 32-разрядный регистр.
PUSH memval ; Поместить в стек 16-разрядный операнд памяти.
PUSH 1000h ; Поместить в стек значение: только 80286 и выше.
Команда POP
Данная команда извлекает значение из стека и помещает его в регистр или перемен
ную. После извлечения значения из стека указатель его инкрементируется и будет указы
вать на предыдущий элемент стека. Для приведенного выше примера указатель стека пе
реместится вверх и будет указывать на значение 0001. Пространство стека ниже
указателя становится неопределенным.
Стр. 167
Команда POP копирует содержимое ячейки, на которую указывает указатель стека
(E)SP, и инкрементирует значение указателя. Регистры CS и IP использоваться в каче&
стве операндов не могут.
POP CX ; Извлечение из стека в 16-разрядный регистр.
POP memval ; Извлечение из стека в 16-разрядный операнд памяти.
POP EDX ; Извлечение из стека в 32-разрядный регистр.
Приведем несколько правил использования стека в программах.
Стек отлично подходит для временного хранения значений всех регистров. Реги&
стры можно использовать как оперативное пространство в программе, а затем бы&
стро восстановить их состояние.
При вызове подпрограмм процессор сохраняет адрес возврата в стеке (место в прог&
рамме, из которого была вызвана подпрограмма).
Когда вызывается процедура, можно разместить аргументы в стеке. Аргументы (зна&
чения параметров) могут быть извлечены из стека вызванной процедурой. В язы&
ках высокого уровня параметры обрабатываются именно так.
Языки высокого уровня создают пространство в стеке для подпрограмм, назы&
ваемое стековым фреймом. В этом пространстве размещаются временные пере&
менные, создаваемые подпрограммой. При возврате из подпрограммы все перемен&
ные удаляются.
Далее показано, когда возникает необходимость в многократном использовании ре&
гистров. В следующем примере строки символов отображаются на экране. Предполагает&
ся, что регистры EDX и EAX содержат важные значения, которые должны быть восстанов&
лены после отображения строки на экране. Стек является структурой LIFO, поэтому
последний сохраненный регистр будет восстановлен первым.
.DATA
message DB “Это сообщение.”,0
.CODE
PUSH EAX ; Сохранение EAX.
PUSH EDX ; Сохранение EDX.
MOV EDX,OFFSET message ; DX указывает на строку.
CALL Writestring ; Отображение строки.
POP EDX ; Восстановление EDX.
POP EAX ; Восстановление EAX.
Стр. 168
отсутствует. 169
регистры в обратном порядке. Команда PUSHAD (Intel 386 и поздние модели) сохраняет
в стеке регистры EAX, ECX, EDX, EBX, ESP, EBP, EDI, а восстанавливаются они коман&
дой POPAD.
Процедуры
Сначала остановимся на общей терминологии. В языках высокого уровня термином
функция обозначают подпрограмму, которая возвращает значение, а слово процедура
обычно используется для подпрограмм, которые не должны возвращать значение. Под&
программа &&&& более широкое понятие и обычно означает блок команд, которые могут
быть вызваны из другого места памяти. Вызов подпрограммы подразумевает, что обяза&
тельно будет совершен возврат из подпрограммы. Это не переход, возврат из которого не
требуется. В языке ассемблера термины подпрограмма, процедура и функция обычно име&
ют один и тот же смысл, хотя в некоторых случаях учитывается их смысловое различие.
mySub PROC
.
.
RET
mySub ENDP
Простая программа
Ниже приведена небольшая рабочая программа, в которой производится вызов трех
процедур. Процедура KeyCode получает символ с клавиатуры и помещает его характери&
стики в регистры AL, AH и DX.
Процедура KeyChar просто считывает символ нажатой клавиши и отображает его на
экране, а процедура Zapros используется для отображения на экране приглашения для
ввода символа.
В данном случае возникает несогласованность между кодировками Windows и DOS,
которые используются при компиляции и отображении в окне консоли. Поэтому вводите
текст английскими буквами. Для перекодировки текста можно использовать процедуру
CharToOemA. Например:
PUSH OFFSET STR1
PUSH OFFSET STR1
CALL CharToOemA
Стр. 169
Листинг 6.2. Демонстрация вызовов подпрограмм
TITLE Демонстрация вызовов подпрограмм.
; В этой программе используется три процедуры: две для
: ввода с клавиатуры и одна для вывода приглашения.
.386
include Study32.inc
.data
str1 byte "ascii = ",0
str2 byte "scan = ",0
str3 byte "key = ",0
ascii byte ?
scan byte ?
key word ?
.code
main PROC
call Zapros
call KeyChar
call KeyCode
invoke WaitMsg
exit
main endp
KeyCode PROC
LookForKey:
mov eax,50 ; Время задержки.
call Delay ; Задержка для ОС.
call ReadKey ; Считывание клавиши.
jz LookForKey ; Нет нажатия.
Стр. 170
отсутствует. 171
Zapros PROC
.data
zapr byte "Press any key ",0
.code
mov edx, offset zapr ; Вывод приглашения.
invoke WriteString
invoke Crlf
ret
Zapros endp
KeyChar PROC
invoke ReadChar
invoke WriteChar
invoke Crlf
ret
KeyChar endp
end main
Разберем подробнее эту программу. Хорошо видно, что сама программа занимает
всего несколько строчек между ее началом (main PROC) и окончанием (main endp).
Все остальное &&&& это процедуры, написанные специально для этой программы, но кото&
рые можно использовать и в других программах. Поэтому в конечном итоге их необходи&
мо будет вынести в отдельную библиотеку и применять при необходимости. При этом не
надо будет вновь писать код процедуры.
Также обратите внимание на повторяющуюся три раза последовательность из строк:
mov edx, offset str2 ; Вывод str2.
invoke WriteString
mov eax,0
mov al,scan
invoke WriteInt ; Вывод скан-кода.
invoke Crlf
Очевидно, что такое повторение увеличивает размеры листинга и затрудняет понима&
ние всей программы. Лучше повторяющуюся последовательность строк оформить как
макроопределение с соответствующим информативным именем.
mToScreen MACRO subject:REQ, value:REQ
mov edx, offset subject ; Вывод обозначения.
invoke WriteString
movzx ax,value
invoke WriteInt ; Вывод значения.
invoke Crlf
endm
Теперь программа будет выглядеть следующим образом.
.386
include Study32.inc
.data
str1 byte "ascii = ",0
Стр. 171
str2 byte "scan = ",0
str3 byte "cod = ",0
ascii word ?
scan word ?
key word ?
.code
main PROC
call Zapros
call KeyChar
call KeyCode
invoke WaitMsg
exit
main endp
KeyCode PROC
LookForKey:
mov eax,50 ; Время задержки.
call Delay ; Задержка для ОС.
call ReadKey ; Считывание клавиши.
jz LookForKey ; Нет нажатия.
mToScreen str1,ascii
mToScreen str2,scan
mToScreen str3,key
ret
KeyCode endp
Zapros PROC
.data
zapr byte "Press any key ",0
.code
mov edx, offset zapr ; Вывод приглашения.
invoke WriteString
invoke Crlf
ret
Zapros endp
KeyChar PROC
invoke ReadChar
invoke WriteChar
invoke Crlf
ret
KeyChar endp
end main
Стр. 172
отсутствует. 173
Стр. 173
Параметры процедур
Передача аргументов в регистры
В языке ассемблера наиболее общим способом для передачи аргументов в подпро&
граммы является размещение аргументов в регистрах. Этот метод эффективен, так как
вызванная подпрограмма может непосредственно использовать переданные значения,
и при этом обращение к регистрам выполняется значительно быстрее обращения к памя&
ти. Например, если некоторая процедура writeint требует, чтобы регистр EAX содержал
32&разрядное целое число, а EBX включал основание системы счисления, то вызов дол&
жен производиться так, как показано в примере ниже.
.DATA
aNumber DWORD ?
.CODE
MOV EAX,aNumber
MOV EBX,10
CALL writeint
Процедура должна отвечать за защиту значений, размещенных в регистрах, от слу&
чайного изменения. Это гарантирует, что вызывающая программа не столкнется с не&
ожиданным изменением состояния регистров. Всегда необходимо соблюдать следующее
важное правило: при написании процедур следует сохранять и восстанавливать все реги&
стры, которые могут изменяться в процедуре. Не пытайтесь игнорировать это правило
в интересах повышения скорости работы программы из&за того, что в подпрограмме нет
явной необходимости использовать некоторые регистры &&&& при необходимости модифи&
кации подпрограммы придется использовать дополнительные регистры, которые могут
непредвиденно изменяться в процедуре.
При написании процедур следует сохранять и восстанавливать все регистры, которые мо&
гут изменяться в процедуре.
Прерывания
Обобщающий термин прерывание используется для двух случаев: аппаратное прерыва%
ние &&&& это сигнал от любого устройства системы для процессора, который по этому сиг&
налу должен обслужить данное устройство, и программное прерывание, которое создается
программами BIOS или DOS для вызова сервисных подпрограмм.
Стр. 174
отсутствует. 175
Команда INT
Команда прерывания INT используется только при программировании в 16&разрядном
режиме и вызывает подпрограммы операционной системы. В 32&разрядном режиме необ&
ходимо использовать соответствующие процедуры и функции операционной системы, ко&
торые и обращаются напрямую к отдельным прерываниям. Эти прерывания имеют но&
мера в диапазоне от 0 до FFh. Перед тем как вызвать команду INT, в регистр обычно
помещают номер функции, который определяет необходимую подпрограмму. Другие
регистры тоже могут использоваться в прерывании. Синтаксис прерывания такой:
INT номер.
Обычно команды INT используются для консольного ввода&вывода, управления фай&
лами и выводом видеоизображения, а также во многих других случаях.
Стр. 175
Вызывающая Память Обработчик
программа прерывания
STI
CLD
MOV ...
PUSH ES
INT 10 F000:F065
ADD ...
IRET
Стр. 176
отсутствует. 177
Клавиша Insert
Клавиша CapsLock
Клавиша NumLock
Клавиша ScrollLock
Клавиша Alt
Клавиша Ctrl
7 6 5 4 3 2 1 0
Стр. 177
Они используются для управления выводом на экран, принтер или асинхронные устрой&
ства связи. Описание некоторых управляющих символов приведено в табл. 6.2.
08h 08 Возврат
09h 09 Горизонтальная табуляция
0Ah 10 Пропуск строки
0Ch 12 Подача страницы (для принтера)
0Dh 13 Возврат каретки (клавиша <Enter>)
1Bh 27 Переход
Символы 0Dh и 0Ah должны находиться в конце каждой строки текстового файла, так
как они управляют возвратом каретки и пропуском строки. При выводе на экран символ
возврата каретки перемещает курсор в левую сторону экрана, а символ пропуска строки пе&
ремещает курсор на одну строку вниз. Этот символ необходим, когда происходит последо&
вательный вывод строк (при его отсутствии строки будут накладываться друг на друга).
Стр. 178
отсутствует. 179
не были очень высокими. С 1991 года все компьютеры стали поставляться с различными
типами графических дисплеев VGA, которые с высоким разрешением воспроизводили
как текст, так и графику.
Видеорежимы
В основном используются два типа режимов: текстовый и графический. Текстовый ре&
жим применяется при работе в DOS, компьютер может отображать только символы в соот&
ветствии с расширенным набором символов. При этом можно использовать специальные
графические символы, которые позволяют изображать простейшие графические образы.
В режиме графики компьютер может управлять отдельными пикселями (точками), позво&
ляя рисовать линии, окружности и отображать сложные графические образы.
Видеоатрибуты
При работе в консольном режиме каждая позиция экрана может содержать отдельный
символ с собственными атрибутами. Для хранения атрибутов используется отдельный байт,
называемый байтом атрибутов. В прерывании 10h есть функция, которая позволяет ус&
тановить атрибуты (например, атрибут цвета, негативного отображения, мигания, под&
черкивания и яркости).
Видеорежим 7 отображает монохромный текст. Доступные атрибуты режима 7 пока&
заны в табл. 6.3. Например, обычное мигание записывается как 87h, яркое мигание &&&&
как 8Fh, негативное изображение &&&& как 0F0h и т.д. В то же время сам символ можно
сделать ярким, установив в третьем бите единицу.
07 Нормальный
87 Мигание
0F Яркий (подсветка)
70 Негатив
01 Подчеркивание
09 Яркое подчеркивание
Стр. 179
0 0 0 1 0 1 1 1
000 00 черный
001 01 синий
010 02 зеленый
011 03 голубой
100 04 красный
101 05 вишневый
110 06 коричневый
111 07 белый
Стр. 180
отсутствует. 181
Видеостраницы
Все графические адаптеры с использованием цвета могут сохранять в памяти несколько
изображений экрана, называемых страницами. Монохромные адаптеры могут отобра&
жать только одну страницу. Графический адаптер с использованием цвета может про&
изводить запись в одну страницу, в то время как на экран выводится другая страница,
и мгновенно переключаться между страницами. Страницы имеют номера от 0 до 7, и ко&
личество страниц зависит от текущего видеорежима (табл. 6.6).
0 07h Mono
0&&7 00h, 01h CGA
0&&3 02h, 03h CGA, EGA, VGA
0&&7 02h, 03h CGA, EGA, VGA
0&&7 0Dh EGA, VGA
0&&3 0Eh EGA, VGA
0&&1 0Fh, 10h EGA, VGA
Стр. 181
Таблица 6.7. Список функций прерывания INT 10h
Номер функции (AH) Описание
Стр. 182
отсутствует. 183
03h (получить позицию курсора). Функция возвращает номер строки и номер столбца
курсора на заданной видеостранице. Это же касается начальной и конечной линий, оп&
ределяющих размер курсора. Необходимо установить 3 в регистре AH, а номер видеостра&
ницы в регистре BH. Возвращаемые значения приведены в табл. 6.8.
Стр. 183
MOV AH,5 ; Установить видеостраницу.
MOV AL,1 ; К странице 1.
INT 10h
MOV AH,9 ; Отобразить сообщение.
MOV DX,OFFSET page1
INT 21h
MOV AH,1 ; Нажатие клавиши.
INT 21h
to_page_0:
MOV AH,5 ; Установить видеостраницу.
MOV AL,0 ; К странице 0.
INT 10h
MOV AX,4C00h ; Возврат в DOS.
INT 21h
main ENDP
END main
06h, 07h (прокрутка окна вверх и вниз). С помощью этой функции можно прокручивать
окно экрана. Термин прокрутка окна означает перемещение данных на дисплее вверх или
вниз. Если, например, изображение на дисплее прокручивается вверх, то нижние линии
заменяются пустым пространством.
Размер окна зависит от используемого количества строк и столбцов. Строки опреде&
ляются номерами 0-24 от верхней границы, а столбцы определяются номерами 0-79 от
левой границы. Координаты окна, покрывающего весь экран, будут иметь значения для
верхнего левого угла (0, 0) и нижнего правого (24, 79). При прокрутке одной или не&
скольких строк перемещаются их номера. Если прокрутить все строки, экран будет чис&
тым. Строки, вышедшие за пределы окна, не восстанавливаются. Входные параметры
для вызова функций 6 и 7 показаны в табл. 6.9.
08h (чтение символа и атрибутов). Функция возвращает символ и его атрибуты в те&
кущей позиции курсора на выбранной видеостранице. Для вызова этой функции в ре&
гистр AH помещается значение 8, а в BH &&&& номер видеостраницы. Следующие команды
помещают курсор в строку 8 и столбец 1, после чего считывают символ.
locate:
MOV AH,2 ; Установить курсор
MOV BH,0 ; на видеостранице 0
MOV DX,0501h ; в позицию 5,1.
INT 10h
getchar:
MOV AH,8 ; Считать атрибуты/символ
MOV BH,0 ; на видеостранице 0.
INT 10h
MOV CHAR,AL ; Сохранить символ.
MOV attrib,AH ; Сохранить атрибуты.
Стр. 184
отсутствует. 185
09h (записать символ и атрибуты). Функция используется для записи одного или не&
скольких символов с текущей позиции курсора. Эта функция может отобразить любой
символ в кодировке ASCII, включая специальные графические символы для кодов 1-31.
Ни один из этих символов не интерпретируется как управляющий код, что характерно
для прерывания INT 21h в DOS. Входные параметры приведены в табл. 6.10.
Фактор повторения определяет, сколько раз символ должен быть повторен. (Символ
не должен выходить за границу текущей строки экрана.) После записи символа необхо&
димо вызвать функцию 02h для перемещения курсора.
В следующих командах 32 раза записывается графический символ, определяемый ко&
дом 0Ah с атрибутами мигания. Символ 0Ah в кодировке ASCII определяет пропуск
строки, но в данном случае он отображается как белая точка на черном фоне.
MOV AH,9 ; Запись символа и атрибутов.
MOV AL,0Ah ; Символ 0Ah в кодировке ASCII.
MOV BH,0 ; Видеостраница 0.
MOV BL,87h ; Атрибуты мигания.
MOV CX,32 ; Отобразить 32 раза.
INT 10h
OAh (записать символ). Функция 0Ah выводит символ на экран в текущей позиции курсора
без изменения текущих атрибутов экрана. Эта функция подобна функции 09h, но без уста&
новки атрибутов. Следующие команды записывают символ A в текущую позицию курсора.
MOV AH,0Ah ; Записать только символ.
MOV AL,'A' ; Символ 'A'.
MOV BH,0 ; Видеостраница 0.
MOV CX,1 ; Отобразить один раз.
INT 10h
OCh (записать графический пиксель). Функция 0Ch рисует один пиксель на экране,
присваивая пикселю значение, номер видеостраницы, строку и столбец.
MOV AH,0Ch
MOV AL,pixelValue
MOV BH,videoPage
MOV EX,column ; Координата X.
MOV DX,row ; Координата Y.
INT 10h
ODh (читать графический пиксель). Функция 0Dh считывает графический пиксель
с экрана в определенной позиции строки и столбца, после чего возвращает его значение
в регистр AL.
MOV AH,0Dh
MOV BH,videoPage
MOV EX,column
Стр. 185
MOV DX,row
INT 10h
MOV pixelValue,AL
OFh (получить видеорежим). Функция 0Fh возвращает число столбцов в регистр AH,
текущий режим дисплея &&&& в AL, активную страницу &&&& в BH. (Список общих видеоре&
жимов приведен в подразделе ‘‘Видеорежимы’’.) Эта функция должна вызываться перед
выполнением любых графических функций или специфических операций с цветом. В сле&
дующих командах определяется текущий видеорежим и активная страница.
MOV AH,0fh ; Получить видеорежим.
INT 10h
MOV vmode,AL ; Сохранить режим.
MOV page,BH ; Сохранить страницу.
11h (загрузка шрифтов по умолчанию). Усовершенствованные EGA& и VGA&дисплеи,
позволяют загружать и отображать различные шрифты в текстовом режиме. С дисплеем
VGA можно использовать 25 строк с символами размером 8×16 и 28 строк с символами
размером 8×14, а также 50 строк с символами 8×8.
Для загрузки шрифтов используется функция 11h из прерывания INT 10h и под&
функции 11h, 12h или 14h для установки шрифта. Подфункция шрифтов загружается
в регистр AL.
eightByFourteen EQU 11h ; 28 строк/экран.
eightByEight EQU 12h ; 50 строк/экран.
eightBySixteen EQU 14h ; 25 строк/экран.
Резюме
В данной главе рассмотрены принципы построения программ с использованием проце&
дур и прерываний. Основное внимание уделено 16&разрядному режиму, так как только
в этом режиме можно непосредственно обращаться к ресурсам компьютера и лучше понять
работу прерываний и графики. При программировании для 32&разрядного режима обраще&
ние к прерываниям выполняется через процедуры Windows. Необходимо уметь грамотно
разбивать программу на отдельные функциональные блоки и научиться разрабатывать
программы по частям, отлаживая отдельные процедуры и используя их в дальнейшем. На&
личие достаточного количества отлаженных процедур позволит быстро и без ошибок пи&
сать довольно сложные программы. В главе также кратко рассмотрены функции работы
с консолью, с помощью которых можно не только выполнять ввод с клавиатуры или вывод
на дисплей, но и производить графическое оформление интерфейса пользователя.
Стр. 186
отсутствует. 187
Контрольные вопросы
1. Для чего нужны подпрограммы сервисных прерываний?
2. На какую ячейку указывает указатель стека?
3. Чем отличается программное прерывание от аппаратного?
4. Какими могут быть наименьший и наибольший номера прерываний?
5. На что указывают значения, помещенные в таблицу векторов прерываний?
6. Почему прикладные программы непосредственно не управляют аппаратными
устройствами?
7. Какое прерывание обслуживает клавиатуру? Какие возможности при этом пре&
доставляются?
8. Что такое скан&код клавиатуры? Чем он отличается от кода ASCII?
9. Какой управляющий символ в кодировке ASCII перемещает курсор в левую сто&
рону экрана?
10. Назовите, по крайней мере, четыре атрибута, которые можно использовать с
адаптером для цветного дисплея.
11. Какая функция из прерывания INT 10h отображает символ на экране без изме&
нения атрибутов экрана?
12. Какая функция прерывания INT 10h устанавливает видеорежим?
13. Когда значение помещается в стек, размер стека в памяти увеличивается. В сто&
рону каких адресов увеличивается размер стека: старших или младших?
14. Почему структура стека называется LIFO?
Стр. 187
Глава 7
Логические вычисления
В этой главе...
Стр. 188
отсутствует. 189
Операторы AND, OR, NOT и XOR выполняют поразрядные операции для целых чисел во
время трансляции. Операнд может быть целой константой или символом. Например, до&
пустимы следующие выражения:
X = 1101101b
Y = X AND 00001111b
Z = X OR 11110000b
Q = NOT Z
W = Y XOR 0FFFFh
Команда AND
Команда AND выполняет логическое умножение (И) для каждого разряда двух операн&
дов и помещает результат в первый операнд. Разрешены следующие комбинации.
AND регистр, регистр
AND регистр, память
AND регистр, значение
AND память, регистр
AND память, значение
Регистр, память или значение могут иметь 8, 16 или 32 разряда, при этом операторы
должны иметь одинаковый размер. Для каждых соответствующих битов в двух операндах
применимо следующее правило: если оба бита равны 1, то результирующий бит равен 1,
в противном случае он равен 0. Используются следующие флаги: переполнения, знака,
нуля, паритета, переноса и дополнительного переноса. В следующем примере показан
результат операции AND для 4&разрядных чисел.
0 0 1 1
AND 0 1 0 1
Результат: 0 0 0 1
С помощью команды AND можно очищать отдельные биты в операнде, при этом за&
щищая или маскируя оставшиеся биты. В следующем примере значение 00001111 явля&
ется битовой маской.
MOV AL,00111011b
AND AL,00001111b ; AL = 00001011b
Операцию AND можно использовать для установки байта состояния клавиатуры, для DOS
который может размещаться по адресу 0040:0017. Установленный в единицу бит 5 оп&
ределяет состояние клавиши <NumLock> как включенное. Можно выключить клавишу
<NumLock>, просто сбросив бит 5 (установив в нуль):
MOV BX,17h ; Указать на байт клавиатуры.
AND BYTE PTR [BX],11011111b ; Выключить NumLock.
Здесь требуется оператор BYTE PTR, без которого ассемблер не может определить раз&
мер операнда памяти.
Команда OR
Команда OR выполняет операцию логического сложения (ИЛИ) между соответствую&
щими битами двух операндов и помещает результат в первый операнд. Следующие ком&
бинации операндов разрешены.
Стр. 189
OR регистр, регистр
OR регистр, память
OR регистр, значение
OR память, регистр
OR память, значение
Регистр, память или значение могут иметь 8, 16 или 32 разряда, кроме того, операторы
должны иметь одинаковый размер. Для каждых соответствующих битов в двух операндах
применимо следующее правило: если оба бита равны 0, то и результирующий бит равен 0,
в противном случае результат равен 1. Используются флаги переполнения, знака, нуля, па&
ритета, переноса и дополнительного переноса. В следующем примере показан результат
операции OR для 4&разрядных чисел.
0 0 1 1
OR 0 1 0 1
Результат: 0 1 1 1
Например, применим команду OR к значениям 3Bh и 0Fh. Нижние четыре бита уста&
новятся, а старшие биты не изменятся.
MOV AL,00111011b ; 3Bh.
OR AL,00001111b ; AL = 3Fh.
Такой способ можно использовать для преобразования десятичного числа в кодиров&
ку ASCII (табл. 7.2.), устанавливая биты 4 и 5. Например, AL=05h можно преобразовать
в символ 5 согласно кодировке ASCII (35h) с помощью команды OR с числом 30h.
Стр. 190
отсутствует. 191
Команда XOR
Команда XOR (исключающее ИЛИ) выполняет логические операции между соответст&
вующими битами двух операндов. Следующие комбинации операндов разрешены.
XOR регистр, регистр
XOR регистр, память
XOR регистр, значение
XOR память, регистр
XOR память, значение
Первый операнд содержит результат операции. Только один операнд может быть опе&
рандом памяти. Для каждых соответствующих битов в двух операндах применимо следую&
щее правило: если оба бита одинаковы, то и результирующий бит равен 0, в противном слу&
чае результат будет равен 1. Используются флаги переполнения, знака, нуля, паритета,
переноса и дополнительного переноса.
Ниже показан результат операции XOR для отдельных битов. Команда XOR устанавли&
вает бит в единицу, только если исходные биты различны:
0 0 1 1
XOR 0 1 0 1
Результат: 0 1 1 0
Инверсия бит
Отличительной особенностью команды XOR является то, что она может инвертиро&
вать саму себя, когда применяется дважды. Предположим, к числу x применяется коман&
да XOR с числом y, в результате получается число z. Затем, если применить операцию XOR
к числам z и y, получим x. Например:
1 1 0 1 0 1 0 1 x
XOR 1 1 1 1 0 0 0 0 y
Равно: 0 0 1 0 0 1 0 1 z = x XOR y
XOR 1 1 1 1 0 0 0 0 y
Равно: 1 1 0 1 0 1 0 1 x = z XOR y
Команда NOT
Команда NOT инвертирует все биты исходного операнда, изменяя нули на единицы
и наоборот. Результат называется дополнение до единицы. Следующие операнды разрешены.
NOT регистр
NOT память
Стр. 191
Например, дополнение до единицы числа F0h будет равняться 0Fh.
MOV AL,11110000b
NOT AL ; AL = 00001111b.
Команда NEG
Команда NEG инвертирует знак числа, преобразовывая число в его дополнение до двух.
Следующие команды разрешены.
NEG регистр
NEG память
Подсчет дополнения до двух от числа может быть выполнен путем инверсии всех его
битов и суммированием с единицей. Используются следующие флаги: переполнения, ну&
ля, знака, дополнительного переноса и переноса.
После выполнения команды NEG проверьте флаг переполнения для определения пра&
вильности результата. Например, если поместить в регистр AL отрицательное число -128,
то в результате операции получим -128 (неправильный результат) и флаг 0F = 1:
MOV al,-128 ; AL = 10000000b
NEG al ; AL = 10000000b, 0F = 1
С другой стороны, если инвертировать +127, то результат будет правильный и 0F = 0:
MOV al,+127 ; AL = 01111111b
NEG al ; AL = 10000001b, 0F = 0
Команда TEST
Команда TEST выполняет команду AND для двух операндов, но не сохраняет резуль&
тат, а только устанавливает флаги. Это особенно удобно для контроля отдельных битов.
Ни один из операндов не изменяется. Следующие команды разрешены.
TEST регистр, регистр
TEST регистр, память
TEST регистр, значение
TEST память, регистр
TEST память, значение
Если любые соответствующие биты установлены в обоих операндах, ZF = 0. Использу&
ются следующие флаги: переполнения, нуля, знака, дополнительного переноса и переноса.
Например, проверить состояние принтера в системе DOS можно с помощью преры&
вания INT 17h, которое проверяет состояние принтера и возвращает байт состояния в ре&
гистр AL. Если пятый бит равен единице, то в принтере нет бумаги. Следующие команды
выполняют проверку и сбрасывают флаг нуля, если установлен бит 5:
MOV AH,2 ; Функция: чтение состояния принтера.
INT 17h ; Вызов BIOS.
TEST AL,00100000b ; ZF = 0, нет бумаги.
Стр. 192
отсутствует. 193
Команда CMP
Команда сравнения CMP выполняет вычитание операнда&отправителя из операнда&
получателя, но при этом ни один из операндов не модифицируется. Результат отражается
в состоянии регистра флагов. Разрешены следующие комбинации операндов, где первый
операнд всегда является операндом&получателем, а второй &&&& операндом&отправителем.
CMP регистр, регистр
CMP регистр, память
CMP регистр, значение
CMP память, регистр
CMP память, значение
Стр. 193
Регистры сегментов не могут быть использованы. Применяются следующие флаги:
переполнения, нуля, знака, дополнительного переноса и переноса.
Когда сравниваются значения без знака, флаги нуля и переноса устанавливаются ко&
мандой CMP в соответствии с табл 7.4.
Команда CMPXCHG
Команда сравнения и изменения CMPXCHG для процессора 80486 сравнивает оператор&
получатель с аккумулятором (AL, AX или EAX). Если значения равны, значение оператора&
отправителя копируется в оператор&получатель, в противном случае оператор&получатель
копируется в аккумулятор. Используются все флаги состояния. Формат команды:
CMPXCHG получатель, отправитель
Стр. 194
отсутствует. 195
Логические структуры
Речь идет не о логических структурах высокого уровня, хотя с помощью системы ко&
манд ассемблера можно смоделировать любую логическую структуру, используя команды
сравнений и переходов. Логическая структура &&&& это лишь несколько логических ко&
манд, работающих совместно. Например, структура WHILE использует условие IF для
оценки условия, как показано в табл. 7.6.
Стр. 195
основе состояния флагов. В следующем примере команда JZ переходит на метку next,
если AL = 0.
CMP AL,0
JZ next ; Переход, если ZF = 1.
...
next:
Команда Jcond
Команда перехода по условию передает контроль по указанному адресу, когда при&
знак условия установлен. Синтаксис этой команды следующий:
Jcond метка
Суффикс cond определяет признак условия, идентифицируя состояние одного или
нескольких флагов. Например:
JC ; Переход, если флаг переноса установлен.
JNC ; Переход, если флаг переноса сброшен.
JZ ; Переход, если флаг нуля установлен.
JNZ ; Переход, если флаг нуля сброшен.
При каждом условном переходе проверяются один или несколько флагов, возвращая
результат (истина или ложь). Если результатом является истина, то переход совершается,
в противном случае программа продолжает исполнение со следующей команды.
Предположим, необходимо совершить переход на метку equal, когда значения реги&
стров AX и BX равны. В следующем примере CMP устанавливает флаг нуля в единицу, если
AX = BX. Команда JE делает переход, если ZF = 1.
1: CMP AX,BX ; Сравнение AX и BX.
2: JE equal
3: NOT_equal: ; Продолжение здесь, если AX <> BX.
4: ...
5: ...
6: JMP exit
7: equal: ; Переход, если AX = BX.
8: ...
9: exit: ; Окончание всегда здесь.
Для того чтобы лучше понять эту программу, используем различные значения для AX и BX.
Если AX = 5 и BX = 5, то команда CMP (строка 1) устанавливает флаг нуля, так как AX
и BX равны. Команда JE (строка 2) делает переход на метку equal в строке 7. Все команды
с этого места точки выполняются последовательно.
Если AX = 5 и BX = –6, то команда CMP сбрасывает флаг нуля, так как AX и BX не рав&
ны. Команда JE не выполняется, поэтому выполнение продолжается со строки 3, проходя
последовательно все команды до строки 6, а потом происходит переход на метку exit.
Стр. 196
отсутствует. 197
Стр. 197
Окончание табл. 7.9
Мнемокод Описание Флаги
JL Переход, если меньше (op1<op2) SF<>OF
JNGE Переход, если не больше или равно (op1 NOT>=op2) SF<>OF
JLE Переход, если меньше или равно (if op1<=op2) ZF=1 или SF<>OF
JNG Переход, если не больше (op1 NOT>op2) ZF=1 или SF<>OF
JS Переход, если отрицательное (op1 отрицательное) SF=1
JNS Переход, если положительное (op1 положительное) SF=0
JO Переход, если переполнение ОF= 1
JNO Переход, если нет переполнения ОF = 0
Стр. 198
отсутствует. 199
Стр. 199
; Процедура isAlpha устанавливает ZF = 1,
; если символ в AL является буквой.
isAlpha PROC
PUSH AX ; Сохранить AX.
AND AL,11011111b ; Преобразование в прописной символ.
CMP AL,'A' ; Проверка диапазона'A'...'Z'.
JB B1
CMP AL,'Z'
JA B1
TEST AX,0 ; ZF = 1.
B1:
POP AX ; Восстановление AX.
RET
isAlpha ENDP
END main
Стр. 200
отсутствует. 201
L3:
ADD EDI,4 ; Указать следующий номер.
LOOP L1 ; Повторять, пока CX не равно 0.
MOV EAX,largest
INVOKE WriteInt ; Вывод значения largest.
INVOKE Crlf
INVOKE WaitMsg
EXIT
main ENDP
END main
Команда SETcond
Команда SETcond для процессоров 80386 устанавливает в байт операнда 1, если усло&
вие истинно, и 0 &&&& если ложно. Синтаксис:
SETcond регистр8
SETcond память8
Слово cond здесь условно обозначает флаг, который процессор должен проверить.
Параметр регистр8 может быть любым 8&разрядным регистром, а память8 &&&& любой 8&
разрядный операнд памяти. Возможные значения для cond включают суффиксы команд
условных переходов, как показано в табл. 7.10 и 7.11.
В следующем примере регистр AL=1 после команды SETB, так как команда CMP уста&
навливает флаг переноса.
MOV BX,20h
CMP BX,30h
SETB AL ; AL = 1
Стр. 201
С другой стороны, AL=0 после SETE, так как команда CMP не устанавливает флаг нуля.
MOV BX,20h
CMP BX,30h
SETE AL ; AL = 0
Циклы с условием
Команды LOOPZ и LOOPE
Команды LOOPZ и LOOPE (повторять пока установлен флаг нуля и счетчик больше ну&
ля) разрешают повторение цикла, пока ZF=1 и CX>0. Синтаксис этих команд следующий.
LOOPZ метка
LOOPE метка
Сначала декрементируется CX. Затем, если CX>0 и ZF=1, процессор совершает переход
на метку, в противном случае начинает выполняться следующая команда.
В листинге 7.4 команда LOOPZ просматривает массив целых чисел, пока не встретится
ненулевое значение. Поскольку команда CMP сравнивает каждое значение с 0, то флаг нуля
будет установлен или сброшен. Если встречается ненулевое значение, то команда LOOPZ
не делает переход на метку next.
Стр. 202
отсутствует. 203
.CODE
MOV SI,OFFSET byteArray-1
MOV EX,arraySize
next:
INC SI
TEST BYTE PTR [SI],10000000b
LOOPNZ next
Обратите внимание, что команда INC перед командой TEST не изменяет флаг нуля меж
ду командами TEST и LOOPNZ.
Стр. 203
Окончание табл. 7.12
Оператор Описание
выраж1 < выраж2 Возвращает TRUE, если выраж1 меньше выраж2
выраж1 <= выраж2 Возвращает TRUE, если выраж1 меньше или равно выраж2
!выраж Возвращает TRUE, если выражение ложно
выраж1 && выраж2 Возвращает логическое AND между выраж1 и выраж2
выраж1 || выраж2 Возвращает логическое OR между выраж1 и выраж2
выраж1 & выраж2 Возвращает поразрядное AND между выраж1 и выраж2
CARRY? Возвращает TRUE, если установлен флаг переноса
OWERFLOW? Возвращает TRUE, если установлен флаг переполнения
PARITY? Возвращает TRUE, если установлен флаг четности
SIGN? Возвращает TRUE, если установлен флаг знака
ZERO? Возвращает TRUE, если установлен флаг нуля
Стр. 204
отсутствует. 205
Стр. 205
CALL WriteDec
CALL Crlf
.UNTIL EAX ==10
Можно использовать и вложения выражений, которые используют директивы для ус&
ловных выражений. Например, в приведенной ниже конструкции на языке высокого
уровня используется вложение условных выражений IF в цикл.
while( op1 < op2 )
{
opl++;
if( op2 == op3 )
X = 2;
else
X=3
}
Аналогичную конструкцию можно написать и на языке ассемблера. В данном случае
переменные opl, op2, и op3 помещаются в регистры, чтобы не использовать в одной
команде два операнда памяти, что в ассемблере запрещено.
.data
opl DWORD 2
op2 DWORD 4
op3 DWORD 5
.code
mov eax,opl
mov ebx,op2
mov ecx,op3
.WHILE eax < ebx
inc eax
.IF ebx == ecx
mov X,2
.ELSE
mov X,3
.ENDIF
.ENDW
Резюме
В данной главе продолжено изучение основных команд процессоров с архитектурой х86.
Рассмотрены команды логических вычислений, используемых при создании логических
структур программы и обработке различных условий, на основе которых происходит пе&
реход на отдельные ветви программы. С помощью этих команд можно разработать логи&
ческие структуры, соответствующие операторам языков высокого уровня, таким как IF,
WHILE, FOR, CASE, и в дальнейшем использовать их в своих разработках.
Контрольные вопросы
1. Какие логические операторы используются в языке ассемблера?
2. Могут ли логические операторы выполняться во время трансляции?
3. Объясните различие между командами JMP и JZ.
4. Какой флаг используется при сравнении чисел без знака?
Стр. 206
отсутствует. 207
Стр. 207
Глава 8
Команды сдвига
Простые прикладные программы
Сложение и вычитание больших чисел
Умножение и деление
Кодировка ASCII и арифметика упакованных чисел
Резюме
Контрольные вопросы
Команды сдвига
Эта глава посвящена выполнению арифметических операций с целыми числами на
языке ассемблера. Команды сдвига и циклического сдвига используются при умножении
и делении. Такие команды в языках высокого уровня отсутствуют, поэтому при разра)
ботке приложений на этих языках для достижения высокой производительности необхо)
димо использовать язык ассемблера. В главе рассмотрены основные арифметические
операции: сложение, вычитание, умножение и деление с использованием команд сдвига.
В конце кратко будут затронуты вопросы арифметики с числами в кодировке ASCII
(одна цифра на байт) и арифметика упакованных десятичных чисел. В главе описаны
только основные команды и приемы работы.
Команды сдвига используют флаги переполнения и переноса.
Команда SHL
Команда SHL (сдвиг влево) перемещает каждый бит в операнде влево, заполняя
младшие биты нулями. Старший бит помещается во флаг переноса, а бит, который до
этого находился во флаге переноса, теряется (рис. 8.1).
Флаг CF 0
7 6 5 4 3 2 1 0
Стр. 208
отсутствует. 209
Быстрое умножение
Команду SHL удобно использовать для быстрого умножения. Стандартные команды
умножения работают значительно медленнее, чем с использованием этого способа. Ко&
личество битов, на которое необходимо сделать сдвиг, должно равняться двоичному ло&
1
гарифму множителя. Например, умножение на 2 (2 ) &&&& это сдвиг на один бит, умноже&
2
ние на 4 (2 ) &&&& сдвиг на 2 бита и т.д. В следующем примере показано десятичное
значение регистра DL после каждого сдвига.
MOV DL,1 ; DL = 1d.
SHL DL,1 ; DL = 2d.
SHL DL,1 ; DL = 4d.
SHL DL,1 ; DL = 8d.
Для умножения на число, которое не является степенью 2, можно использовать раз&
ложение по формуле. Например, умножение числа на десять можно разложить на два
3
этапа: сначала умножить на 8 (2 ) и затем сложить с числом, умноженным на два, т.е. ис&
3
пользовать формулу 10=2 +2.
Стр. 209
SHLD регистр16, регистр16, счетчик
SHLD память16, регистр16, счетчик
SHLD регистр32, регистр32, счетчик
SHLD память32, регистр32, счетчик
Например, в следующих командах происходит сдвиг влево на 4 бита переменной wval
и вставляются 4 старших бита из регистра AX в младшие позиции wval.
.DATA
wval DW 9BA6h
.CODE
MOV AX,0AC36h
SHLD wval,AX,4 ; wval = BA6Ah.
В примере ниже регистр AX сдвигается вправо на 4 бита, и младшие 4 бита регистра DX
помещаются на место старших битов регистра AX.
MOV AX,234Bh
MOV DX,7654h
shrd AX,DX,4 ; AX = 4234h.
В следующем примере на 1 бит влево сдвигается регистр EBX, а старший бит пере
менной dval помещается в младшую позицию EBX:
.DATA
dval DD 8124365Ah
.CODE
MOV EBX,00000006h
SHLD EBX,dval,1 ; EBX = 0000000Dh.
Эти команды широко используются в прикладных графических программах, где
необходимо распределять битовые маски изображений для вывода на экран. Они также
находят применение при шифровании данных, где алгоритм шифрования использует
сдвиги битов.
Команда SHR
Команда сдвига вправо SHR сдвигает каждый бит вправо, заменяя старший бит нулем.
Младший бит копируется во флаг переноса, а бит, находившийся ранее во флаге перено
са, теряется (рис. 8.2).
0 Флаг CF
7 6 5 4 3 2 1 0
Синтаксис команды SHR такой же, как и для команды SHL. В следующем примере 0 из
младшего бита регистра AL копируется во флаг переноса, а старший бит AL сбрасывается:
MOV AL,0D0h ; AL = 11010000b.
SHR AL,1 ; AL = 01101000b, CF = 0.
Команда SHR может использоваться для деления чисел без знака на 2. Например,
осуществив сдвиг числа 32 на 1 бит вправо, получим 16.
MOV DL,32 ; 00100000b
SHR DL,1 ; 00010000b (DL = 16)
Стр. 210
отсутствует. 211
Флаг CF
7 6 5 4 3 2 1 0
Команда ROL
Команда циклического сдвига влево ROL сдвигает каждый бит влево. Старший бит ко
пируется во флаг переноса и в младший бит. Синтаксис команды такой же, как и для
команды SHL (рис. 8.4).
Флаг CF
7 6 5 4 3 2 1 0
Отличие команд циклического сдвига от сдвига в том, что биты никогда не теряются.
Бит, который “выталкивается” с одного конца, появляется на другом конце.
Стр. 211
В следующем примере старший бит копируется и во флаг переноса и в младший бит
с номером 0.
MOV AL,40h ; AL = 01000000b.
ROL AL,1 ; AL = 10000000b, CF = 0.
ROL AL,1 ; AL = 00000001b, CF = 1.
ROL AL,1 ; AL = 00000010b, CF = 0.
Команда ROL удобна для обмена старшей и младшей половинок операнда, как пока
зано в листинге 8.1.
Команда ROR
Команда циклического сдвига вправо ROR сдвигает каждый бит вправо. Младший бит
копируется во флаг переноса и одновременно в старший бит. Синтаксис команды здесь
такой же, как для команды SHL (рис. 8.5).
Флаг CF
7 6 5 4 3 2 1 0
Стр. 212
отсутствует. 213
Флаг CF
7 6 5 4 3 2 1 0
влево. Вторая команда RCL перемещает флаг переноса в младший бит и смещает все ос
тальные биты влево.
CLC ; CF = 0.
MOV BL,88h ; BL = 10001000b.
RCL BL,1 ; BL = 00010000b, CF = 1.
RCL BL,1 ; BL = 00100001b, CF = 0.
Команда RCR
Команда циклического сдвига вправо с копированием RCR сдвигает каждый бит впра
во и копирует младший бит во флаг переноса. Флаг переноса копируется в старший бит
результата (рис. 8.7).
Флаг CF
7 6 5 4 3 2 1 0
Стр. 213
После сдвига байт 1 принимает значение 00011101, и устанавливается флаг переноса
(CF=1). Затем используется команда RCR для циклического сдвига байта 2 вправо, когда ко&
пируется содержание флага переноса в старший бит байта 2. После сдвига байт 2 принимает
значение 10100011. Наконец, байт 3 сдвигается вправо, результатом будет 01111111. Эти
три шага повторяются каждый раз для сдвига на несколько битов. В листинге 8.2 сдвига&
ются все биты для трех байтов вправо на четыре бита.
Строка битов
Часто байт или слово содержат больше одного поля, поэтому приходится выде&
лять короткую последовательность битов, называемую битовой строкой, или строкой
битов. Например, функция 7 DOS возвращает дату файла в регистр DX (дата послед&
него обновления файла). Биты 0&&4 представляют день (от 1 до 31), биты 5&&8 представ&
ляют месяц, а биты 9&&15 &&&& год. Например, предположим, что файл был модифициро&
ван 10 марта 1999 года. Тогда дата будет представлена в регистре DX следующим
Стр. 214
отсутствует. 215
этого правила выделим день месяца. Сначала сделаем Рис. 8.8. Формат даты файла
копию регистра DL и маску. В регистре AL будет резуль
тирующее значение.
MOV AL,DL ; Делаем копию DL.
AND AL,00011111b ; Сбрасываем биты 5-7.
MOV day,AL ; Сохраняем день.
Для получения номера месяца необходимо сдвинуть биты 58 в правый конец регист
ра AL перед маскированием остальных битов.
MOV AX,DX ; Сделать копию DX.
MOV CL,5 ; Счетчик сдвигов.
SHR AX,CL ; Сдвинуть вправо на 5 бит.
AND AL,00001111b ; Сбросить биты 4-7.
MOV month,AL ; Сохранить месяц.
Номер года (биты 915) занимает почти весь регистр DH. Необходимо переместить его
в регистр AL и сдвинуть на 1 бит вправо.
MOV AL,DH ; Сделать копию DH.
SHR AL,1 ; Сдвинуть вправо на одну позицию.
MOV AH,0 ; Сбросить AH.
ADD AX,1980 ; Счет от 1980 года.
MOV year,AX ; Сохранить год.
Директива RECORD
Директива RECORD (запись) в языке ассемблера описывает группу битов в байте или
слове. Ее используют, чтобы сделать описание битовой маски и сдвиги проще. Сначала
для записи должно быть определено имя и ширина каждого поля. Синтаксис директи
вы следующий:
имя записи RECORD поле1[, поле2] ...
Синтаксис поля:
имя поля: ширина[= выражение]
Имя поля это имя отдельного поля в записи, причем первое поле располагается
в старших битах байта или слова. Выражение представляет собой целочисленное кон
стантное выражение.
Используем директиву RECORD для определения рас
положения битов в 16разрядной дате для файла. Распо
ложение битов включает 7 битов для номера года, 4 бита 0010011001101010
для месяца и 5 битов для дня месяца (рис. 8.9).
Обычное использование директивы RECORD для да Год (915) Месяц (58) День (04)
ты следующее:
Рис. 8.9. Расположение битов
date_record RECORD year:7, month:4, day:5
в слове, представляющем дату
Стр. 215
За каждым именем поля следует его ширина, выраженная количеством битов.
Можно присваивать полям значения по умолчанию. Например, инициализация записи
date_record для 1 января 1980 года будет иметь такой вид (значение 0 обозначает 1980 год):
date_record RECORD year:7=0, month:4=1, day:5=l
Если определить запись в восемь битов или меньше, запись автоматически будет ссы&
латься на байт, в противном случае &&&& на слово. Если используются не все позиции, про&
исходит автоматическое выравнивание вправо. Например, следующая запись определяет
только 12 битов. Ассемблер устанавливает младшие биты, а в старшие четыре бита запи&
сывается нуль.
bitrec RECORD fieldl:6=llllllb, field2:6=llllllb
Значение всех 16 битов будет равно 0000111111111111.
Если запись определена, ее можно использовать для создания переменной записи.
Синтаксис:
[имя] имя записи < [значение[,значение]] ...>
Можно присвоить начальное значение любому полю. Если присваиваемое значение
превышает допустимое, ассемблер выводит сообщение об ошибке.
Создадим имя записи, используя ранее определенную запись date_record. Сле&
дующее объявление содержит значения (0,1,1) для 1 января 1980 года.
date_record RECORD year:7=0, month:4=1, day:5=l
.
birthDate date_record <>
Можно инициализировать запись birthDate значением 30 мая 1999 года.
birthDate date_record <19,5,30>
Оператор MASK
Поскольку мы использовали директиву RECORD для определения записи date_record,
можно улучшить предыдущие утверждения. Оператор MASK создает битовую маску &&&& все
биты, соответствующие данному полю, устанавливаются, остальные сбрасываются.
MOV AX,file_date ; Получить дату.
AND AX,MASK month ; Сбросить неиспользуемые биты.
MOV CL,month
SHR AX,CL ; Сдвинуть вправо на 5 бит.
Оператор WIDTH
Оператор ширины поля WIDTH возвращает число выделенных для поля битов в запи&
си. В следующем примере ширина каждого поля в записи date_record определяется
по&разному, как и ширина всей записи. Эти значения определяются во время трансля&
ции, как показано в листинге 8.3.
Стр. 216
отсутствует. 217
.CODE
MOV AX,width day ; Ширина = 5.
IF (WIDTH date_record) GT 8 ; Запись 16 разрядов?
MOV AX,birthDate ; Поместить в AX.
ELSE ; Запись 8 разрядов?
MOV AL,birthDate ; Поместить AL.
ENDIF
; Multi32_Add
; Складывает два целых числа, содержащих несколько двойных слов.
; Входные параметры: ESI и EDI указывают на операнды, BX указывает на
Стр. 217
; операнд-получатель, и ECX содержит число суммируемых двойных слов.
.CODE
Multi32_Add PROC
PUSHAD
CLC ; Сбрасывает флаг переноса.
L1:MOV EAX,[ESI] ; Получает первый операнд.
ADC EAX,[EDI] ; Суммирует второй операнд.
PUSHFD ; Сохраняет флаг переноса.
MOV [EBX],EAX ; Сохраняет результат.
ADD ESI,4 ; Продвигает все 3 указателя.
ADD EDI,4
ADD EBX,4
POPFD ; Восстанавливает флаг переноса.
LOOP L1 ; Повторение цикла.
ADC WORD PTR [EBX],0 ; Сложить остаток.
POPAD
RET
Multi32_Add ENDP
END main
Команда SBB
Команда вычитания с заемом SBB используется для вычитания больших операндов.
Синтаксис этой команды следующий:
SBB получатель,отправитель
Операнд&отправитель и операнд&получатель могут быть 8& или 16&разрядными значе&
ниями. Сначала операнд&отправитель вычитается из операнда&получателя, затем флаг
переноса вычитается из результата.
В листинге 8.5 одно учетверенное слово вычитается из другого. Команда SBB вычита&
ет флаг переноса и содержимое операнда op2 из регистра AL. Директива DQ сохраняет
байты в памяти и изменяет порядок, поэтому используется регистр SI.
В этом примере оператор op1 больше, чем op2, поэтому результат положительный.
Если результат будет отрицательный, флаг переноса будет установлен после последнего
цикла, причем результат будет сохранен в форме двоичного дополнения.
Команда очистки флага переноса CLC сбрасывает флаг переноса. Это необходимо делать
перед командами ADC или SBB только в начале. И напротив, последнее значение флага оп&
ределяет результат. Команда установки флага переноса STC помещает во флаг значение 1.
Стр. 218
отсутствует. 219
Умножение и деление
В системе команд Intel есть операции, которые выполняют умножение и деление 8&,
16& и 32&разрядных целых чисел. Все операнды должны быть двоичными числами, по&
этому все десятичные и двоично&десятичные числа должны быть приведены к необходи&
мому формату. Операции с вещественными числами должны выполняться в блоке обра&
ботки вещественных чисел или должны эмулироваться программно (программы
эмуляции дополнительно поставляются с ассемблером).
Команда умножения MUL и команда деления DIV используют двоичные числа. Ко&
манды целочисленного умножения IMUL и целочисленного деления IDIV применяются
для двоичных чисел со знаком.
Когда процессор выполняет арифметические операции, он рассматривает все операн&
ды как двоичные числа без знака. Если используются числа со знаком, то необходимо
контролировать флаги во избежание переполнения операнда&приемника. Хотя процес&
сор корректно выполняет все арифметические операции с числами без знака, операции
с числами со знаком тоже будут выполняться правильно. Например, сложим FFFFh
и 3000h. Результатом будет 2FFFh.
MOV CX,3000h ; Начальное значение в CX.
ADD CX,0FFFFh ; CX = 2FFF, 0F = 0, CF = 1.
Сумма чисел без знака 3000h и 0FFFFh будет равна 12FFFh, что вызывает переполнение
регистра CX и устанавливает флаг переноса. Сумма чисел со знаком 3000h и FFFFh (–1)
будет равна 2FFFh, что является правильным результатом.
Флаг переполнения указывает, было ли переполнение в результате операций над чис&
лами со знаком. Например, в регистре AL содержится число 7Fh (+127). Добавляем к AL
единицу. Результат 80h (–128) явно неправильный.
После такой операции будет установлен флаг переполнения. Для операций с 8&раз&
рядными числами флаг переноса OF равен c7 XOR c6, где c7 определяет перенос из
бита 7, а c6 &&&& перенос из бита 6. В следующем примере в шестой позиции генерирует&
ся перенос, в отличие от седьмой позиции. Таким образом, OF=(0 XOR 1), что равня&
ется единице.
Стр. 219
01111111 (+127)
+ 00000001 (+1)
10000000 (-128, OF = 1, CF = 0)
Команда MUL
Команда MUL выполняет умножение 8&, 16& или 32&разрядных операндов с помощью
регистров AL, AX или EAX. Синтаксис команды следующий:
MUL множитель
IMUL множитель
Множитель может быть или регистром, или операндом памяти, но не может быть не&
посредственным значением. В табл. 8.2 показаны все регистры, которые используются
при каждом типе операции в зависимости от размера умножаемых чисел.
Стр. 220
отсутствует. 221
Команда IMUL
Команда IMUL служит для перемножения двоичных чисел со знаком. Результатом
также будет число со знаком. При операции с 8&разрядными числами знак регистра AL
переносится в AH. При 16&разрядных операндах знак в AX переносится в DX, и при 32&раз&
рядных операндах знак EAX переносится в EDX.
При перемножении 8&разрядных операндов IMUL устанавливает флаги переноса и пе&
реполнения, если регистр результата AH не получает знак AL. Это значит, что регистры
знака AH и AL имеют разные значения.
Как для команды MUL, так и для команды IMUL, флаги CF и OF устанавливаются, если
старший байт результата содержит значение, в противном случае они сбрасываются.
Следующие примеры демонстрируют это.
Операция с 8&разрядными числами: 48*4=192.
MOV AL,48
MOV BL,4
IMUL BL ; AX = 00C0h (+192), CF = 1, OF = 1
Результирующее число в AX содержит значение 00C0h (0000000011000000). Здесь
нет расширения знака на регистр AH, поэтому CF=1 и OF=1. Значение результата при
этом больше, чем 7 битов.
Операция с 8&разрядными числами: -4*4=-16.
MOV AL,-4
MOV BL,4
IMUL BL ; AX = FFF0h (-16), CF = 0, OF = 0
Результирующим значением в AX является число FFF0h (&&16), и регистр AH получает
знаковое расширение регистра AL (заполняется знаком AL), поэтому CF=0 и OF=0.
Операция с 16&разрядными числами: 48*4=192.
MOV AX,48
MOV BX,4
IMUL BX ; DX = 0000, AX = 00C0h(+192), CF = 0, OF = 0.
Результат в DX:AX представляет 000000C0h. Знаки DX и AX являются одинаковыми
(положительными), поэтому CF=0 и OF=0.
Команда DIV
Команда DIV выполняет деление 8&, 16& и 32&разрядных чисел без знака. Указывается
один операнд (регистр иди операнд памяти), который является делителем. Синтаксис
этой команды следующий:
DIV делитель
Если делитель &&&& 8&разрядное значение, делимое находится в регистре AX. В AL на&
ходится целая часть результата, а в AH &&&& остаток. Если делитель является 16&разрядным
числом, делимое находится в DX:AX. В AX помещается целая часть результата, а в DX &&&&
остаток (табл. 8.3).
Стр. 221
Таблица 8.3. Регистры команды DIV
Делимое Делитель Частное Остаток
AX операнд 8 AL AH
DX:AX операнд 16 AX DX
EDX:EAX операнд 32 EAX EDX
Команда IDIV
Команда IDIV имеет такой же синтаксис, как и команда DIV с теми же операндами.
Различие заключается в выполнении операций над числами со знаком. Для 8&разрядных
операторов делимое находится в регистре AX, поэтому знак определяется по биту 15. На&
пример, если &&48 разделить на 5, то AL=-9, а AH=-3.
MOV AX,-48 ; AX = FFD0h.
MOV BL,5
IDIV BL ; AX = FDF7h (частное = -9, остаток = -3).
Общая ошибка заключается в том, что перед делением помещают 8&разрядное дели&
мое в регистр AL. В следующем примере команда IDIV выполняется некорректно, так
как делимое получается равным +208 (00D0h), и частное будет неправильным.
MOV AH,0
MOV AL,-48 ; AX = 00D0h (+208).
MOV BL,5
IDIV BL ; AX = 0329h (частное = 41, остаток = 3).
Стр. 222
отсутствует. 223
Например, для рассмотренного выше примера деления –48 команда CBW обеспечит
правильный результат.
MOV AH,0
MOV AL,-48 ; AX = 00D0h (+208).
CBW ; AX = FFD0h (-48).
MOV BL,5
IDIV BL ; AX = FDF7h (частное=–9, остаток=-3).
Стр. 223
Рис. 8.10. Число 3402 в разных форматах
Команда AAA
Команда AAA корректирует двоичный результат команд ADD или ADC. Это делает ре&
зультат в регистре AL совместимым с представлением чисел в кодировке ASCII. В сле&
дующем примере показано, как получается корректный результат при сложении двух чи&
сел (8 и 12) в кодировке ASCII с помощью команды AAA. Необходимо очистить регистр
AH перед выполнением сложения. Последняя команда превращает значения в регистрах
AH и AL в числа в кодировке ASCII.
MOV AH,0
MOV AL,'S' ; AX = 0038h.
ADD AL,'2' ; AX = 006Ah.
AAA ; AX = 0100h (ASCII корректировка результата).
OR AX,3030h ; AX = 3130h = '10' (преобразование в ASCII).
Команда AAS
Команда AAS корректирует двоичный результат команд SUB или SBB. Это делает ре&
зультат в регистре AL совместимым с представлением чисел в кодировке ASCII. Данная
корректировка необходима, когда при вычитании получается отрицательный результат.
В следующем примере вычитается число 9 в формате ASCII из числа 8. После команды
SUB регистр AX получает значение 00FFh (&&1). Команда AAS преобразует AX в FF09h, де&
сятичное дополнение до &&1.
.DATA
val1 DB '8'
val2 DB '9'
.CODE
MOV AH,0
MOV AL,val1 ; AX = 0038h.
sub AL,val2 ; AX = 00FFh.
AAS ; AX = FF09h.
PUSHF ; Сохранить флаг переноса.
OR AL,30h ; AX = FF39h.
POPF ; Восстановить флаг переноса.
Команда AAM
Команда AAM корректирует двоичный результат команды умножения MUL. Умноже&
ние может выполняться с неупакованными десятичными числами, но не может выпол&
няться с числами в формате ASCII, пока старшие четыре бита каждого числа не будут
сброшены. В следующем примере перемножаются числа 5 и 6, а результат корректирует&
ся в регистре AX. После корректировки в AX появится число 0300h, которое является не&
упакованным представлением десятичного числа 30.
Стр. 224
отсутствует. 225
.DATA
ascVal DB 05h, 06h
.CODE
MOV BL,ascVal ; Первый операнд.
MOV AL,ascVal+l ; Второй операнд.
MUL BL ; AX = 001Eh.
AAM ; AX = 0300h.
Команда AAD
Команда AAD корректирует неупакованное десятичное делимое в регистре AX перед
операцией деления. В следующем примере число 35 в кодировке ASCII делится на 5.
Сначала команда AAD превращает 0307h в 0025h. Затем команда DIV получает частное
07h из AL и остаток 02h из AH.
.DATA
quotient DB ?
remainder DB ?
.CODE
MOV AX,0307h ; Делимое.
AAD ; AX = 0025h.
MOV BL,5 ; Делитель.
DIV BL ; AX = 0207h.
MOV quotient,AL
MOV remainder,AH
Стр. 225
Команда DAS преобразовывает двоичный результат команд SUB или SBB в регистре AL
в упакованный десятичный формат. Например, в следующих командах вычитается упа&
кованное десятичное число 48 из 85, а результат корректируется:
MOV BL,48h
MOV AL,85h
SUB AL,BL ; AL = 3Dh.
DAS ; AL = 37h (скорректированный результат).
Резюме
В данной главе рассмотрены команды выполнения операций с целыми числами. Это
наиболее часто используемые в программах команды и поэтому необходимо хорошо
знать их работу и возможные ограничения и ошибки. Начинающие программисты обыч&
но не обращают внимания на величины чисел и во время отладки программа может рабо&
тать отлично. Однако при работе с большими числами может возникнуть переполнение
и расчеты будут выполняться неправильно. В языках высокого уровня такие ошибки
фиксируются компилятором или возникает исключительная ситуация, но в языке ас&
семблера контроль диапазона возможных значений &&&& задача самого программиста.
Контрольные вопросы
1. Предложите возможные пути сложения следующих двух десятичных чисел в фор&
мате ASCII:
2.1234
300.5
2. Какая команда передвигает каждый бит операнда влево и копирует старший бит во
флаг переноса и в позицию младшего бита?
3. Какая команда передвигает каждый бит вправо, копирует младший бит во флаг
переноса, а флаг переноса копирует в позицию старшего бита?
4. Какая команда сдвигает каждый бит вправо и дублирует бит знака?
5. Какая команда передвигает каждый бит влево, копирует флаг переноса в позицию
младшего бита и копирует старший бит во флаг переноса?
6. Что случится с содержимым флага переноса после выполнения команды SHR?
7. Как с помощью команды сдвига можно умножить содержимое регистра AX на 16?
8. Как с помощью команды сдвига можно разделить содержимое регистра BX на 4?
9. Напишите программу с использованием команды сдвига для умножения регистра
AL на 12.
10. Напишите одну команду циклического сдвига, которая меняет местами старшую
и младшую половинки регистра DL.
11. Напишите команду, которая перемещает старший бит регистра DL в младший бит
регистра DH.
12. Какое значение будет в регистре DX после выполнения следующих команд?
MOV DX,5
STC
MOV AX,10h
ADC DX,AX
Стр. 226
Глава 9
Структуры
и макроопределения
В этой главе...
Структуры
Введение в макроопределение
Дополнительные макроопределения и директивы
Резюме
Контрольные вопросы
Структуры
Директива STRUC определяет структуру, которая может быть отображена в памяти. Струк&
тура может инициализироваться в программе значениями по умолчанию. Отдельные час&
ти структуры называются полями.
Структура должна быть объявлена до того, как она будет использоваться в программе.
Например, можно описать структуру с полями, определяющими отдельного сотрудника.
Следующее описание структуры должно быть помещено в исходный файл перед сегмен&
том данных.
EMPNUMBER_SIZE = 7
LASTNAME_SIZE = 20
typEmployee STRUC
IdNum DB EMPNUMBER_SIZE+1 DUP(0)
Lastname DB LASTNAME_SIZE+1 DUP(0)
Credits DW ?
Status DB ?
typEmployee ENDS
В сегменте данных можно объявить переменную типа ‘‘структура’’.
.DATA
sRec typEmployee <>
Угловые скобки (<>) означают, что ассемблер должен зарезервировать поля в соответ&
ствии с описанием структуры.
При объявлении переменной типа ‘‘структура’’ можно изменить значения полей, уста&
новленных по умолчанию. Новые значения должны разделяться запятой, причем любое
Стр. 227
значение может быть пропущено, но оставлена разделяющая запятая. В следующем при&
мере определяются поля в переменной типа ‘‘структура’’ typEmployee.
sRec typEmployee <"1234","Gennady",32,0> ; Изменить все поля.
myRec typEmployee <,,50,0> ; Изменить последние два поля.
yourRec typEmployee <,"Govorov"> ; Изменить только второе поле.
Как только структура определена, ссылка на отдельные поля может производиться по
именам переменной и поля, разделенных точкой.
MOV DL,sRec.IdNum
MOV AX,sRec.Credits
Ассемблер добавляет к структуре смещение поля от начала структуры для получения
эффективного адреса. В структуре typEmployee смещение поля IdNum равно 0, так как
это первое поле в структуре. Смещение поля LastName равно 8.
Если регистр базы или регистр индекса содержат смещение структуры, то точка раз&
деляет косвенный оператор от имени поля. При использовании ассемблера Microsoft не&
обходимо поставить оператор PTR.
MOV BX,OFFSET sRec
MOV AX,(typEmployee PTR [BX]).Credits
MOV DL,(typEmployee PTR [BX]).Status
Можно использовать и другие модели адресации при ссылке на переменные струк&
турного типа.
MOV AX,sRec[SI].Credits
MOV DL,[BX+SI].Status
Для объявления массива переменных структурного типа можно использовать опера&
тор DUP.
allEmployee typEmployee 100 DUP(<>)
В следующих командах используется регистр ESI для указания на элементы массива
allEmployee. Оператор SIZE возвращает значение 32 &&&& число байт для размещения
одного элемента массива (структура typEmployee).
MOV ESI,OFFSET allEmployee
MOV ECX,Employee_COUNT
L1:CALL InputEmployee
ADD ESI,SIZE typEmployee
LOOP L1
SYSTEMTIME STRUCT
wYear WORD ?
wMonth WORD ?
wDayOfWeek WORD ?
wDay WORD ?
Стр. 228
отсутствует. 229
wHour WORD?
wMinute WORD ?
wSecond WORD ?
wMilliseconds WORD ?
SYSTEMTIME ENDS
Обе структуры описаны в подключаемом файле, листинг которого приведен в при&
ложении.
Для получения системного времени, установленного с учетом временной зоны, вызы&
вается функция GetLocalTime, в которую передается адрес структуры SYSTEMTIME.
.DATA
sysTime SYSTEMTIME <>
.CODE
INVOKE GetLocalTime, ADDR sysTime
Затем извлекаются соответствующие значения из структуры SYSTEMTIME. Например:
MOVZX EAX,sysTime.wYear
CALL WriteDec
Перед тем как произвести вывод данных на экран, вызывается функция Windows
GetStdHandle для получения стандартного дескриптора выхода консоли.
.DATA
consoleHandle DWORD ?
.CODE
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
MOV consoleHandle,EAX
Константа STD_OUTPUT_HANDLE определена в заголовочном файле.
Для установления позиции курсора вызывается функция SetConsoleCursorPosition,
в которую передается дескриптор консоли и переменная типа COORD, содержащая коор&
динаты X, Y в символах.
.DATA
XYPos COORD <10,5>
.CODE
INVOKE SetConsoleCursorPosition, consoleHandle, XYPos
Полностью программа показана в листинге 9.1. Она может быть запущена только в за&
щищенном режиме.
Стр. 229
INVOKE SetConsoleCursorPosition, consoleHandle, XYPos
INVOKE GetLocalTime, ADDR sysTime
; Отобразить системное время (hh:mm:ss).
MOVZX EAX,sysTime.wHour ; Часы.
CALL WriteDec
MOV EDX,offset colonStr ; ":".
CALL WriteString
MOVZX EAX,sysTime.wMinute ; Минуты
CALL WriteDec
MOV EDX,offset colonStr ; ":".
CALL WriteString
MOVZX EAX,sysTime.wSecond ; Секунды
CALL WriteDec
CALL Crlf
CALL Crlf
CALL WaitMsg ; "Press Enter..."
exit
main ENDP
END main
Введение в макроопределение
Макроопределение &&&& это одно или несколько утверждений языка ассемблера, кото&
рым присвоено символическое имя и которые можно вызывать по этому имени. Однажды
описанное, оно может вызываться сколь угодно много раз в программе. Когда происхо&
дит вызов макроопределения, копия команд, представляющих данное макроопределе&
ние, вставляется непосредственно в программу. И хотя внешне операции перехода по
ссылке и вызов макроопределения похожи, но в техническом плане вызов макроопреде&
ления &&&& это не то же самое, что вызов подпрограммы командой CALL.
Макроопределение выполняется быстрее, чем процедура, имеющая те же самые ко&
манды. Это происходит потому, что для вызова процедуры необходимы дополнительные
команды CALL и RET, что заставляет процессор организовывать переход на другую ветвь
и делать возврат из нее в исходную точку. Это одно из преимуществ применения макрооп&
ределений, но их использование увеличивает объем программного кода, так как каждый
вызов макроопределения вставляет в программу код самого макроопределения.
Например, команда CALL Crlf производит перемещение курсора в начало следую&
щей строки. Эта команда должна размещаться в каждой точке программы, где необходи&
мо сделать вывод на дисплей с новой строки, но лучше создать макроопределение с понят&
ным именем NewLine:
NewLine MACRO
CALL Crlf
ENDM
После того как макроопределение будет описано, его можно вызывать из любого мес&
та программы. При вызове макроопределения все команды, входящие в состав макрооп&
ределения, помещаются в то место программы, где произошел вызов. Например:
.CODE
NewLine
В каждой точке программы, где появляется имя макроопределения, вставляются команды
из тела макроопределения, в данном случае это CALL Crlf. Вставка кодов происходит
Стр. 230
отсутствует. 231
при первом прохождении ассемблером исходного файла, и они будут присутствовать в лис&
тинге, сгенерированном ассемблером.
Очень важной особенностью макроопределения является возможность включать па&
раметры.
Макроопределение вызывается при размещении его имени в исходном коде програм&
мы со всеми необходимыми аргументами. Синтаксис макроопределения следующий:
имя макроопределения argument_1, argument_2, ...
Каждый аргумент является значением, передаваемым в макроопределение. Это зна&
чение заменяет соответствующие параметры в макроопределении при его вызове. Поря&
док аргументов должен соответствовать порядку параметров, но число аргументов не
обязательно равно числу этих параметров. Если аргументов слишком много, то ассемб&
лер выдаст предупреждение, но если аргументов мало, то оставшиеся параметры будут
пропущены. Директива условия IFB в ассемблере позволяет выполнять проверку пропу&
щенных аргументов.
Макроопределение может быть создано в любом месте программы с помощью дирек&
тив MACRO и ENDM. Синтаксис:
имя макроопределения MACRO parameter_1, parameter_2...
список команд
ENDM
Чтобы как&то выделить макроопределение для удобства чтения программы, в его
имени можно применять определенный префикс. В данном случае в качестве префикса
используется строчная буква ‘‘m’’.
При макроопределении может быть любое число параметров, которое можно размес&
тить на одной строке, разделяя их запятыми.
Для выделения комментариев в макроопределениях два раза ставится точка с запя&
той (;;). При этом комментарии появляются только в написанном макроопределении,
а не в тех фрагментах, которые вставляются в тело программы.
Используя спецификатор REQ, можно задать обязательную установку требуемых па&
раметров. Если макроопределение вызывается без необходимых аргументов, ассемблер
генерирует ошибку. Например:
mPutChar MACRO char:REQ ;; Необходимый параметр.
PUSH EAX
MOV AL, char
CALL WriteChar
ENDM
Если в макроопределении несколько необходимых параметров, каждый параметр
должен включать спецификатор REQ.
Примеры макроопределений
Макроопределение mWriteStr
Создадим макроопределение с именем mWriteStr для записи строки в стандартный
выход. Для данного макроопределения требуется один параметр, который назовем string
и который будет ссылаться на нужную строку.
mWriteStr MACRO string
PUSH EDX
MOV EDX,OFFSET string
CALL WriteString
POP EDX
ENDM
Стр. 231
Параметр string замещается при каждом вызове макроопределения. Например, для
отображения трех строк макроопределение вызывается три раза, используя каждый раз
различные аргументы.
.DATA
msgl DB "Это сообщение l.",0Dh,0Ah,'$'
msg2 DB "Это сообщение 2.",0Dh,0Ah,'$'
msg3 DB "Это сообщение 3.",0Dh,0Ah,'$'
.CODE
mWriteStr msg1
mWriteStr msg2
mWriteStr msg3
Таким образом, параметры позволяют использовать макроопределение mWriteStr
с наибольшей выгодой. Макроопределения могут вызывать сами себя, как будет показа&
но в дальнейшем. Однако ценой всех этих удобств будет заметное увеличение размера
выполняемого кода при частом вызове макроопределений, так как будут сгенерированы
следующие команды:
; mWriteStr msgl
PUSH EDX
MOV EDX,OFFSET msgl
CALL WriteString
POP EDX
; mWriteStr msg2
PUSH EDX
MOV EDX,OFFSET msg2
CALL WriteString
POP EDX
; mWriteStr msg3
PUSH EDX
MOV EDX,OFFSET msg3
CALL WriteString
POP EDX
Макроопределение mReadStr
Макроопределение mReadStr содержит вызов библиотечной процедуры ReadString.
Она принимает имя массива символов.
mReadStr MACRO varName
PUSH ECX
PUSH EDX
MOV EDX,OFFSET varName
MOV ECX,(SIZEOF varName) - 1
CALL ReadString
POP EDX
POP ECX
ENDM
Ниже показан пример вызова макроопределения mReadStr.
.DATA
firstName BYTE 30 DUP(?)
.CODE
mReadStr firstName
Макроопределение mGotoxy
Это макроопределение размещает курсор на определенной строке и в заданном столбце
экрана. Используя квалификатор REQ, можно указать параметры как необходимые. Если
Стр. 232
отсутствует. 233
Макроопределение mDumpMem
Рассматривая вызов различных процедур, можно заметить, что иногда это выглядит
довольно неуклюже. Например, для процедуры DumpMem, описанной ранее, требуется
передача адреса в регистре ESI, количество отображаемых модулей в ECX и размер модуля
памяти в EBX. В следующем примере отображается восемь двойных слов, принадлежащих мас&
сиву.
PUSH EBX ; Сохранение регистров.
PUSH ECX
PUSH ESI
MOV ESI,OFFSET array ; Адрес массива.
MOV ECX,8 ; Счетчик.
MOV EBX,TYPE array ; Отобразить двойное слово.
CALL DumpMem
POP ESI ; Восстановить регистры.
POP ECX
POP EBX
Необходимо сохранять в стеке регистры ESI, EBX и ECX (которые могут содержать
важные данные) до того, как использовать команду CALL.
Будет удобнее, если написать макроопределение, в котором вызов процедуры проис&
ходит внутри макроопределения. В теле макроопределения можно сохранить регистры,
Стр. 233
разместить необходимые аргументы, сделать вызов процедуры и восстановить все значе&
ния. Напишем макроопределение mDumpMem.
mDumpMem MACRO address, ;; Адрес переменной.
itemCount, ;; Количество элементов.
componentSize ;; Размер каждого элемента.
PUSH EBX ;; Сохранение регистров.
PUSH ECX
PUSH ESI
MOV ESI,address ;; Инициализация аргументов.
MOV ECX,itemCount
MOV EBX,componentSize
CALL DumpMem ;; Вызов процедуры.
POP ESI ;; Восстановление регистров.
POP ECX
POP EBX
ENDM
Выход этого макроопределения можно произвести так:
mDumpMem OFFSET array, 8, 4
Альтернативный формат вызова макроопределений позволяет использовать символ
продолжения строки (\) в конце первой и второй строк.
mDumpMem OFFSET array, \ ; Смещение массива.
LENGTHOF array, \ ; Количество модулей.
TYPE array ; Размер каждого модуля.
Стр. 234
отсутствует. 235
CALL Writestring
POP EDX
; mWrite " Введите фамилию "
.DATA
??0001 BYTE " Введите фамилию ",0
.CODE
PUSH EDX
MOV EDX,OFFSET ??0001
CALL Writestring
POP EDX
Имена, генерируемые ассемблером, имеют вид ??nnnn, где nnnn &&&& уникальное це&
лое число. Директива LOCAL обязательно должна использоваться для имен внутри кода
макроопределения, тогда макроопределение может вызываться несколько раз.
Вложенные макроопределения
Быстро написать комплексное макроопределение можно с помощью компоновки суще&
ствующих макроопределений. Макроопределение, включенное в другое макроопределение,
называется вложенным. Создадим макроопределение с именем mDisplayRowCol, которое
отображает строки в заданном месте экрана. В нем будут вызываться макроопределения
mGotoRowCol и mDisplayStr, получающие в качестве аргументов значения, которые пе&
редаются макроопределению mDisplayRowCol в соответствии с его параметрами.
mDisplayRowCol MACRO row,col,string
mGotoRowCol row,col ; Вызов mGotoRowCol.
mDisplayStr string ; Отображение строки.
ENDM
Стр. 235
Аргументы, передаваемые в макроопределение, могут быть регистрами, переменными
или константами для удобства вызова процедуры.
.DATA
wordval DW 1000h
.CODE
mWriteint 2000h,10 ; Непосредственное десятичное значение.
mWriteint DX,16 ; Шестнадцатеричное значение регистра.
mWriteint wordval,2 ; Двоичный операнд памяти.
Стр. 236
отсутствует. 237
макроопределении с именем mymac проверяется значение для параметра parm1. Если mymac
вызывается без аргументов, директива EXITM прекратит генерацию кодов.
mymac MACRO parm1
IFB <parm1> ; Аргумент пропущен?
EXITM ; Выходим из макроопределения
ENDIF ; и не генерируем код.
...
.CODE
mymac ; Код не сгенерирован.
mymac val1 ; Код получен.
Аргументы по умолчанию
Другой путь для решения проблемы с пропуском параметров заключается в установке
значений по умолчанию для параметров.
Например, макроопределение mWriteLn может использовать строку, содержащую
один пробел как аргумент по умолчанию. Если произвести вызов без аргументов, то будет
напечатан пробел с концом строки.
mWriteLn MACRO text:=<" ">
mWrite text
CALL Crlf
ENDM
Если в качестве аргумента по умолчанию использовать пустую строку (“”), то ассемблер
зафиксирует ошибку.
Директивы условия, такие как IF, должны включать выражение, которое может возвратить
значения истина или ложь во время трансляции. Нельзя использовать значения в регист&
рах или памяти, так как они могут быть определены только во время работы программы.
Булевы выражения
Стр. 237
Можно использовать такой формат для IF, ELSE и ENDIF:
IF выражение
утверждения
ELSE
утверждения
ENDIF
В качестве примера рассмотрим макроопределение mGotoxyConst (листинг 9.2), в ко&
тором использованы операторы LT и GT для проверки допустимого диапазона аргументов.
Аргументы X и Y должны быть константами. Еще одна константа с символическим именем
ERRS содержит количество найденных ошибок. Если ошибочен аргумент X, то значение ERRS
становится равным 1, если ошибочен аргумент Y, то к значению ERRS добавляется 1. На&
конец, если ERRS больше, чем 1, то происходит выход из макроопределения.
IFIDN <идентификатор>,<идентификатор>
statements
ENDIF
Стр. 238
отсутствует. 239
Оператор замены
Оператор замены (&) разрешает заменять параметр значением, которое передается как
аргумент. Синтаксис оператора замены следующий:
&параметр
Этот оператор особенно полезен, когда в качестве аргумента используется текстовое
сообщение и этот аргумент передается строке или команде. С помощью оператора заме&
ны можно разрешать неоднозначные ссылки на имена параметров в макроопределении.
Например, макроопределение ShowRegister отображает имя и шестнадцатеричное со&
держимое 32&разрядного регистра.
.CODE
ShowRegister ECX
Стр. 239
После выполнения этого кода будет выведена следующая строка:
ECX=00000101
Строковая переменная, содержащая имя регистра, может быть определена внутри
макроопределения.
ShowRegister MACRO regName
.DATA
tempStr BYTE " regName=",0
Однако препроцессор предположит, что regName &&&& часть строкового литерала, и не бу&
дет заменять его значением аргумента, передаваемого в макроопределение. Вместо этого
он добавит оператор & для вставки аргумента макроопределения, подобного ECX, в стро&
ковый литерал:
ShowRegister MACRO regName
.DATA
tempStr BYTE " ®Name=",0
В следующем листинге содержится макроопределение ShowRegister, в котором ис&
пользуется процедура DumpRegs.
.DATA
tempStr BYTE " ®Name=",0
.CODE
PUSH EAX
PUSH EDX
; Отобразить имя регистра.
MOV EDX,offset tempStr
CALL WriteString
; Отобразить содержимое регистра.
MOV EAX,regName
CALL WriteHex
POP EDX
POP EAX
ENDM
Оператор выражения
Оператор выражения (%) позволяет рассчитать результат выражения и передать его
как аргумент в макроопределение или преобразовать константное выражение в его тек&
стовое представление. Оператор указывает ассемблеру на то, что необходимо передать
результат расчета выражения, а не само выражение. При совместном использовании с ди&
рективой TEXTEQU выполняется вычисление выражения и преобразование его в целочис&
ленное значение.
В следующем примере вычисляется выражение (5+count) и возвращается значение
15 как символьный текст.
count = 10
sumVal TEXTEQU %(5 + count) ; = "15"
Если в макроопределении требуется целочисленный аргумент, оператор % предостав&
ляет различные возможности передачи целых чисел. Вычисляется выражение, после чего
целочисленный результат передается в макроопределение. Например, при вызове макро&
определения mGotoxyConst будут рассчитаны выражения:
mGotoxyConst %(5 * 10), %(3 + 4)
Стр. 240
отсутствует. 241
Стр. 241
Например, если макроопределение будет вызвано в строке 40
MUL32 val1,EAX,val3,
то будет отображено следующее сообщение:
Ошибка в строке 40: регистр EAX не может быть вторым
аргументом при вызове макроопределения MUL32.
Стр. 242
отсутствует. 243
Директива IRP
Директива IRP создает повторяющийся блок, где каждое повторение включает разные
значения. Синтаксис этой директивы таков:
IRP аргумент,< аргумент[, аргумент] ...>
команды
ENDM
Блок повторяется только для каждого аргумента. При каждом повторении очеред&
ной аргумент передается в качестве параметра. Эта директива полезна при инициали&
зации таблиц или блоков данных, где содержатся различные значения. Аргумент может
быть символическим именем, строкой или числовой константой. Используя следующее
Стр. 243
написание макроопределения, проанализируем распределение данных, сгенерированных
ассемблером.
IRP parm,<10,20,30,40>
DW parm, parm * 2, parm * 3, parm * 4
ENDM
Сгенерировано.
DW 10, 10 * 2, 10 * 3, 10 * 4
DW 20, 20 * 2, 20 * 3, 20 * 4
DW 30, 30 * 2, 30 * 3, 30 * 4
DW 40, 40 * 2, 40 * 3, 40 * 4
С помощью директивы IRP можно создать таблицу смещений процедур. Это хороший
стиль программирования для выбора из нескольких ветвей по индексу.
MOV BX,indexvalue ; Выбор раздела таблицы.
CALL proctable[BX] ; Косвенный вызов.
В следующем фрагменте с использованием директивы IRP в качестве аргументов пере&
даются четыре имени процедур. Каждое имя последовательно передается в параметр
procname, в результате чего получается таблица смещений процедур.
proctable LABEL word
IRP procname,<MOVup,MOVdn,MOVlft,MOVrt>
DW procname
ENDM
Ассемблер сгенерирует следующие команды.
proctable LABEL word
DW MOVup
DW MOVdn
DW MOVlft
DW MOVrt
Стр. 244
отсутствует. 245
Оператор замены (&) позволяет заменять строку аргументов при вызове макроопреде&
ления. В приведенном примере директива IRP произведет вызов макроопределения во&
семь раз, используя различные значения переменной styp при каждом вызове.
Директива IRPC
Директива IRPC подобна директиве IRP, за исключением того, что число символов в
аргументе строка определяет число повторений. Синтаксис этой директивы следующий:
IRPC параметр,строка
команды
ENDM
Строка может быть заключена в угловые скобки (<>), если она содержит пробелы, за&
пятые или другие специальные символы. В следующем примере создается пять перемен&
ных (value_A, value_B и т.д.), в качестве аргумента используется строка ABCDE.
IRPC parm,ABCDE
value_&parm DB '&parm'
ENDM
Сгенерированные команды:
value_A DB 'A'
value_B DB 'B'
value_C DB 'C'
value_D DB 'D'
value_E DB 'E'
Макроопределения предоставляют огромные возможности для расширения системы
команд &&&& вы ограничены только пределами вашей фантазии. Когда соберется достаточ&
но большая коллекция макроопределений, приходится каждый раз копировать их в соз&
даваемую программу. Вместо этого создайте отдельный файл с макроопределениями
и используйте директиву INCLUDE для подключения его при ассемблировании. Теперь
необходимые макроопределения станут частью окончательной программы. Если исполь&
зуется двухпроходной ассемблер, такой как Microsoft Assembler (MASM), хорошей идеей
будет ограничить INCLUDE директивами IF1 и ENDIF, которые заставят ассемблер вклю&
чать макроопределение только при первом проходе.
IF1 ; Если это первый проход,
include macros.inc ; подключить файл макроопределений.
ENDIF
Если не указан полный путь подключаемого файла, ассемблер будет искать его в те&
кущем каталоге. Можно дополнительно указать полный или относительный путь таким
образом:
include ..\..\library\macros.INC
Резюме
В данной главе рассмотрены дополнительные возможности языка ассемблера, позво&
ляющие создавать блоки команд и применять их в различных местах программы. Ис&
пользование дополнительных возможностей значительно повышает удобство и надеж&
ность программирования, поэтому необходимо иметь четкое представление о том, что
такое структуры или макроопределения, уметь анализировать программы и находить
Стр. 245
повторяющиеся фрагменты, которые можно оформить в виде структур, процедур или мак&
роопределений, что в конечном итоге сделает вашу программу хорошо понимаемой
не только для вас.
Контрольные вопросы
1. Почему удобно использовать структуры? Напишите пример структуры myStruct
с двумя полями.
2. Напишите макроопределение с именем mPUSHData, которое помещает регистры
EAX, EBX, ECX и EDX в стек, и второе макроопределение, которое извлекает те же
самые регистры из стека.
3. В чем заключается преимущество использования макроопределений от вызова
процедур?
4. Напишите пример макроопределения с использованием данных.
5. Каково назначение директивы IFB?
6. Каково назначение директивы IFIDN?
7. Какая директива прерывает выполнение кода макроопределения?
8. Чем директива IFIDNI отличается от директивы IFIDN?
9. Каково назначение директивы IFDEF?
10. Какая директива отмечает конец блока условия?
11. Покажите пример параметра макроопределения, инициализированного по умол&
чанию.
12. Каково назначение оператора &?
13. Каково назначение оператора !?
Стр. 246
Глава 10
Написание графических
приложений Windows
В этой главе...
Стр. 247
Обратите внимание, что директива /SUBSYSTEM:WINDOWS поставлена вместо исполь&
зуемой ранее /SUBSYSTEM:CONSOLE. В программе будут функционировать стандартные
библиотеки Windows kernel32.lib и user32.lib.
Стр. 248
отсутствует. 249
style &&&& комбинации различных опций стиля, таких как WS_CAPTION и WS_BORDER,
которые определяют поведение окна и его видимость.
IpfnWndProc &&&& указывает на функцию в программе, которая принимает и обра&
батывает сообщения о событиях, инициируемых пользователем.
cbClsExtra &&&& ссылается на память, совместно используемую всеми окнами,
принадлежащими данному классу. Может иметь нулевое значение.
cbWndExtra &&&& определяет количество дополнительных байт для размещения
следующего экземпляра окна.
hlnstance &&&& содержит дескриптор текущего экземпляра программы.
hlcon &&&& содержит дескриптор для ресурсов пиктограммы текущей программы.
hCursor &&&& содержит дескриптор для ресурсов курсора текущей программы.
hbrBackground &&&& содержит цвет фона.
IpszMenuName &&&& указывает на строку меню.
IpszClassName &&&& указывает на строку с нулевым окончанием, содержащую имя
класса окна.
Функция MessageBox
Простейшим способом отобразить текст в программе будет использование окна со&
общений, в котором указываются необходимые данные и которое высвечивается на
экране и сохраняется до тех пор, пока пользователь не щелкнет на кнопке. Функция
MessageBox из библиотеки Win32 API отображает простое окно сообщений, прототип
которого показан ниже.
MessageBox PROTO,
hWnd:DWORD, pText:PTR BYTE, pCaption:PTR BYTE, style:DWORD
Параметр hWnd определяет дескриптор текущего окна. Параметр pText указывает на
строку с нулевым окончанием, которая содержит необходимый текст для отображения. Па&
раметр pCaption указывает на строку с нулевым окончанием, которая появляется в заго&
ловке окна. Параметр style &&&& целочисленное значение, с помощью которого указываются
пиктограмма окна сообщений (дополнительно) и кнопки (обязательно). Кнопки определяют&
ся такими константами, как MB_OK и MB_YESNO. Пиктограммы определяются константами,
подобными MBJCONQUESTION. При отображении окна сообщений можно использовать со&
вместно константы как для пиктограмм, так и для кнопок, как показано в примере ниже.
INVOKE MessageBox, hWnd, ADDR QuestionText,
ADDR QuestionTitle, MB_OK + MB_ICONQUESTION
Процедура WinMain
Для каждого приложения Windows необходима процедура запуска, обычно называе&
мая WinMain, которая выполняет следующие задачи:
получает дескриптор текущей программы;
загружает пиктограммы и курсоры текущей программы;
регистрирует класс основного окна программы и идентифицирует процедуру, ко&
торая будет выполнять обработку сообщений для окна;
Стр. 249
создает основное окно;
отображает и обновляет основное окно;
начинает циклический процесс приема и диспетчеризации сообщений.
Процедура WinProc
Процедура WinProc принимает и обрабатывает все сообщения о событиях, относя&
щихся к окну. Большинство событий создает пользователь, щелкая мышкой и перетас&
кивая объекты, нажимая клавиши клавиатуры и т.д. Процедура декодирует каждое сооб&
щение о событии, и если сообщение распознано, то обращается к соответствующим
функциям для выполнения задач, связанных с окном. Прототип функции следующий:
WinProc PROC,
hWnd:DWORD, ; Дескриптор окна.
localMsg:DWORD, ; Идентификатор (ID) сообщения.
wParam:DWORD, ; Параметр 1 (изменяется).
lParam:DWORD ; Параметр 2 (изменяется).
Содержимое третьего и четвертого параметров будет отличаться в зависимости от со&
общения. Например, при щелчке мыши lParam содержит координаты X и Y точки, в ко&
торой произошел щелчок.
В демонстрационной программе, приведенной ниже, процедура WinProc обрабатыва&
ет три сообщения о событиях, которые создаются:
WM_LBUTTONDOWN &&&& при нажатии левой кнопки мыши;
WM_CREATE &&&& при появлении основного окна;
WM_CLOSE &&&& во время закрытия основного окна.
Например, следующие строки обрабатывают сообщение WM_LBUTTONDOWN, при этом
вызывается окно сообщений для отображения сообщения.
.IF EAX == WM_LBUTTONDOWN
INVOKE MessageBox, hWnd, ADDR PopupText,
ADDR PopupTitle, MB_OK
JMP WinProcExit
В результате нажатия левой кнопки мыши на экране появится следующее окно сооб&
щений (рис. 10.1).
Стр. 250
отсутствует. 251
Процедура ErrorHandler
Процедура ErrorHandler будет вызываться, если возникнет сбой при регистрации
и создании основного окна программы. Например, функция RegisterClass должна
возвратить ненулевое значение при успешном создании и регистрации окна. Однако если
будет возвращен нуль, то при этом вызывается процедура ErrorHandler для отображе&
ния сообщения и выхода из программы.
INVOKE RegisterClass, ADDR MainWin
.IF EAX == 0
CALL ErrorHandler
JMP Exit_Program
.ENDIF
Процедура ErrorHandler выполняет несколько важных задач:
вызывает процедуру GetLastError для извлечения номера системной ошибки;
вызывает процедуру FormatMessage и извлекает подходящую строку форматиро&
вания для сообщения об ошибке;
вызывает процедуру MessageBox для получения и отображения окна сообщения,
содержащего необходимое сообщение об ошибке;
вызывает процедуру LocalFree для освобождения памяти, занимаемой строкой
сообщения об ошибке.
Листинг программы
Пусть вас не смущает размер листинга 10.1 &&&& большинство фрагментов данной про&
граммы используются в любом приложении для Windows.
Стр. 251
msg MSGStruct <>
winRect RECT <>
hMainWnd DWORD ?
hInstance DWORD ?
.code
WinMain PROC
; Получить дескриптор текущего процесса.
INVOKE GetModuleHandle, NULL
mov hInstance, eax
mov MainWin.hInstance, eax
; Загрузить пиктограммы и курсоры.
INVOKE LoadIcon, NULL, IDI_APPLICATION
mov MainWin.hIcon, eax
INVOKE LoadCursor,NULL,IDC_ARROW
mov MainWin.hCursor, eax
; Зарегистрировать класс окна.
INVOKE RegisterClass, ADDR MainWin
.IF eax == 0
call ErrorHandler
jmp Exit_Program
.ENDIF
; Создать основное окно приложения.
INVOKE CreateWindowEx,0,ADDR className,
ADDR WindowName,MAIN_WINDOW_STYLE,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,NULL,NULL,hInstance,NULL
; Если окно не создано, то отобразить сообщение об ошибке и выйти.
.IF eax == 0
call ErrorHandler
jmp Exit_Program
.ENDIF
; Сохранить дескриптор окна, показать и отобразить окно.
mov hMainWnd,eax
INVOKE ShowWindow,hMainWnd, SW_SHOW
INVOKE UpdateWindow, hMainWnd
; Отобразить сообщение об ошибке.
INVOKE MessageBox,hMainWnd,ADDR GreetText,ADDR GreetTitle,MB_OK
; Начать циклическую обработку сообщений.
Message_Loop:
; Получить следующее сообщение из очереди.
INVOKE GetMessage, ADDR msg, NULL,NULL,NULL
; Выйти, если нет сообщений.
.IF eax == 0
jmp Exit_Program
.ENDIF
; Передать сообщение в программу WinProc.
INVOKE DispatchMessage, ADDR msg
jmp Message_Loop
Exit_Program:
INVOKE ExitProcess,0
WinMain ENDP
WinProc PROC,
hWnd:DWORD, localMsg:DWORD, wParam:DWORD, lParam:DWORD
Стр. 252
отсутствует. 253
ErrorHandler PROC
; Отобразить нужное сообщение об ошибке.
.data
pErrorMsg DWORD ? ; Указатель на сообщение об ошибке.
messageID DWORD ?
.code
INVOKE GetLastError ; Возвратить идентификатор сообщения в EAX
mov messageID, eax
; Получить строку сообщения.
INVOKE FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER + \
FORMAT_MESSAGE_FROM_SYSTEM,NULL,messageID,NULL,
ADDR pErrorMsg,NULL,NULL
; Отобразить сообщение об ошибке.
INVOKE MessageBox,NULL, pErrorMsg, ADDR ErrorTitle,
MB_ICONERROR+MB_OK
; Освободить память.
INVOKE LocalFree, pErrorMsg
ret
ErrorHandler ENDP
END WinMain
Запуск программы
Когда программа запускается, то появляется следующее окно сообщений (рис. 10.2).
Стр. 253
При щелчке пользователя на кнопке OK первое окно сообщений закрывается и ото&
бражается окно сообщений, представленное на рис. 10.3.
Если щелкнуть левой кнопкой мыши на поле окна, то появится окно сообщений, ко&
торое показано на рис. 10.5.
При закрытии основного окна Windows появляется следующее окно сообщений
(рис. 10.6).
После щелчка пользователя на кнопке OK программа для этого окна завершается.
Рис. 10.5. Окно сообщений, появляющееся по% Рис. 10.6. Окно сообщений, кото%
сле щелчка мыши на поле основного окна рое появляется при закрытии
основного окна
Стр. 254
отсутствует. 255
Согласование вызовов
В операционной системе MS&DOS вызываемая процедура могла находиться либо в том
же сегменте, что и команда вызова, тогда вызов назывался близким или внутрисегмент&
ным, либо в другом сегменте, тогда вызов назывался дальним или межсегментным. Раз&
ница заключалась в том, что адрес в первом случае формировался из двух байт, а во вто&
ром &&&& из четырех байт. Соответственно, возврат из процедуры мог быть либо близким,
т.е. адрес возврата формировался на основе двух байт, взятых из стека, либо дальним,
когда адрес формировался на основе четырех байт, взятых опять же из стека. Ясно, что
вызов и возврат должны быть согласованы друг с другом. В рамках единой программы
это, как правило, не вызывало больших проблем. Но вот когда необходимо было под&
ключить библиотеку или объектный модуль, то могли возникнуть трудности. Если в объ&
ектном модуле возврат осуществлялся ближний, необходимо было компоновать объект&
ные модули так, чтобы сегмент, где находится процедура, был объединен с сегментом,
откуда осуществляется вызов. Вызов в этом случае, разумеется, должен быть ближним.
Если же возврат из процедуры осуществлялся как дальний, то и вызов этой процедуры
должен быть дальним. Проблема согласования вызовов и возвратов усугублялась еще
и тем, что ошибки обнаруживались не при компоновке, а при исполнении программы.
С этим были связаны и так называемые модели памяти в языке C, что также было голов&
ной болью многих начинающих программистов. Сегментация памяти приводила в C еще
к одной проблеме &&&& проблеме указателей. В языке Pascal пошли по другому пути. Там
приняли, что в программе должен существовать один сегмент данных и несколько сег&
ментов кода. Если же одного сегмента для хранения данных не хватало, то предлагалось
использовать динамическую память. При переходе к Windows все программисты получи&
ли замечательный подарок в виде плоской модели памяти. Теперь все вызовы являются
ближними, т.е. осуществляются в пределах одного огромного сегмента. Тем самым была
снята проблема согласования вызовов.
Согласование имен
При программировании для Windows на согласование вызовов уже можно не обра&
щать внимания, но согласование имен со временем только усложнилось. Как уже упоми&
налось ранее, транслятор MASM добавляет в конце имени @n, где n &&&& количество пере&
даваемых в стек параметров. То же делает и компилятор Visual C++. Таким образом,
трудности возникают уже при согласовании двух ассемблерных модулей. В этом смысле
TASM является более гибким компилятором, так как при желании к любому имени мож&
но добавить @n, тем самым согласовав имена.
Другая проблема &&&& подчеркивание перед именем. Транслятор MASM генерирует под&
черкивание автоматически, если в начале программы устанавливается тип вызова stdcall
(Standard Call &&&& стандартный вызов). Транслятор TASM этого не делает, следовательно,
Стр. 255
при необходимости это нужно делать непосредственно в тексте программы. Между фир&
мами Borland и Microsoft здесь полное несоответствие.
Еще одна проблема &&&& согласование заглавных и прописных букв. При трансляции
с помощью TASM используется ключ /ml как раз для того, чтобы различать прописные
и заглавные буквы. Транслятор MASM делает это автоматически. Как известно, и в стан&
дарте языка C с самого начала предполагалось различие между заглавными и прописны&
ми буквами. В языке Pascal прописные и заглавные буквы не различаются.
В этом есть своя логика: Pascal и Delphi не создают стандартных объектных модулей,
зато могут подключать их. При создании же динамических библиотек имя помещается
в библиотеку так, как оно было указано в заголовке процедуры.
Наконец, последняя проблема, связанная с согласованием имен, &&&& это уточняющие
имена в C++. Дело в том, что в языке C++ возможна так называемая перегрузка. Это
значит, что одно и то же имя может относиться к разным функциям. В тексте программы
эти функции различаются по количеству и типу параметров, а также по типу возвращае&
мого значения. Поэтому компилятор C++ автоматически делает в конце имени добавку &&&&
так, чтобы разные по смыслу функции различались при компоновке. Разумеется, фирмы
Borland и Microsoft и тут не пожелали согласовать свои позиции и делают в конце имени
совершенно разные добавки. (Обойти эту проблему не так сложно, нужно использовать
модификатор extern "С".)
Согласование параметров
В табл. 10.1 представлены основные соглашения по передаче параметров в процедуру.
Ранее во всех ассемблерных программах указывался тип передачи параметров как stdcall.
Однако, по сути, это никак и нигде не использовалось &&&& передача и извлечение пара&
метров делалась явно, без помощи транслятора. Когда мы имеем дело с языками высо&
кого уровня, необходимо четко представлять себе, как работают те или иные соглашения.
Необходимо отметить еще один важный момент &&&& тип возвращаемых функцией дан&
ных. С точки зрения ассемблера здесь все предельно просто: в регистре ЕАХ возвращается
значение, которое может быть либо числом, либо указателем на некую переменную или
структуру. Если возвращаемое число имеет тип WORD, то оно передается в младшем слове
регистра ЕАХ. Однако имея дело с языками высокого уровня, следует очень аккуратно
относиться к преобразованию типов.
Стр. 256
отсутствует. 257
{ TForm1 }
procedure TForm1.Display(Z: Integer);
begin
label1.Caption := 'Произведение = ' + IntToStr(Z);
end;
Не надо хорошо знать Delphi, чтобы увидеть, что здесь объявлены и описаны три
процедуры:
Display &&&& отображение целочисленной переменной;
LongMul &&&& умножение двух целых чисел;
Button1Click &&&& событие нажатия клавиши для отображения числа.
Стр. 257
При этом тело процедуры LongMul написано на ассемблере. Все отлично работает
и на экране можно получить окно с результатом, как показано на рис. 10.7.
Большое удобство заключается в том, что можно использовать все возможности Delphi
по интегрированной отладке программы, т.е. можно сделать останов, например, на ко&
манде MOV из процедуры LongMul, и пройти по шагам весь цикл выполнения програм&
мы, отслеживая состояние всех регистров, флагов и памяти, как показано на рис. 10.8.
Для более полного представления о том, что делает компилятор с кодом для процеду&
ры LongMul, рассмотрим последовательно весь процесс.
Во&первых, компилятор оптимизирует данный код. Нет никакого кода для копирова&
ния значений параметров в локальные переменные. Это может произойти со строковыми
и другими параметрами, размер которых отличается от 1, 2 или 4 байт. В нашем случае
параметры рассматриваются в качестве переменных.
Пока функция не возвращает строку, вариант или ссылку на интерфейс, компилятор
не создает результирующую переменную и ссылка на переменную @Result вызовет
ошибку. Для строк, вариантов или ссылок на интерфейс вызывающая программа ис&
пользует указатель @Result.
Стр. 258
отсутствует. 259
Резюме
В данной главе кратко рассмотрено использование графических примитивов Windows
и процесс написания графических приложений. В созданном программным способом
окне можно будет отображать сообщения и реагировать на события мыши. Сведения,
приведенные в главе, могут послужить только основой для понимания принципов по&
строения окон и ни в коей мере не рассчитаны на полное описание всех возможностей
создания графических приложений. Однако усвоив основы построения графических при&
ложений, можно легко понять и более развитые средства разработки оконных приложе&
ний для Windows. В главе также описано использование языка ассемблера совместно
с языками высокого уровня. Необходимо хорошо представлять эти возможности и при&
менять их при создании эффективных программ.
Контрольные вопросы
1. Опишите структуру POINT.
2. Для чего используется структура WNDCLASS?
3. Что означают поля IpfnWndProc, style и hlnstance в структуре WNDCLASS?
4. Напишите пример вызова функции MessageBox.
5. Назовите две константы для кнопок, которые используются при вызове функции
MessageBox.
6. Назовите по крайней мере три задачи, которые выполняются процедурой WinMain.
7. Опишите роль процедуры WinProc, используемой в приведенной программе.
8. Опишите роль процедуры ErrorHandler, используемой в приведенной программе.
9. Почему возникает необходимость использовать язык ассемблера при написании
программ на языке высокого уровня?
Стр. 259
Глава 11
Работа в DOS
В этой главе...
Функции DOS
Вызовы процедур
Использование моделей памяти
Адресация переменных
Команда JMP для 16&разрядного режима
Перенаправление ввода&вывода
Функции DOS
При написании программ для 16&разрядного режима можно использовать прерыва&
ние INT 21h, предназначенное для вызова функций DOS. При этом может вызваться
около 90 различных функций. Номер функции предварительно помещается в регистр AH.
Функции разделяются на две группы: ввода и вывода. Каждая функция использует вход&
ные параметры, передающиеся через регистры, которые должны быть инициализирова&
ны перед вызовом прерывания INT 21h. Ниже приведены короткие примеры кодов, вы&
зывающих функции.
Функции вывода
02h (вывод символа). Функция посылает символ на стандартное устройство вывода.
В регистр DL помещается выводимый символ. Регистр AL модифицируется системой DOS.
MOV AH,2 ; Выбор функции 2 DOS.
MOV DL,'*’ ; Cимвол для отображения помещается в DL.
INT 21h ; Вызов DOS для выполнения.
Стр. 260
отсутствует. 261
O5h (вывод на принтер). Функция посылает один символ на принтер. В регистр DL по&
мещается выводимый символ. DOS ожидает, когда принтер будет готов принять символ.
Можно прервать ожидание нажатием комбинации клавиш <Ctrl+Break>. Выходным уст&
ройством по умолчанию является порт принтера LPT1. Для немедленной печати необхо&
димо послать символ конца строки (0Dh) или страницы (0Ch), так как многие принтеры
собирают символы для печати во внешнем буфере и не производят печать, пока буфер не
заполнится или не встретятся символы конца строки или страницы. Следующие коман&
ды выведут на принтер знак доллара ($).
MOV AH,5 ; Выбор функции печати.
MOV DL,'$' ; Символ для печати.
INT 21h ; Вызов DOS.
MOV DL,0Dh ; Сделать возврат каретки.
INT 21h ; Вызов DOS.
Нет необходимости снова помещать число 5 в регистр AH перед повторным вызовом
INT 21h. Это общее правило для функций по прерыванию INT 21h.
При использовании функций по прерыванию INT 21h, номер функции, помещенный в ре&
гистр AH, сохраняется до последующего изменения программным способом.
06h (прямой вывод). Функция может или читать из стандартного входного устройства,
или выводить на стандартное устройство. Ниже показана версия для отображения символа.
MOV AH,6 ; Выбор функции 6 DOS.
MOV DL,'&' ; DL — символ для вывода.
INT 21h ; Вызов DOS.
09h (вывод строки). Функция передает строку символов на стандартное устройство
вывода. В регистр DX помещается смещение строки. Строка должна оканчиваться симво&
лом доллара ($). Управляющие символы, такие как табуляция (9h) или возврат каретки,
распознаются системой DOS. В следующем примере выходная строка включает символы
возврата каретки (0Dh) и пропуска строки (0Ah).
MOV AH,9 ; Функция вывода строки.
MOV DX,OFFSET string ; Адрес строки.
INT 21h
.DATA
string DB 'Это строка байтов.',0Dh,0Ah,'$' ;
Эта функция DOS имеет недостаток: символ доллара ($) не может быть частью стро&
ки. А если символ доллара в конце объявления пропустить, то DOS будет производить
вывод до тех пор, пока не встретит нужный символ (24h). До этого может быть выведено
большое число символов.
Функции ввода
Ниже перечислено только несколько функций для стандартного ввода.
01h &&&& Фильтрующий ввод с дублированием на экране (эхо).
06h &&&& Прямой ввод без ожидания.
07h &&&& Прямой ввод с отключенным <Ctrl+Break>.
08h &&&& Прямой ввод с <Ctrl+Break>.
0Ah &&&& Буферизованный ввод.
Стр. 261
0Bh &&&& Получение статуса ввода.
0Ch &&&& Очистить входной буфер, вызвать функцию ввода.
3Fh &&&& Чтение из файла или устройства.
Буфер клавиатуры
Это кольцевой буфер для 15 символов, который DOS использует для хранения кодов
нажатых клавиш. Это сделано для того, чтобы исключить потерю символов при очень
быстром вводе, когда программа может не успеть отследить нажатие всех клавиш. DOS
запоминает эти нажатия, но может случиться и так, что буфер переполнится и произой&
дет потеря символов, о чем система сообщит звуковым сигналом. Однако в современных
быстрых компьютерах такая ситуация не происходит.
Функции DOS для ввода символов (функции 1, 6, 7 и 8) имеют большое количество
характеристик (табл. 11.1).
Стр. 262
отсутствует. 263
06h (прямой ввод без ожидания). Особенность использования функции 6 DOS состоит
в том, что она не ожидает поступления очередного символа и сама обращается к стан&
дартному входному буферу за следующим символом. Если буфер пуст, функция устанав&
ливает флаг ZF (ZF = 1). Комбинация <Ctrl+Break> неактивна и фильтрации управ&
ляющих символов нет.
Перед вызовом прерывания INT 21h необходимо поместить в регистр DL значение 0FFh.
Если обнаружен символ во входном буфере, он перемещается в регистр AL и флаг ZF
сбрасывается (ZF = 0). Если символ не обнаружен, то ZF = 1.
MOV AH,6 ; Проверка входного буфера.
MOV DL,0FFh
INT 21h
Иногда в программе необходимо освободить буфер клавиатуры для команд пользова&
теля на то время, пока компьютер не готов принять и обработать их. Следующая про&
грамма комбинирует функцию 6 с циклом для очистки буфера клавиатуры.
clear_keyboard PROC
PUSH AX
PUSH DX
L1:
MOV AH,6 ; Проверка входного буфера.
MOV DL,0FFh
INT 21h
JNZ L1 ; Переход к метке L1, если ZF=0.
POP DX
POP AX
RET ; Возврат в вызывающую программу.
clear_keyboard ENDP
Команда JNZ выполняет переход на метку, если флаг нуля (ZF) сброшен.
07h (прямой ввод с неактивной <Ctrl+Break>). Функция ожидает нефильтрованный
символ со стандартного входа без эхо&символа. Комбинация <Ctrl+Break> неактивна.
Регистр AL содержит вводимый символ.
MOV AH,7 ; Функция ввода.
INT 21h ; Вызов DOS.
MOV CHAR,AL ; Сохранение символа.
08h (прямой ввод с активной <Ctrl+Break>). Функция ожидает нефильтрованный сим&
вол с консоли без эхо&символа. Комбинация <Ctrl+Break> активна. Регистр AL содержит
вводимый символ.
MOV AH,8 ; Функция ввода.
INT 21h ; Вызов DOS.
MOV CHAR,AL ; Сохранение символа.
0Ah (буферизованный ввод). Функция 0Ah считывает строку символов размером до 255
символов со стандартного входа и сохраняет ее в буфере. Клавиша <Backspace> может
использоваться для стирания символов и возврата курсора. Пользователь может прервать
ввод нажатием клавиши <Enter>. DOS не пропускает нажатие клавиш, не создающих код
ASCII, таких как перемещение курсора или <PgDn>, которые не сохраняются в буфере.
Комбинация <Ctrl+Break> активна и все вводимые символы отображаются на экране.
Байт со смещением 0 содержит максимальное число символов, которое можно ввести,
включая клавишу <Enter>. Допустим, это число равняется 5, тогда DOS позволит ввести
только четыре символа плюс клавишу <Enter>. В байте со смещением 1 сохраняется ко&
личество введенных символов. Сами символы будут размещены в буфере со смещением 2.
Стр. 263
В следующем примере максимальное значение maxКeys равно 32, количество введенных
символов charsInput заполняется DOS после прерывания INT 21h, и буфер buffer
содержит символы, введенные пользователем.
7 6 5 4 3 2 1 0
OBh (получение статуса ввода). Функция 0Bh проверяет стандартный буфер ввода на
наличие в нем символов. Если есть символ, то регистр AL = 0FFh; в противном слу
чае AL = 0.
MOV AH,0Bh ; Проверка статуса ввода.
INT 21h ; Вызов DOS.
0Ch (очистка входного буфера, вызов функции ввода). Функция 0Ch очищает буфер кла
виатуры и вызывает функцию из прерывания INT 21h для ввода с консоли. Эту функцию
Стр. 264
отсутствует. 265
можно использовать для защиты от ввода до того, как на экране появится приглашение.
В регистр AL помещается номер функции DOS (1, 6, 7 или 8). Входной символ будет раз&
мещаться в регистре AL. В следующем примере происходит очистка буфера и вызов
функции 1 для ввода символа.
MOV AH,0Ch ; Очистка буфера.
MOV AL,1 ; Вызов функции 1 после окончания.
INT 21h ; Вызов DOS.
MOV CHAR,AL ; Сохранение символа.
3Fh (чтение из файла или входного устройства). Функция может использоваться для
чтения строки символов из файла или входного устройства. Регистр DX содержит смеще&
ние входного буфера, размер которого больше или равен значению величины, помещен&
ной в регистр CX. Регистр BX определяет стандартное входное устройство или индекс
файла. Для стандартной клавиатуры значение регистра BX=0. В регистре CX находится
значение максимального количества байтов для чтения.
Функция 3Fh может считывать символы со стандартного входа и прекращает работу
при нажатии клавиши <Enter>. Необходимо установить BX в 0 (ввод с клавиатуры), в CX
поместить максимальное число считываемых байтов и в DX установить смещение буфера.
Функция возвращает число реально считанных символов в регистр AX.
.DATA
inputBuffer DB 127 dup(0)
.CODE
MOV AH,3Fh ; Чтение из файла/устройства.
MOV BX,0 ; Устройство (при значении 0 - клавиатура).
MOV CX,127 ; Максимально можно считать 127 байт.
MOV DX,OFFSET inputBuffer
INT 21h ; В AX будет число считанных символов.
Клавиши <Backspace> и <←> можно применять для редактирования ввода, как и при
использовании функции 0Ah. При нажатии клавиши <Enter> DOS добавляет символы
возврата каретки (0Dh) и пропуска строки (0Ah) во входной буфер. Кроме того, DOS уве&
личивает на 2 счетчик символов в регистре AX. Количество считываемых символов, раз&
мещенное в CX, должно также включать символы возврата каретки и пропуска строки
(CR/LF), которые DOS добавляет к строке.
Стр. 265
Функции даты/времени
2Ah (получить дату). Функция возвращает текущую системную дату, помещая год в ре&
гистр CX, а номер месяца &&&& в регистр DH. Номер дня помещается в регистр DL, а день не&
дели &&&& в регистр AL. Для дней недели используются числовые обозначения: 0 &&&& для
воскресенья, 1 &&&& для понедельника и т.д. В следующих командах вызывается функция
и сохраняются значения.
MOV AH,2Ah
INT 21h
MOV year,CX
MOV month,DH
MOV day,DL
MOV dayOfWeek,AL
2Bh (установить дату). Функция устанавливает текущую системную дату, используя
те же регистры, что и в функции 2Ah (получить дату). Функция возвращает значение 0
в регистр AL, если изменение прошло успешно, или значение 0FFh, если данные не были
изменены.
MOV AH,2Bh
MOV CX,year
MOV DH,month
MOV DL,day
INT 21h
CMP AL,0
JNE badDate
2Ch (получить время). Функция возвращает текущее системное время, помещая часы
в регистр CH, минуты &&&& в регистр CL, секунды &&&& в DH и сотые доли секунд &&&& в DL. По&
следнее значение обычно некорректное. Рассмотрим пример вызова функции.
MOV AH,2Ch
INT 21h
MOV hours,CH
MOV minutes,CL
MOV seconds,DH
2Dh (установить время). Функция устанавливает текущее системное время, используя
те же регистры, что и функция 2Ch (получить время). Функция возвращает значение 0,
если изменение прошло успешно, и значение 0FFh, если возникли ошибки.
MOV AH,2Dh
MOV CH,hours
MOV CL,minutes
MOV DH,seconds
INT 21h
CMP AL,0
JNE badTime
Управляющие клавиши
Ранее уже говорилось о том, что когда пользователь нажимает управляющие клавиши,
такие как F1 или PgUp, BIOS сохраняет два байта в буфере клавиатуры. Если использует&
ся функция 8 прерывания INT 21h для чтения управляющих клавиш, то прерывание
должно вызываться дважды. В листинге 11.2 процедура GetKey читает как коды ASCII,
так и управляющие клавиши.
Стр. 266
отсутствует. 267
Вызовы процедур
Ранее подробно было рассмотрено использование процедур, но нигде не упоминалось
о ближних и дальних вызовах. При программировании под Windows такой проблемы не
возникает, так как все вызовы считаются ближними, в пределах одного большого сегмен&
та памяти, при этом используется 32&разрядная адресация. При работе с DOS можно ис&
пользовать ближние вызовы в пределах одного сегмента памяти размером 64 Кб, для чего
можно применить один 16&разрядный регистр, или дальние вызовы с заходом в другой
сегмент, что требует использования двух 16&разрядных регистров. Это связано с ограни&
чениями DOS на размер сегмента.
Поэтому команда вызова процедуры CALL помещает в стек адрес возврата, занимающий
16 бит, если процедура определена с атрибутом NEAR, и 32 бита, если она определена с атрибу&
том FAR. Процедуры с атрибутом NEAR могут быть вызваны только из того сегмента, в кото&
ром они находятся; процедуры с атрибутом FAR могут быть вызваны и из другого сегмента.
Если процедура имеет атрибут FAR, то команда CALL загружает также адрес сегмента
процедуры в регистр CS.
Если процедура содержит атрибут NEAR, то команда CALL помещает смещение адреса
следующей команды в стек. Если процедура включает атрибут FAR, то команда CALL по&
мещает в стек содержимое регистра CS, а затем смещение адреса.
Если процедура имеет атрибут NEAR (находится в том же сегменте, что и команда CALL),
то команда RET извлекает из стека одно слово и загружает его в указатель команд IP. Ес&
ли процедура имеет атрибут FAR (находится в другом сегменте), то команда RET извлека&
ет из стека два слова: сначала смещение адреса для загрузки в указатель команд IP, а за&
тем слово для загрузки в регистр CS.
Можно вызвать процедуру с атрибутом NEAR через регистр, например:
CALL BX
В данном случае регистр ВХ содержит смещение адреса процедуры относительно ре&
гистра сегмента CS. При исполнении этой команды микропроцессор копирует содержи&
мое регистра ВХ в указатель команд IP, затем передает управление команде, адресуемой
парой регистров CS:IP. Если, например, регистр ВХ содержит значение 1АВН, то микро&
процессор извлечет следующую команду из ячейки 1АВН, находящейся в сегменте команд.
Стр. 267
Процедуру с атрибутом NEAR можно вызвать косвенно, используя переменную разме&
ром в слово, например:
CALL WORD PTR [BX]
CALL WORD PTR [BX][SI]
CALL WORD PTR variable_name
CALL WORD PTR variable_name[ВХ]
CALL mem_word
CALL WORD PTR ES:[BX][SI]
Последняя команда CALL получает адрес процедуры из ячейки дополнительного сег&
мента (использовано переопределение сегмента ES:); остальные команды извлекают ад&
реса процедур из ячеек сегмента данных.
Ниже показан пример ближнего вызова процедуры. Перед переходом к процедуре
команда CALL сохраняет текущее значение указателя команд (IP) в стеке. Затем в IP по&
мещается смещение подпрограммы, что заставит процессор немедленно перейти к вы&
полнению первой команды подпрограммы.
Стр. 268
отсутствует. 269
Возникает вопрос: зачем использовать дальние вызовы, если ближние вызовы проще
и быстрее? Дальние вызовы необходимы в следующих случаях.
Когда программа на языке ассемблера вызывается из программы на языке высо&
кого уровня. В этом случае вызывающая программа будет диктовать тип вызова.
Например, модель памяти large в программе на языке С требует дальнего вызова
подпрограмм.
Когда производится вызов из библиотеки подпрограмм, то библиотека может быть
разработана для использования дальних вызовов.
Когда прикладная программа настолько большая, что требуется более 64 Кбайт
памяти для команд, а также в случае, если используются модели памяти medium
или large (тогда может быть несколько сегментов кодов, поэтому требуется ис&
пользование дальних вызовов).
sub_FAR PROC
MOV DX,OFFSET msg2
CALL NEAR PTR Writestring ; Загрузчик выдаст ошибку.
RET
sub_FAR ENDP
END main
Стр. 269
изменять регистр CS, загружая в него значение из стека. Если программа выполняет даль&
ний вызов по умолчанию, то при использовании этих библиотек необходимо делать вызовы
с помощью оператора NEAR PTR для команды CALL. Несмотря на то что загрузчик будет
обнаруживать ошибку переполнения привязки адреса, работа будет происходить корректно.
Программы, транслируемые с использованием моделей памяти medium или large,
будут иметь отдельные сегменты кодов для каждого модуля. Например, программа в при&
веденном выше листинге оттранслирована с моделью памяти large. В таблице распре&
деления памяти (табл. 11.2) будут указаны имена всех сегментов. Сегменты кодов будут
иметь имена с окончанием _TEXT.
Можно дополнительно создать сегмент кодов, добавив его имя к директиве .CODE.
Будет создан новый сегмент кодов, отличающийся от сегмента по умолчанию. В следую&
щей директиве объявляется сегмент, названный MYCODE.
.CODE MYCODE
В таблице распределения памяти (табл. 11.3) появится сегмент MYCODE.
Адресация переменных
При программировании для DOS регистры CS, DS, SS, ES используются как указатели на
соответствующий сегмент, т.е. содержат адрес сегмента, в отличии от программ для Windows,
где эти регистры указывают на соответствующие ячейки таблицы дескрипторов.
Смещение переменной &&&& это расстояние от начала сегмента до места размещения дан&
ных. Имена переменных автоматически связываются со смещением. Например, если
Стр. 270
отсутствует. 271
объявить массив из четырех символов, то его имя str1 определяет смещение первого
символа (A).
.DATA
str1 DB "ABCD"
Если, условно говоря, смещение первого символа равно 0, то смещение второго сим&
вола будет равно 1, следующего &&&& 2 и т.д. Смещение имени str1 равно 0, как и смеще&
ние первого символа.
При использовании команд условного перехода необходимо учитывать расстояние, на ко&
торое совершается переход. Адрес перехода должен быть на расстоянии –128 или +127 байт
от команды перехода, что не всегда удобно, если логическая структура большая. Для полу&
чения больших расстояний переходов необходимо использовать специальные конструкции.
Стр. 271
Цикл, основанный на операторе JMP, никогда не остановится, как показано в приве&
денном примере. Но использование прерывания INT 21h позволяет остановить про&
грамму в отладчике с помощью нажатия клавиш <Ctrl+Break>.
start: MOV AH,2 ; Вывести символ на экран.
MOV DL,'A'
INT 21h
JMP start
Проследить, как транслируется команда JMP, поможет следующий фрагмент из лис&
тинга, сгенерированного ассемблером. В начале каждой строки указано шестнадцате&
ричное смещение каждой команды с последующим объектным кодом, создаваемым ас&
семблером.
Перенаправление ввода*вывода
Обычно для ввода и вывода данных используются стандартные входные и выходные
устройства. Эти устройства можно назвать одним словом &&&& консоль, при этом для ввода
данных используется клавиатура, а вывод производится на дисплей.
Из командной строки DOS можно переопределить стандартные устройства так, что
ввод данных будет производиться из файла или аппаратного порта (вместо клавиатуры),
а вывод может быть перенаправлен в файл или любой аппаратный порт вместо дисплея.
Например, программа с именем prog1.exe может выполнять ввод&вывод так, как пока&
зано в табл. 11.4.
Стр. 272
отсутствует. 273
Стр. 273
Глава 12
Справочный раздел
В этой главе...
Флаги
В описании каждой команды используется диаграмма для флагов, которые применяют&
ся при выполнении данной команды в процессоре. Каждый флаг определяется одной буквой:
1 Установить флаг
0 Сбросить флаг
? Возможна установка флага в неопределенное состояние
(пусто) Состояние флага не изменяется
* Состояние флага изменяется в соответствии с правилами, определенными для этого флага
Стр. 274
отсутствует. 275
O D I S Z A P C
? ? ? * ? *
Система команд
AAA
O D I S Z A P C
? ? ? * ? *
Стр. 275
ASCII+коррекция после сложения (ASCII Adjust after Additional). Корректирует результат
сложения двух чисел в формате ASCII в регистре AL. Если значение в AL больше 9, то
старшая цифра результата помещается в регистр AH и устанавливаются флаги переноса
и дополнительного переноса.
Формат команды: AAA
AAD
O D I S Z A P C
? * * ? * ?
Коррекция ASCII перед делением (ASCII Adjust before Division). Преобразовывает неупа&
кованные числа в формате BCD в регистрах AH и AL в двоичные значения перед выпол&
нением команды DIV.
Формат команды: AAD
AAM
O D I S Z A P C
? * * ? * ?
AAS
O D I S Z A P C
? ? ? * ? *
ADC
O D I S Z A P C
* * * * * *
Стр. 276
отсутствует. 277
ADD
O D I S Z A P C
* * * * * *
AND
O D I S Z A P C
* * * ? * 0
BOUND
O D I S Z A P C
Проверка границ массива (check array BOUND). (80282) Проверяет, находится ли ин&
декс массива в указанном диапазоне. Для процессоров 80282 операндом&получателем
может быть любой 16&разрядный регистр, содержащий проверяемый индекс. Операндом&
отправителем должен быть 32&разрядный операнд памяти, в котором старшее и младшее
слово содержит верхнюю и нижнюю границу диапазона. Для процессора 80383 операн&
дом&получателем должен быть 32&разрядный регистр, а операндом&отправителем должен
быть 64&разрядный операнд памяти.
Формат команды:
BOUND reg16, mem32 BOUND reg32, mem64
BSF, BSR
O D I S Z A P C
*
Сканирование битов (Bit Scan). (80383) Сканирует операнд для нахождения первого
единичного бита. Если бит найден, то флаг нуля сбрасывается и в операнде&получателе
Стр. 277
устанавливается номер позиции бита (индекс). Если единичный бит не найден, то ZF=1.
Команда BSF производит сканирование от нулевого бита к старшим битам, а команда BSR
сканирует от старшего бита к младшим.
Формат команды (для BSF и BSR):
BSF reg16, reg16 BSF reg32, reg32
BSF reg15, mem16 BSF reg32, mem32
BSWAP
O D I S Z A P C
Проверка битов (Bit Tests). (80386) Копирует определенный бит во флаг переноса. Опе&
ранд&получатель содержит значение, в котором производится проверка, а операнд&
источник содержит номер позиции для проверки. Команда BT только копирует бит во флаг
переноса. Команда BTC копирует бит и инвертирует его в операнде&получателе. Команда BTR
копирует бит и устанавливает его в 0, а команда BTS копирует бит и устанавливает его в 1.
Формат команды (для BT, BTC, BTR и BTS):
BT reg16, immed8 BT mem16, immed8
BT reg16, reg16 BT mem16, reg16
CALL
O D I S Z A P C
Вызов процедуры (CALL procedure). Помещает в стек адрес следующей команды и со&
вершает переход по указанному адресу. Если процедура является ближней (в том же сег&
менте), то в стек помещается только смещение следующей команды, в противном случае
в стек помещается и смещение, и сегмент.
Формат команды:
CALL nearlabe CALL mem16
CALL farlabel CALL mem32
CALL reg
Стр. 278
отсутствует. 279
CBW
O D I S Z A P C
CDQ
O D I S Z A P C
CLC
O D I S Z A P C
0
Сбросить флаг переноса (CLear Carry flag). Установить флаг переноса в нуль.
Формат команды: CLC
CLD
O D I S Z A P C
0
Сбросить флаг направления (CLear Direction flag). Установить флаг направления в нуль.
Команды строковых примитивов автоматически инкрементируют регистры SI и DI.
Формат команды: CLD
CLI
O D I S Z A P C
0
Сбросить флаг прерывания (CLear Interrupt flag). Установить флаг прерывания в нуль.
Это вынуждает процессор не реагировать на аппаратные прерывания, пока не будет вы&
полнена команда STI.
Формат команды: CLI
CMC
O D I S Z A P C
*
Инвертировать флаг переноса (CoMplement Carry flag). Изменяет значение флага переноса.
Формат команды: CMC
Стр. 279
CMP
O D I S Z A P C
* * * * * *
CMPXCHG
O D I S Z A P C
* * * * * *
CWD
O D I S Z A P C
Стр. 280
отсутствует. 281
CWDE
O D I S Z A P C
DAA
O D I S Z A P C
? * * * * *
DAS
O D I S Z A P C
? * * * * *
DEC
O D I S Z A P C
* * * * *
DIV
O D I S Z A P C
? ? ? ? ? ?
Деление без знака (unsigned integer DIVide). Выполняет деление 8&, 16& или 32&разряд&
ных чисел без знака. Если делитель является 8&разрядным числом, делимое помещается
в регистр AX, частное &&&& в регистр AL, а остаток &&&& в регистр AH. Если делимое является
16&разрядным числом, делимое помещается в регистры DX:AX, частное &&&& в регистр AX,
и остаток &&&& в регистр DX. Если делитель является 32&разрядным числом, делимое поме&
щается в регистры EDX:EAX, частное &&&& в регистр EAX и остаток &&&& в регистр EDX.
Формат команды:
DIV reg DIV mem
Стр. 281
ENTER
O D I S Z A P C
? * * * * *
Установка кадра стека. (80286) Создает кадр стека для процедуры, которая принима&
ет параметры и использует локальные переменные стека. Первый операнд определяет
число байт для хранения локальных переменных. Второй операнд определяет уровень
вложения процедур (устанавливается в нуль для C, Basic и FORTRAN).
Формат команды: ENTER immed16, immed8
HLT
O D I S Z A P C
IDIV
O D I S Z A P C
? ? ? ? ? ?
Деление целых чисел со знаком (signed Integer DIVision). Выполняет деление целых чисел
со знаком в регистрах EDX:EAX, DX:AX или AX. Если делитель имеет 8 разрядов, делимое
помещается в регистр AX, частное &&&& в AL и остаток &&&& в AH. Если делитель имеет 16 раз&
рядов, делимое помещается в регистры DX:AX, частное &&&& в AX и остаток &&&& в DX. Если
делитель имеет 32 разряда, делимое помещается в регистры EDX:EAX, частное &&&& в EAX и
остаток &&&& в EDX. Обычно операции IDIV предшествуют команды CBW или CWD для пре&
образования делимого.
Формат команды:
IDIV reg IDIV mem
IMUL
O D I S Z A P C
* ? ? ? ? *
Умножение целых чисел со знаком (signed Integer MULtiply). Выполняет умножение це&
лых чисел со знаком в регистрах AL или AX. Если множитель имеет 8 разрядов, то второй
сомножитель помещается в регистр AL, а результат &&&& в регистр AX. Если множитель име&
ет 16 разрядов, то второй сомножитель помещается в регистр AX, а результат &&&& в регистр
DX:AX. Если множитель имеет 32 разряда, то второй сомножитель помещается в регистр EAX,
а результат &&&& в регистр EDX:EAX. Флаги переноса и переполнения устанавливаются в слу&
чаях, когда: 16&разрядный результат производит расширение знака в регистр AH, 32&раз&
рядный результат выполняет расширение знака в регистр DX, 64&разрядный результат
производит расширение знака в регистр EDX.
Стр. 282
отсутствует. 283
Формат команды:
IMUL reg/mem8 IMUL reg/mem16
IMUL reg/mem32
IMUL reg16,reg/mem16 IMUL reg16,imm8
IMUL reg32,reg/mem32 IMUL reg32,imm8
IMUL reg16,imm16 IMUL reg32,imm32
IMUL reg16,reg/mem16,imm8 IMUL reg16,reg/mem16,imm16
IMUL reg32,reg/mem32,imm8 IMUL reg32,reg/mem32,imm32
IN
O D I S Z A P C
Ввод из порта (INput from port). Вводит байт или слово из порта в регистр AL или AX. Опе&
рандом является адрес порта, выраженный или 8&разрядной константой, или 16&разряд&
ным адресом в регистре DX. Для процессора 80386 из порта можно ввести двойное слово.
Формат команды:
IN accum, imm IN accum, DX
INC
O D I S Z A P C
* * * * *
Ввод строк (INput from port to string). (80286) Вводит строку из порта по адресу, указан&
ному в регистрах ES:DI. Номер порта определяется в регистре DX. Для каждого вводи&
мого значения происходит коррекция, как и в команде LODSB при работе со строковыми
примитивами. С этой командой можно использовать префикс REP.
Формат команды:
INS dest, DX REP INSB dest, DX
REP INSW dest, DX REP INSD dest, DX
Стр. 283
INT
O D I S Z A P C
0
INTO
O D I S Z A P C
* *
IRET
O D I S Z A P C
* * * * * * * *
Jcondition
Переход, если условие выполнено (Jump CONDITIONal). Переход на метку, если установ&
лен флаг условия, определенный кодом операции. В процессорах более ранних, чем
80386, метка должна была находиться в диапазоне от &&128 до +127 байт от текущего по&
ложения. В процессорах 80386 и более поздних моделях диапазон составляет от &&32 768
до +32 767 байт. В табл. 11.2 приводится полный список мнемокодов для данной команды.
Формат команды: Jcondition label
Стр. 284
отсутствует. 285
JCXZ, JECXZ
O D I S Z A P C
Переход, если CX=0 (Jump if CX is Zero). Переход на ближнюю метку, если значение в ре&
гистре CX равно нулю. Ближняя метка должна быть в диапазоне от &&128 до +127 байт от те&
кущего адреса. Для процессора 80386 по команде JECXZ совершается переход, если ECX=0.
Формат команды:
JCXZ label JECXZ label
JMP
O D I S Z A P C
LAHF
O D I S Z A P C
Загрузка флагов в AH (Load AH from Flags). Копирует младшие 8 бит регистра флагов
в регистр AH. Флаги из старших битов не копируются (это флаги трассировки, прерыва&
ния, переполнения, направления и знака).
Формат команды: LAHF
Стр. 285
LDS, LES, LFS, LGS, LSS
O D I S Z A P C
Загрузка дальнего указателя (Load far pointer). Загружает операнд памяти (двойное слово)
в регистр сегмента и в регистр&получатель. В моделях процессоров, более младших чем
80386, команда LDS производит загрузку в регистр DS, а команда LES &&&& в регистр ES. Для
процессора 80386 команда LFS производит загрузку в регистр FS, команда LGS &&&& в GS
и команда LSS &&&& в регистр SS.
Формат команды (для LDS, LES, LFS, LGS, LSS): LDS reg, mem
LEA
O D I S Z A P C
Получение эффективного адреса (Load Effective Address). Рассчитывает и загружает 16& или
32&разрядный эффективный адрес операнда памяти. Действует подобно MOV OFFSET, за
исключением того, что только команда LEA получает адрес, рассчитанный во время вы&
полнения.
Формат команды: LEA reg,mem
LEAVE
O D I S Z A P C
Выход из процедуры (high+level procedure exit). Удаляет кадр стека процедуры, создан&
ный командой ENTER. Восстанавливает указатели (E)SP и (E)BP.
Формат команды: LEAVE
LOCK
O D I S Z A P C
Блокировка системной шины (LOCK the system bus). Блокирует системную шину на
время выполнения некоторых команд. Эта команда необходима, когда другой процессор
может модифицировать операнды памяти во время одновременной работы с ними ос&
новного процессора.
Формат команды: LOCK instruction
Загрузка строки в аккумулятор (LOaD accumulator from String). Загружает байт памяти
или слово памяти по адресу, указанному в регистрах DS:SI, а также в аккумулятор (AL, AX
или EAX). Если используется команда LODS, то операнд памяти должен быть определен.
LODSB загружает байт в AL, LODSW загружает слово в AX и LODSD (для процессора 80386)
Стр. 286
отсутствует. 287
LOOP, LOOPW
O D I S Z A P C
LOOP
Цикл (LOOP).(80386) Декрементирует регистр ECX и совершает переход на короткую
метку, если ECX больше нуля. Размер перехода не должен превышать зону от &&128 до
+127 байт от текущего положения.
Формат команды: LOOP shortlabel
LOOPE, LOOPZ
O D I S Z A P C
Цикл, если равно нулю (LOOP if Equal (Zero)). Декрементирует регистр (E)CX и совер&
шает переход на короткую метку, если (E)CX больше нуля и флаг нуля установлен. Раз&
мер перехода не должен превышать зону от &&128 до +127 байт от текущего положения.
Формат команды:
LOOPE shortlabel LOOPZ shortlabel
LOOPNE, LOOPNZ
O D I S Z A P C
Цикл, если не равно нулю (LOOP if Not Equal (Zero)). Декрементирует регистр (E)CX
и совершает переход на короткую метку, если (E)CX больше нуля и флаг нуля установлен.
Формат команды:
LOOPNE shortlabel LOOPNZ shortlabel
Стр. 287
MOV
O D I S Z A P C
Пересылка строк (MOVe String). Копирует байт или слово из памяти, адрес которой
находится в регистрах DS:(E)SI, в память с адресом в регистрах ES:(E)DI. Команда
MOVS требует определения обоих операндов, команда MOVSB копирует один байт, коман&
да MOVSW копирует слово, а команда MOVSD (для процессоров 80386) копирует двойное
слово. Регистры (E)SI и (E)DI инкрементируются или декрементируются в зависимо&
сти от состояния флага направления. Если DF=1, регистры (E)SI и (E)DI декременти&
руются, если DF=0, (E)SI и (E)DI инкрементируются.
Формат команды:
MOVS dest, source MOVSB
MOVS ES:dest, seqreg:source MOVSW
MOVSD
MOVSX
O D I S Z A P C
Пересылка с нулевым расширением (MOVe with Sign+eXtend). Копирует байт или слово
из операнда&отправителя в регистр&получатель и заполняет значением знакового бита
старшую половину регистра&получателя. Эта команда используется для преобразования
8& или 16&разрядных чисел в числа большей разрядности.
Формат команды:
MOVZX reg32, reg16 MOV reg32, mem16
MOVZX reg16, reg8 MOV reg16, mem8
MOVZX
O D I S Z A P C
Стр. 288
отсутствует. 289
Пересылка с нулевым расширением (Move with zero+extend). Копирует байт или слово из
операнда&отправителя в регистр&получатель и заполняет нулями старшую половину ре&
гистра&получателя. Эта команда используется для преобразования 8& или 16&разрядных
чисел в числа большей разрядности.
Формат команды:
MOVZX reg32, reg16 MOV reg32, mem16
MOVZX reg16, reg8 MOV reg16, mem8
MUL
O D I S Z A P C
* ? ? ? ? *
Умножение целых чисел без знака (unsigned integer MULtiply). Перемножает значение
в регистре AL, AX или EAX со значением исходного операнда. Если исходное значение имеет
8 разрядов, то оно перемножается со значением в регистре AL и результат сохраняется
в регистре AX. Если исходное значение имеет 16 разрядов, то оно перемножается со зна&
чением в регистре AX, а результат сохраняется в регистрах DX:AX. Если исходное значе&
ние имеет 32 разряда, то оно перемножается со значением в регистре EAX и результат со&
храняется в регистре EDX:EAX.
Формат команды: MUL reg MUL mem
NEG
O D I S Z A P C
* * * * * *
NOP
O D I S Z A P C
Нет операции (NO oPeration). Эта команда не производит никаких операций. Ее ис&
пользуют во временных циклах или для выравнивания команд по границе слова.
Формат команды: NOP
NOT
O D I S Z A P C
Стр. 289
OR
O D I S Z A P C
0 * * ? * 0
OUT
O D I S Z A P C
Вывод в порт (OUTput to port). Для более ранних моделей процессоров, чем 80386,
с помощью этой команды выполнялся вывод байта или слова в порт из аккумулятора.
Адрес порта может указываться с помощью константы с диапазоном от 0 до FFh, или на&
ходиться в регистре DX с диапазоном от 0 до FFFFh. В более поздних моделях можно
производить вывод двойного слова.
Формат команды:
OUT imm8, accum OUT DX, accum
Вывод строки в порт (OUTput String to port). Выводит в порт строку, адрес которой ука&
зан в регистрах ES:(E)DI. Номер порта определяется в регистре DX. Для каждого значе&
ния регистр (E)DI корректируется таким же образом, как это делается в команде LODSB
и для команд строковых примитивов. С этой командой может использоваться префикс REP.
Формат команды:
OUTS dest, DX REP OUTSB dest, DX
REP OUTSW dest, DX REP OUTSD dest, DX
POP
O D I S Z A P C
Извлечение операнда из стека (POP from stack). Копирует слово или двойное слово из
стека в операнд&получатель. Добавляет 2 или 4 к значению указателя стека (регистр (E)SP).
Стр. 290
отсутствует. 291
Формат команды:
POP reg16/reg32 POP segreg
POP mem16/mem32
POPA, POPAD
O D I S Z A P C
Извлечение из стека регистров общего назначения (POP All). Извлекает 16 байт из стека
в восемь регистров общего назначения в следующем порядке: DI, SI, BP, SP, BX, DX, CX, AX.
Значение указателя стека не восстанавливается. Команда POPA производит извлечение в
16&разрядные регистры, а POPAD (для процессоров 80386) &&&& в 32&разрядные регистры.
Формат команды: POPA POPAD
POPF, POPFD
O D I S Z A P C
* * * * * * * *
Извлечь флаги из стека (POP Flags from stack). Команда POPF извлекает из стека значе&
ния в 16&разрядный регистр флагов, а команда POPFD для процессора 80386 производит
извлечение в 32&разрядный регистр флагов.
Формат команды: POPF POPFD
PUSH
O D I S Z A P C
PUSHA, PUSHAD
O D I S Z A P C
Поместить в стек все (PUSH All). Команда PUSHA (для процессора 80186) помещает в
стек 16&разрядные регистры общего назначения в следующем порядке: AX, CX, DX, BX,
SP, BP, SI и DI. А команда PUSHAD (для процессоров 80386) помещает регистры EAX,
ECX, EDX, EBX, ESP, EBP, ESI и EDI.
Формат команды: PUSHA PUSHAD
Стр. 291
PUSHF, PUSHFD
O D I S Z A P C
Помещение регистра флагов в стек (PUSH Flags). Команда PUSHF помещает 16&разряд&
ный регистр флагов в стек. Команда PUSHFD для процессоров 80386 помещает 32&раз&
рядный регистр флагов в стек.
Формат команды: PUSHF PUSHFD
PUSHW, PUSHD
O D I S Z A P C
RCL
O D I S Z A P C
* *
Циклический сдвиг операнда влево через флаг переноса (Rotate Carry Left). Производит
циклический сдвиг операнда&получателя влево. Количество циклов задается в операнде&
отправителе. Флаг переноса копируется в младший бит, а старший бит копируется во
флаг переноса. Для процессоров 8086/8088 операнд imm8 может быть установлен в 1.
Формат команды:
RCL reg, imm8 RCL mem, imm8
RCL reg, CL RCL mem, CL
RCR
O D I S Z A P C
* *
Циклический сдвиг операнда вправо через флаг переноса (Rotate Carry Right). Производит
циклический сдвиг операнда&получателя вправо. Количество циклов задается в операн&
де&отправителе. Флаг переноса копируется в младший бит, а старший бит копируется во
флаг переноса. Для процессоров 8086/8088 операнд imm8 может быть установлен в 1.
Формат команды:
RCR reg, imm8 RCR mem, imm8
RCR reg, CL RCR mem, CL
Стр. 292
отсутствует. 293
REP
O D I S Z A P C
Повторить команду для строкового примитива (Repeat string). Повторяет команду для
строкового примитива, используя регистр (E)CX в качестве счетчика. Значение (E)CX
декрементируется при каждом повторении команды, пока не станет равным нулю.
Формат команды (с MOVS): REP MOVS получатель, отправитель
REPcondition
O D I S Z A P C
*
ROL
O D I S Z A P C
* *
Стр. 293
Циклический сдвиг влево (ROtate Left). Производит циклический сдвиг операнда&
получателя влево. Количество циклов задается в операнде&отправителе. Старший бит ко&
пируется во флаг переноса и перемещается в младший бит. Для процессоров 8086/8088
операнд imm8 может быть установлен в 1.
Формат команды:
ROL reg, imm8 ROL mem, imm8
ROL reg, CL ROL mem, CL
ROR
O D I S Z A P C
* *
SAHF
O D I S Z A P C
* * * * *
SAL
O D I S Z A P C
* * * ? * *
Арифметический сдвиг влево (Shift Arithmetic Left). Производит сдвиг влево каждого би&
та операнда&получателя. Количество циклов задается в операнде&отправителе. Старший
бит копируется во флаг переноса, а младшие биты заполняются нулями. Для процессо&
ров 8086/8088 операнд imm8 может быть установлен в 1.
Формат команды:
SAL reg, imm8 SAL mem, imm8
SAL reg, CL SAL mem, CL
SAR
O D I S Z A P C
* * * ? * *
Стр. 294
отсутствует. 295
Арифметический сдвиг вправо (Shift Arithmetic Right). Производит сдвиг вправо каждого
бита операндаполучателя. Количество циклов задается в операндеотправителе. Млад
ший бит копируется во флаг переноса, а старшие биты не изменяются. Этот сдвиг часто
используют при работе с операндами со знаком, чтобы сохранить знаковый бит. Для
процессоров 8086/8088 операнд imm8 может быть установлен в 1.
Формат команды:
SAR reg, imm8 SAR mem, imm8
SAR reg, CL SAR mem, CL
SBB
O D I S Z A P C
* * * * * *
Сканирование строки (SCan String). Сканирует строку, адрес которой находится в ре
гистрах ES:(E)DI, сравнивая значения со значением в аккумуляторе. Команда SCAS
требует, чтобы операнд был определен. SCASB выполняет сканирование 8разрядных
значений, сравнивая со значениями в AL. Команда SCASW сканирует 16разрядные зна
чения, сравнивая со значениями в AХ, и SCASD производит сканирование 32разрядных
значений, сравнивая со значениями в EAХ. Регистр (E)DI инкрементируется или декре
ментируется в соответствии с размерами операнда и состоянием флага направления. Ес
ли DF=1, (E)DI декрементируется; если DF=0, (E)DI инкрементируется.
Формат команды:
SCAS dest SCASB
SCAS ES:dest SCASW
SETcondition
O D I S Z A P C
Стр. 295
SHL
O D I S Z A P C
* * * ? * *
Логический сдвиг влево (SHift Left). Производит сдвиг влево каждого бита операнда&
получателя. Количество циклов задается в операнде&отправителе. Старший бит копиру&
ется во флаг переноса, а младшие биты заполняются нулями (подобно SAL). Для процес&
соров 8086/8088 операнд imm8 может быть установлен в 1.
Формат команды:
SHL reg, imm8 SHL mem, imm8
SHL reg, CL SHL mem, CL
SHLD
O D I S Z A P C
* * * ? * *
Сдвиг влево двойной точности (SHift Left Double+precision). Помещает биты второго
операнда в первый операнд. Третий операнд определяет количество сдвигаемых битов.
Освобождаемые позиции заполняются старшими битами второго операнда. Второй опе&
ранд должен быть регистром, а третий может быть или непосредственным значением,
или регистром CL.
Формат команды:
SHLD reg16, reg16, imm8 SHLD mem16, reg16,imm8
SHLD reg32, reg32, imm8 SHLD mem32, reg32, imm8
SHLD reg16, reg16, CL SHLD mem16, reg16, CL
SHLD reg32, reg32, CL SHLD mem32, reg32, CL
SHR
O D I S Z A P C
* * * ? * *
Логический сдвиг вправо (SHift Right). Производит сдвиг вправо каждого бита операн&
да&получателя. Количество циклов задается в операнде&отправителе. Младший бит ко&
пируется во флаг переноса, а старшие биты заполняются нулями. Для процессоров
8086/8088 операнд imm8 может быть установлен в 1.
Формат команды:
SHR reg, imm8 SHR mem, imm8
SHR reg, CL SHR mem, CL
SHRD
O D I S Z A P C
? * * ? * *
Стр. 296
отсутствует. 297
Сдвиг вправо двойной точности (SHift Right Double+precision). (80386) Помещает биты
второго операнда в первый операнд. Третий операнд определяет количество сдвигаемых
битов. Освобождаемые позиции заполняются младшими битами второго операнда. Вто&
рой операнд должен быть регистром, а третий операнд может быть или непосредствен&
ным значением, или регистром CL.
Формат команды:
SHRD reg16, reg16, imm8 SHRD mem16, reg16, imm8
SHRD reg32, reg32, imm8 SHRD mem32, reg32, imm8
SHRD reg16, reg16, CL SHRD mem16, reg16, CL
SHRD reg32, reg32, CL SHRD mem32, reg32, CL
STC
O D I S Z A P C
1
Установить флаг переноса (SeT Carry flag). Устанавливает флаг переноса. Эту команду
используют в вызванной процедуре, чтобы информировать вызывающую программу об
ошибке.
Формат команды: STC
STD
O D I S Z A P C
1
STI
O D I S Z A P C
1
Стр. 297
то операнд&получатель должен быть определен. Команда STOSB копирует в память ре&
гистр AL, STOSW копирует AX и STOSD (для процессора 80386) копирует EAX.
Формат команды:
STOS mem STOSB
STOS ES:mem STOSW
SUB
O D I S Z A P C
TEST
O D I S Z A P C
* * * ? * 0
WAIT
O D I S Z A P C
XADD
O D I S Z A P C
* * * * * *
Стр. 298
отсутствует. 299
XCHG
O D I S Z A P C
XLAT, XLATB
O D I S Z A P C
XOR
O D I S Z A P C
* * * ? * 0
Операция логическое исключающее ИЛИ (eXclusive OR). Над каждым битом операнда&
отправителя и соответствующим битом операнда&получателя производится операция
логическое исключающее ИЛИ. Бит в операнде&получателе будет иметь значение 1 только
в том случае, если соответствующие биты операнда&отправителя и операнда&получателя
имеют различные значения.
Формат команды:
XOR reg, reg XOR reg, imm
XOR mem, reg XOR mem, imm
XOR reg, mem XOR accum, imm
Стр. 299
Crlf
Delay
DumpMem
DumpRegs
GetCommandtail
GetDateTime
GetMaxX
GetMseconds
Gotoxy
IsDigit
Random32
Randomize
RandomRange
ReadChar
ReadDec
ReadHex
ReadInt
ReadKey
ReadKeyFlush
ReadKeyTranslate
ReadString
SetTextColor
Str_compare
Str_copy
Str_length
Str_trim
Str_ucase
WaitMsg
WriteBin
WriteChar
WriteDec
WriteHex
WriteHexB
WriteInt
WriteString
Замечания по реализации:
1. Функция Windows Sleep изменяет содержимое регистра ECX.
2. Не забывайте сохранять и восстанавливать все 32-разрядные регистры
общего назначения (кроме EAX) перед вызовом функций MS-Windows API.
Конец коментария----------------------------------------------------@
;OPTION CASEMAP:NONE ; Чувствительность к регистру.
;-----------------------------------------------------------------
ShowFlag MACRO flagName,shiftCount
LOCAL flagStr, flagVal, L1
; Вспомогательное макроопределение.
; Отображает значения флагов CPU
; Непосредственный доступ к eflags в Study32.asm
;---------------------------------------------------------------------
.data
flagStr DB " &flagName="
flagVal DB ?,0
.code
Стр. 300
отсутствует. 301
push eax
push edx
mov eax,eflags ; Извлечение флагов.
mov flagVal,'1'
shr eax,shiftCount ; Смещение во флаг переноса.
jc L1
mov flagVal,'0'
L1:
mov edx,OFFSET flagStr ; Отображение имени флага и значение.
call WriteString
pop edx
pop eax
ENDM
;-------------------------------------------------------------
CheckInit MACRO
; Вспомогательное макроопределение.
; Проверяет инициализацию дескрипторов консоли. При необходимости
; вызывает функцию инициализации.
;-------------------------------------------------------------
LOCAL exit
cmp InitFlag,0
jne exit
call Initialize
exit:
ENDM
Стр. 301
attribs WORD MAX_COLS DUP(0)
lineLength DWORD 0
cursorLoc COORD <0,0>
count DWORD ?
.code
pushad
CheckInit
;-----------------------------------------------------
Crlf PROC
; Записывает последовательность символов возврата
; каретки (0Dh,0Ah) в стандартный выход.
;-----------------------------------------------------
CheckInit ; Проверка инициализации.
mNewLine ; вызов макроопределения.
Стр. 302
отсутствует. 303
ret
Crlf ENDP
;------------------------------------------------------
Delay PROC
; Задержка текущего процесса на заданное число миллисекунд
; Принимает: EAX = число миллисекунд
; Возарвщает: ничего
;------------------------------------------------------
pushad
INVOKE Sleep,eax
popad
ret
Delay ENDP
;---------------------------------------------------
DumpMem PROC
LOCAL unitsize:dword, byteCount:word
; Записывает участок памяти в стандартный выход в
; шестнадцатеричном виде.
; Принимает: ESI = начальное смещение, ECX = число модулей,
; EBX = размер модуля (1=байт, 2=слово, 4=двойное слово)
; Возвращает: ничего
;---------------------------------------------------
.data
oneSpace DB ' ',0
.code
pushad
mov edx,OFFSET dumpPrompt
call WriteString
mov eax,esi ; Получить смещение.
call WriteHex
mNewLine
mov edx,OFFSET dashLine
call WriteString
mov byteCount,0
mov unitsize,ebx
cmp ebx,4 ; Формат вывода.
je L1
cmp ebx,2
je L2
jmp L3
; Вывод слова.
L2:
Стр. 303
mov ax,[esi] ; Получить слово из памяти.
ror ax,8 ; Отобразить старший байт.
call HexByte
ror ax,8 ; Отобразить младший байт.
call HexByte
mWriteSpace 1 ; Отобразить пробел.
add esi,unitsize ; Указать на следующее слово.
Loop L2
jmp L4
;---------------------------------------------------
DumpRegs PROC
; Отображает EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP в
; шестнадцатеричном виде. Также отображает флаги Zero,
; Sign,Carry и Overflow.
; Принимает: ничего.
; Возвращает: ничего.
;---------------------------------------------------
.data
saveIP DWORD ?
saveESP DWORD ?
.code
pop saveIP ; Получить EIP
mov saveESP,esp ; Сохранить значение ESP's value at entry
push saveIP ; replace it on stack
push eax ; Сохранить EAX (восстановление выхода)
mNewLine
mShowRegister EAX,EAX
Стр. 304
отсутствует. 305
mShowRegister EBX,EBX
mShowRegister ECX,ECX
mShowRegister EDX,EDX
mNewLine
mShowRegister ESI,ESI
mShowRegister EDI,EDI
mShowRegister EBP,EBP
mov eax,saveESP
mShowRegister ESP,EAX
mNewLine
mov eax,saveIP
mShowRegister EIP,EAX
mov eax,eflags
mShowRegister EFL,EAX
mNewLine
mNewLine
popfd
pop eax
ret
DumpRegs ENDP
;------------------------------------------------------------
GetCommandtail PROC
; Копирует аргументы командной строки в буфер
; (после первого аргумента - имени программы)
; Принимает: EDX указывает на 129-байт буфер для приема данных.
; Возвращает: Флаг Carry = 1 если нет аргументов, иначе CF=0
;-------------------------------------------------------------
pushad
INVOKE GetCommandLine ; Функция Win32 API
; возвращает указатель в EAX.
; Копирует командную строку в массив,
; на который указывает EDX (пропускается имя_файла.exe).
mov esi,eax
L0: mov al,[esi] ; Пропуск первого аргумента.
inc esi
cmp al,' ' ; Наличие пробела.
jne L0
mov edi,edx ; Сохранение адреса буфера.
L1: mov al,[esi]
mov [edx],al
inc esi
inc edx
cmp al,0 ; Найдет нулевой байт?
jne L1 ; нет, очередная итерация.
clc ; Предполагает наличие аргументов.
cmp BYTE PTR [edi],0 ; Буфер начинается с нуля?
Стр. 305
jne L2
stc ; Да, устанавливаем флаг переноса.
L2: popad
ret
GetCommandtail ENDP
;--------------------------------------------------
GetDateTime PROC,
pDateTime:PTR QWORD
LOCAL flTime:FILETIME
; Получает локальную дату и время, сохраняя их в
; 64-разрядном числе.
; Принимает: указатель на QWORD
; Возвращает: ничего.
;--------------------------------------------------
pushad
;----------------------------------------------------------------
GetMaxXY PROC
LOCAL bufInfo:CONSOLE_SCREEN_BUFFER_INFO
; Возвращает текущее положение курсора консоли: колонка (X), строка (Y).
; Принимает: ничего.
; Возвращает: DH = строка (Y); DL = колонка (X) (диапазон 1-255)
;----------------------------------------------------------------
.data
.code
push eax
CheckInit
mov dx,bufInfo.dwSize.X
mov ax,bufInfo.dwSize.Y
mov dh,al
pop eax
Стр. 306
отсутствует. 307
ret
GetMaxXY ENDP
;----------------------------------------------------------------
GetMseconds PROC USES ebx edx
LOCAL hours:DWORD, min:DWORD, sec:DWORD
;Возвращает число миллисекунд после полуночи.
;Принимает: ничего.
;Возвращает: число миллисекунд.
;Подсчет: ((часы*3600)+(минуты*60)+секунды))*1000+миллисекунды
;-----------------------------------------------------------------
pushad
INVOKE GetLocalTime,OFFSET sysTime
; Преобразование часов в секунды.
popad
movzx eax,sysTime.wHour
mov ebx,3600
mul ebx
mov hours,eax
ret
GetMseconds ENDP
;--------------------------------------------------
Gotoxy PROC
; Размещение курсора на консоле.
; Принимает: DH = строка, DL = колонка.
; Последнее обновление: 7/11/01
;--------------------------------------------------------
.data
_cursorPosition COORD <>
.code
pushad
Стр. 307
movzx ax,dh
mov _cursorPosition.Y, ax
INVOKE SetConsoleCursorPosition, consoleOutHandle, _cursorPosition
popad
ret
Gotoxy ENDP
;----------------------------------------------------
Initialize PROC private
; Получить стандартные дескрипторы консоли для входа и выхода и
; установить переменную (флаг) инициализации.
;----------------------------------------------------
pushad
mov InitFlag,1
popad
ret
Initialize ENDP
;-----------------------------------------------
IsDigit PROC
; Определяет, является ли символ в AL десятичной цифрой.
; Принимает: AL = символ
; Возвращает: ZF=1 если AL содержит цифру,
; в противном случае ZF=0.
;-----------------------------------------------
cmp al,'0'
jb ID1
cmp al,'9'
ja ID1
test ax,0 ; устанавливает ZF = 1
ID1:
ret
IsDigit ENDP
;--------------------------------------------------------------
RandomRange PROC
; Генерирует псевдо-случайную последовательность 32-разрядных
; целых чисел в диапазоне 0 ... (n-1).
; Принимает: EAX = n.
; Возвращает: EAX = случайное число.
;--------------------------------------------------------------
push ebx
push edx
Стр. 308
отсутствует. 309
pop edx
pop ebx
ret
RandomRange ENDP
;--------------------------------------------------------------
Random32 PROC
; Генерирует псевдо-случайную последовательность 32-разрядных
; целых чисел в диапазоне 0 ... FFFFFFFFh.
; Принимает: ничего.
; Возвращает: EAX = случайное число.
;--------------------------------------------------------------
.data
seed DWORD 1
.code
push edx
mov eax, 343FDh
imul seed
add eax, 269EC3h
mov seed, eax ; Нач. значение для очередного вызова.
ror eax,8 ; Ротация младщих цифр.
pop edx
ret
Random32 ENDP
;--------------------------------------------------------
Randomize PROC
; Инициирует генератор случайных чисел текущим временем в секундах.
; Принимает: ничего.
; Возвращает: ничего.
;--------------------------------------------------------
pushad
INVOKE GetSystemTime,OFFSET sysTime
movzx eax,sysTime.wMilliseconds
mov seed,eax
popad
ret
Randomize ENDP
;------------------------------------------------------------
ReadChar PROC
; Считывает один символ со стандартного входа (консоли). Символ
; не дублируется на экран. Ожидает ввода, если входной буфер пуст.
; Принимает: ничего.
; Возвращает: AL = код ASCII
; Добавлено.
;----------------------------------------------------------
push ebx
push eax
Стр. 309
mov bl,al ; Сохранить код ASCII.
pop eax
mov al,bl
pop ebx
ret
ReadChar ENDP
;--------------------------------------------------------
ReadDec PROC USES ebx ecx edx esi
LOCAL saveDigit:DWORD
; Считывает 32-разрядное беззнаковое целое число со стандартного
; входа. Концом ввода служит нажатие клавиши <Enter>.
; Все цифры, введенные до нецифрового символа, преобразовываются
; в числовое значение. Предшествующие пробелы игнорируются.
; Принимает: ничего.
; Возвращает:
; Если ничего не введено, EAX=0 and CF=1
; Если введены одни пробелы, EAX=0 and CF=1
; Если целое число больше 2^32-1, EAX=0 and CF=1
; В остальных случаях, EAX=преобразованное число и CF=0
;--------------------------------------------------------
; Преобразование числа.
L2: mov eax,0 ; Очистить аккумулятор.
mov ebx,10 ; EBX является делителем.
mov saveDigit,edx
mul ebx ; EDX:EAX = EAX * EBX
jc L5 ; quit if Carry (EDX > 0)
mov edx,saveDigit
Стр. 310
отсутствует. 311
;--------------------------------------------------------
ReadHex PROC USES ebx ecx edx esi
; Считывает 32-разрядное шестнадцатеричное число со стандартного
; входа. Концом ввода служит нажатие клавиши <Enter>.
; Принимает: ничего.
; Возвращает: EAX = двоичное целое число
; Если ничего не введено, EAX=0 and CF=1
; Если введены одни пробелы, EAX=0 and CF=1
; В остальных случаях, EAX=преобразованное число и CF=0
; Замечание: Не производится проверки ошибок ввода.
;--------------------------------------------------------
.data
xbtable BYTE 0,1,2,3,4,5,6,7,8,9,7 DUP(0FFh),10,11,12,13,14,15
numVal DWORD ?
charVal BYTE ?
.code
mov edx,OFFSET digitBuffer
mov esi,edx ; save in ESI also
mov ecx,MAX_DIGITS
call ReadString ; Считать строку.
mov ecx,eax ; Сохранить длину в ECX.
cmp ecx,0 ; Больше чем 0?
jne B1 ; Да: продолжать.
jmp B8 ; Нет: выход с CF=1.
; Преобразование числа.
B4: mov numVal,0 ; Очистить аккумулятор.
mov ebx,OFFSET xbtable ; Транслировать таблицу.
Стр. 311
B6: sub al,30h ; Скорректировать таблицу.
xlat ; Преобразовать в двоичное.
mov charVal,al
mov eax,16 ; numVal *= 16
mul numVal
mov numVal,eax
movzx eax,charVal ; numVal += charVal
add numVal,eax
inc esi ; Указать на следующую цифру.
loop B5 ; Повторять, декрементировать счетчик.
B9: ret
ReadHex ENDP
;--------------------------------------------------------
ReadInt PROC USES ebx ecx edx esi
LOCAL Lsign:SDWORD, saveDigit:DWORD
; Пропуск пробелов.
L1: mov al,[esi] ; Получить символ из буфера.
cmp al,' ' ; Пробел?
jne L2 ; Нет: проверить знак.
inc esi ; Да: указать на следующий символ.
loop L1
Стр. 312
отсутствует. 313
mov saveDigit,edx
imul ebx ; EDX:EAX = EAX * EBX
mov edx,saveDigit
Стр. 313
L7A:
mov edx,OFFSET invalid_msgL
;------------------------------------------------------------------
ReadKey PROC USES ecx
LOCAL evEvents:WORD, saveFlags:DWORD
; Выполняет непосредственную проверку клавиатуры и считывает
; доступный символ.
; Если код Ascii равен нулю, то обрабатываются функцианальные
; клавиши.
; Принимает: ничего.
; Возвращает: ZF установлен, если нет нажатия, сбрасывается при
; считывании символа.
; AL = код Ascii клавиши (0 для специальных клавиш).
; AH = Виртуальный скан-код (или преобразованный при Translate).
; DX = Виртуальный код клавиши.
; EBX = Флаги клавиатуры (Alt,Ctrl,Caps и т.д.)
; Верхние половинки EAX и EDX переписывается.
;-----------------------------------------------------------------
.data
evBuffer INPUT_RECORD <> ; Буфера клавиш.
evRepeat WORD 0 ; Счетчик повторений.
.code
CheckInit ; Инициализация, если не инициализировано.
Peek:
; Если есть задержанные события, обработать их.
INVOKE PeekConsoleInput, consoleInHandle, ADDR evBuffer,
1, ADDR evEvents
test evEvents,0FFFFh
Стр. 314
отсутствует. 315
HaveKey:
mov al,evBuffer.Event.uChar.AsciiChar ; Симол Ascii в AL.
mov ah,BYTE PTR evBuffer.Event.wVirtualScanCode ; Скан-код в AH.
mov dx,evBuffer.Event.wVirtualKeyCode ; Код вирт.клавиши в DX.
mov ebx,evBuffer.Event.dwControlKeyState ; Флаги в EBX.
NoKey:
mov evRepeat,0 ; Переустановить счетчик повторений.
test eax,0 ; Нет нажатия: ZF=1 и выход.
Done:
pushfd ; Сохранить флаг нуля.
pushad
; Восстановить режим консоли.
INVOKE SetConsoleMode,consoleInHandle,saveFlags
call ReadKeyFlush
popad
popfd ; Восстановить флаг нуля.
ret
ReadKey ENDP
;-----------------------------------------------------------------
ReadKeyFlush PROC
; Выгружает входной буфер консоли и сбрасывает счетчики.
; Может использоваться для быстрой реакции на нажатия клавиш в
; играх, где использование обработчика данных приводит к задержкам.
; Принимает: ничего.
; Возвращает: ничего.
;-----------------------------------------------------------------
Стр. 315
INVOKE FlushConsoleInputBuffer, consoleInHandle ; Выгрузить буфер
mov evRepeat,0 ; Переустановить счетчик.
ret
ReadKeyFlush ENDP
;-----------------------------------------------------------------
ReadKeyTranslate PROC PRIVATE USES ebx ecx edx esi
; Перевести скан-код в совместимые с DOS/BIOS возвращаемые значения
; Принимает:
; al = код Ascii клавиши,
; ah = виртуальный скан-код,
; dx = виртуальный код клавиши,
; ebx = флаги клавиатуры (Alt,Ctrl,Caps и т.д.)
; Возвращает:
; ah = измененный скан-код (для Alt/Ctrl/Shift и др.)
; al = измененный код Ascii клавиши (0 для функциональных)
;-----------------------------------------------------------------
.data ; Таблица перевода.
; Порядок: виртуальная клавиша, скан-код, CtrlScan, AltScan
SpecialCases \
BYTE VK_LEFT, 4Bh, 73h, 4Bh
CaseSize = ($ - SpecialCases) ; Размер элемента.
BYTE VK_RIGHT, 4Dh, 74h, 4Dh
BYTE VK_UP, 48h, 8Dh, 48h
BYTE VK_DOWN, 50h, 91h, 50h
BYTE VK_PRIOR, 49h, 84h, 49h ; PgUp
BYTE VK_NEXT, 51h, 76h, 51h ; PgDn
BYTE VK_HOME, 47h, 77h, 47h
BYTE VK_END, 4Fh, 75h, 4Fh
BYTE VK_INSERT,52h, 92h, 52h
BYTE VK_DELETE,53h, 93h, 53h
BYTE VK_ADD, 4Eh, 90h, 4Eh
BYTE VK_SUBTRACT,4Ah,8Eh, 4Ah
BYTE VK_F11, 85h, 85h, 85h
BYTE VK_F12, 86h, 86h, 86h
BYTE VK_11, 0Ch, 0Ch, 82h
BYTE VK_12, 0Dh, 0Dh, 83h
BYTE 0 ; Конец таблицы.
.code
pushfd ; Сохранение флагов.
mov esi,0
; Поиск в таблице.
Search:
cmp SpecialCases[esi],0 ; Проверка окончания таблицы.
je NotFound
Found:
.IF ebx & CTRL_MASK
mov ah,SpecialCases[esi+2] ; Установить скан-код Ctrl.
mov al,0 ; Обновить символ.
Стр. 316
отсутствует. 317
NotFound:
.IF ! (ebx & KEY_MASKS) ; Выполнить, если нет комбинации shift/ctrl/alt.
jmp Done
.ENDIF
Done:
popfd ; Восстановление.
ret
ReadKeyTranslate ENDP
;------------------------------------------------------------------
ReadString PROC
LOCAL bufSize:DWORD, saveFlags:DWORD, junk:DWORD
;
; Считывание строки с консоли в буфер.
; Принимает: EDX смещение входного буфера.
; ECX = максимальное число символов с нулевым.
; Возвращает: EAX = размер входной строки.
; Комментарий: Прекращение ввода при нажатии <Enter>.
;---------------------------------------------------------------------
.data
_$$temp DWORD ?
.code
pushad
CheckInit
mov edi,edx
mov bufSize,ecx
push edx
INVOKE ReadConsole,
Стр. 317
consoleInHandle,
edx,
ecx,
OFFSET bytesRead,
0
pop edx
cmp bytesRead,0
jz L5
dec bytesRead
cld
mov ecx,bufSize
mov al,0Ah
repne scasb
jne L1
L5: popad
mov eax,bytesRead
ret
ReadString ENDP
;---------------------------------------------------------------------
SetTextColor PROC
; Изменить цвет последующего выходного текста.
; Принимает: EAX = атрибуты. Биты 0-3 цвет символа,
; биты 4-7 цвет фона.
; Возвращает: ничего.
;---------------------------------------------------------------------
.data
scrAttrib DWORD ?
Стр. 318
отсутствует. 319
.code
pushad
mov scrAttrib,eax ; Младший байт содержит атрибуты.
INVOKE GetStdHandle, STD_OUTPUT_HANDLE
mov [consoleOutHandle],eax
;---------------------------------------------------------------------
Str_compare PROC USES eax edx esi edi,
string1:PTR BYTE,
string2:PTR BYTE
; Сравнение строк.
; Возврат: ничего, но флаги нуля и переноса изменяются как
; при команде CMP.
;-----------------------------------------------------
mov esi,string1
mov edi,string2
;---------------------------------------------------------
Str_copy PROC USES eax ecx esi edi,
source:PTR BYTE, ; source string
target:PTR BYTE ; target string
;
; Копирование строки.
; Требования: приемная строка должна содержать достаточно места.
;----------------------------------------------------------
INVOKE Str_length,source ; EAX = длина исходной.
mov ecx,eax ; Счетчик.
inc ecx ; add 1 for null byte
mov esi,source
mov edi,target
cld ; Направление вверх.
rep movsb ; Копирование.
ret
Str_copy ENDP
;---------------------------------------------------------
Стр. 319
Str_length PROC USES edi,
pString:PTR BYTE ; pointer to string
;
; Подсчет длины строки с нулевым окончанием.
; Принимает: pString - указатель на строку
; Возвращает: EAX = длина строки
;---------------------------------------------------------
mov edi,pString
mov eax,0
L1:
cmp BYTE PTR [edi],0
je L2
inc edi
inc eax
jmp L1
L2: ret
Str_length ENDP
;-----------------------------------------------------------
Str_trim PROC USES eax ecx edi,
pString:PTR BYTE, ; points to string
char:BYTE ; char to remove
;
; Удаляет все вхождения заданного символа
; в конце строки.
; Возвращает: ничего.
;-----------------------------------------------------------
mov edi,pString
INVOKE Str_length,edi
cmp eax,0
je L2
mov ecx,eax
dec eax
add edi,eax
mov al,char
std
repe scasb
jne L1
dec edi
L1: mov BYTE PTR [edi+2],0
L2: ret
Str_trim ENDP
;---------------------------------------------------
Str_ucase PROC USES eax esi,
pString:PTR BYTE
; Устанавливает символы строки в верхний регистр.
; Принимает: pString - указатель на строку.
; Возвращает: ничего.
;---------------------------------------------------
mov esi,pString
L1:
mov al,[esi] ; очередной символ.
cmp al,0 ; конец строки?
je L3 ; Да: выход.
cmp al,'a' ; Перед "a"?
jb L2
cmp al,'z' ; После "z"?
Стр. 320
отсутствует. 321
ja L2
and BYTE PTR [esi],11011111b ; Преобразовать.
L3: ret
Str_ucase ENDP
;------------------------------------------------------
WaitMsg PROC
; Отображает запрос и ожидает нажатия клавиши Enter.
; Принимает: ничего.
; Возвращает: ничего.
;------------------------------------------------------
WAITMSG_BUFSIZE = 5
.data
waitmsgstr DB "Press [Enter] to continue...",0
localBuf BYTE WAITMSG_BUFSIZE DUP(?)
.code
pushad
CheckInit
mov edx,OFFSET waitmsgstr
call WriteString
mNewLine
INVOKE ReadConsole,
consoleInHandle, ; Дескриптор входа консоли.
OFFSET localBuf, ; Указатель на локальный буфер.
WAITMSG_BUFSIZE, ; Максимальное показание счетчика.
OFFSET bytesRead,
0
cmp bytesRead,2
jnz w1 ; Цикл до возврата ReadConsole 2 байт.
popad
ret
WaitMsg ENDP
;------------------------------------------------------
WriteBin PROC
; Запись 32-разрядного целого числа в выходной поток.
; Принимает: EAX = число.
; Возвращает: ничего.
;------------------------------------------------------
push ebx
mov ebx,4 ; Формат двойного слова.
call WriteBinB
pop ebx
ret
WriteBin ENDP
;------------------------------------------------------
WriteBinB PROC
; Запись 32-разрядного целого числа в выходной поток.
; Принимает: EAX = число.
Стр. 321
; EBX = отображаемый размер (1,2,4)
; Возвращает: ничего.
;------------------------------------------------------
pushad
cmp ebx,1 ; Убедиться, что EBX равен 1, 2 или 4
jz WB0
cmp ebx,2
jz WB0
mov ebx,4
WB0:
mov ecx,ebx
shl ecx,1 ; Число из 4-бит в конце EAX.
cmp ebx,4
jz WB0A
ror eax,8 ; Предположим TYPE==1.
cmp ebx,1
jz WB0A ; Предположение правильно.
ror eax,8 ; TYPE==2.
WB0A:
mov esi,OFFSET buffer
WB1:
push ecx ; Сохраняем счетчик.
mov ecx,4 ; В группе 4 бита.
WB1A:
shl eax,1 ; Сдвигает EAX во флаг переноса.
mov BYTE PTR [esi],'0'
jnc WB2
mov BYTE PTR [esi],'1'
WB2:
inc esi
Loop WB1A
mov BYTE PTR [esi],' ' ; Пробел
inc esi ; между группами.
pop ecx ; Восстановить счетчик.
loop WB1 ; Следующие 4-бита.
dec esi ; Исключить пробелы.
mov BYTE PTR [esi],0 ; Нулевой байт в конец.
mov edx,OFFSET buffer ; Отобразить буфер.
call WriteString
popad
ret
WriteBinB ENDP
;------------------------------------------------------
WriteChar PROC
; Записать символ в выходной поток.
; Принимает: AL = символ.
;------------------------------------------------------
pushad
pushfd
CheckInit
mov buffer,al
cld ; Обязательно сбросить флаг направления.
INVOKE WriteConsole,
consoleOutHandle,
OFFSET buffer,
1,
Стр. 322
отсутствует. 323
OFFSET bytesWritten,
0
popfd
popad
ret
WriteChar ENDP
;-----------------------------------------------------
WriteDec PROC
; Записать 32-разрядное число в выходной поток.
; Вход: EAX = число.
;------------------------------------------------------
.data
; До 10 цифр.
BUFFER_SIZE = 12
bufferL BYTE BUFFER_SIZE DUP(?),0
.code
pushad
CheckInit
mov ecx,0 ; Счетчик.
mov edi,OFFSET bufferL
add edi,(BUFFER_SIZE - 1)
mov ebx,10 ; Основание счисления.
WI4:
popad
ret
WriteDec ENDP
;------------------------------------------------------
WriteHex PROC
; Записать 32-разрядное шестнадцатеричное число
; без знака в выходной поток.
; Вход: EAX = Число.
;------------------------------------------------------
push ebx
mov ebx,4
call WriteHexB
pop ebx
Стр. 323
ret
WriteHex ENDP
;------------------------------------------------------
WriteHexB PROC
LOCAL displaySize:DWORD
; Записать 32-разрядное шестнадцатеричное число
; без знака в выходной поток.
; Принимает: EAX = число.
; EBX = отображаемый размер (1,2,4)
; Возвращает: ничего.
;------------------------------------------------------
DOUBLEWORD_BUFSIZE = 8
.data
bufferLHB BYTE DOUBLEWORD_BUFSIZE DUP(?),0
.code
pushad
mov displaySize,ebx
.IF EBX == 1
and eax,0FFh
.ELSE
.IF EBX == 2
and eax,0FFFFh
.ELSE
mov displaySize,4
.ENDIF
.ENDIF
CheckInit
mov edi,displaySize
shl edi,1
mov bufferLHB[edi],0
dec edi
mov ecx,0
mov ebx,16
L1:
mov edx,0
div ebx
xchg eax,edx
call AsciiDigit
mov bufferLHB[edi],al
dec edi
xchg eax,edx
inc ecx
or eax,eax
jnz L1
mov eax,displaySize
shl eax,1
sub eax,ecx
jz L3
mov ecx,eax
L2:
mov bufferLHB[edi],'0'
dec edi
loop L2
Стр. 324
отсутствует. 325
; Отображаем цифры.
L3:
mov ecx,displaySize
shl ecx,1
inc edi
mov edx,OFFSET bufferLHB
add edx,edi
call WriteString
popad
ret
WriteHexB ENDP
;-----------------------------------------------------
WriteInt PROC
; Записывает 32-разрядное число со знаком в стандартный выход
; в коде ASCII.
; Принимает: EAX = целое число.
; Возвращает: ничего.
; Комментарий: Отображает знак числа.
;-----------------------------------------------------
WI_Bufsize = 12
true = 1
false = 0
.data
buffer_B BYTE WI_Bufsize DUP(0),0 ; Буфер для цифр.
neg_flag BYTE ?
.code
pushad
CheckInit
WIS1:
mov ecx,0 ; Счетчик цифр = 0
mov edi,OFFSET buffer_B
add edi,(WI_Bufsize-1)
mov ebx,10 ; Будет делить на 10
WIS2:
mov edx,0 ; Устанавливает делимое в 0.
div ebx ; Делим AX на 10.
or dl,30h ; Преобразовывает остаток в ASCII
dec edi ; Возвращаемся.
mov [edi],dl ; Сохранение цифр ASCII.
inc ecx ; Инкрементируем счетчик.
or eax,eax ; Частное > 0?
jnz WIS2 ; Да: делим дальше.
; Вставляем знак.
dec edi
inc ecx
mov BYTE PTR [edi],'+' ; Вставляет знак плюс.
Стр. 325
cmp neg_flag,false ; Число положительное?
jz WIS3 ; Да.
mov BYTE PTR [edi],'-' ; Нет: вставляем знак минус.
;--------------------------------------------------------
WriteString PROC
;
; Записывает строку с нулевым окончанием в стандартный выход.
; Принимает: EDX указывает на строку.
;--------------------------------------------------------
pushad
CheckInit
INVOKE WriteConsole,
consoleOutHandle, ; Дескриптор выхода.
edx, ; Указывает на строку.
eax, ; Длина строки.
OFFSET bytesWritten, ; Число записанных байт.
0
popad
ret
WriteString ENDP
Стр. 326
отсутствует. 327
and al,0Fh
xlat
mov [buffer+1],al ; Сохранение второго символа.
mov [buffer+2],0
popad
ret
HexByte ENDP
end
FILE_ATTRIBUTE_READONLY = 1
FILE_ATTRIBUTE_HIDDEN = 2
FILE_ATTRIBUTE_SYSTEM = 4
FILE_ATTRIBUTE_DIRECTORY = 10h
FILE_ATTRIBUTE_ARCHIVE = 20h
FILE_ATTRIBUTE_DEVICE = 40h
FILE_ATTRIBUTE_NORMAL = 80h
FILE_ATTRIBUTE_TEMPORARY = 100h
FILE_ATTRIBUTE_SPARSE_FILE = 200h
FILE_ATTRIBUTE_REPARSE_POINT = 400h
FILE_ATTRIBUTE_COMPRESSED = 800h
FILE_ATTRIBUTE_OFFLINE = 1000h
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 2000h
FILE_ATTRIBUTE_ENCRYPTED = 4000h
FILE_READ_DATA = 1
FILE_WRITE_DATA = 2
FILE_APPEND_DATA = 4
FILE_DELETE_CHILD = 40h
FILE_BEGIN = 0
FILE_CURRENT = 1
FILE_END = 2
CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5
GENERIC_READ = 80000000h
GENERIC_WRITE = 40000000h
GENERIC_EXECUTE = 20000000h
Стр. 327
GENERIC_ALL = 10000000h
INVALID_HANDLE_VALUE = -1
ENABLE_PROCESSED_INPUT = 1
ENABLE_LINE_INPUT = 2
ENABLE_ECHO_INPUT = 4
ENABLE_WINDOW_INPUT = 8
ENABLE_MOUSE_INPUT = 16
ENABLE_PROCESSED_OUTPUT = 1
ENABLE_WRAP_AT_EOL_OUTPUT = 2
; Константы событий.
KEY_EVENT = 1
MOUSE_EVENT = 2
WINDOW_BUFFER_SIZE_EVENT = 4 ; Запись изменений событий окна.
MENU_EVENT = 8 ; Запись меню событий.
FOCUS_EVENT = 16 ; Изменение фокуса.
Стр. 328
отсутствует. 329
VK_NUMPAD0 = 60H
VK_NUMPAD1 = 61H
VK_NUMPAD2 = 62H
VK_NUMPAD3 = 63H
VK_NUMPAD4 = 64H
VK_NUMPAD5 = 65H
VK_NUMPAD6 = 66H
VK_NUMPAD7 = 67H
VK_NUMPAD8 = 68H
VK_NUMPAD9 = 69H
VK_MULTIPLY = 6AH
VK_ADD = 6BH
VK_SEPARATER = 6CH
VK_SUBTRACT = 6DH
VK_DECIMAL = 6EH
VK_DIVIDE = 6FH
VK_F1 = 70H
VK_F2 = 71H
VK_F3 = 72H
VK_F4 = 73H
VK_F5 = 74H
VK_F6 = 75H
VK_F7 = 76H
VK_F8 = 77H
VK_F9 = 78H
VK_F10 = 79H
VK_F11 = 7aH
VK_F12 = 7bH
VK_F13 = 7cH
VK_F14 = 7dH
VK_F15 = 7eH
VK_F16 = 7fH
VK_F17 = 80H
VK_F18 = 81H
VK_F19 = 82H
VK_F20 = 83H
VK_F21 = 84H
VK_F22 = 85H
VK_F23 = 86H
VK_F24 = 87H
VK_NUMLOCK = 90H
VK_SCROLL = 91H
VK_11 = 0BDh ; клавиша -
VK_12 = 0BBh ; клавиша +
COORD STRUCT
X WORD ?
Y WORD ?
COORD ENDS
Стр. 329
SYSTEMTIME STRUCT
wYear WORD ?
wMonth WORD ?
wDayOfWeek WORD ?
wDay WORD ?
wHour WORD ?
wMinute WORD ?
wSecond WORD ?
wMilliseconds WORD ?
SYSTEMTIME ENDS
KEY_EVENT_RECORD STRUCT
bKeyDown DWORD ?
wRepeatCount WORD ?
wVirtualKeyCode WORD ?
wVirtualScanCode WORD ?
UNION uChar
UnicodeChar WORD ?
AsciiChar BYTE ?
ENDS
dwControlKeyState DWORD ?
KEY_EVENT_RECORD ENDS
SMALL_RECT STRUCT
Left WORD ?
Top WORD ?
Right WORD ?
Bottom WORD ?
SMALL_RECT ENDS
INPUT_RECORD STRUCT
eventType WORD ?
ALIGN DWORD
event KEY_EVENT_RECORD <>
INPUT_RECORD ENDS
GetConsoleScreenBufferInfo PROTO,
Стр. 330
отсутствует. 331
GetConsoleMode PROTO,
ioHandle:DWORD, ; Дескриптор входа.
pMode:PTR DWORD ; Указатель на dword, содержащий флаги.
PeekConsoleInput PROTO,
inHandle:DWORD, ; Дескриптор входа.
pBuffer:PTR BYTE, ; Указатель на буфер.
nNumberOfBytesToRead:DWORD, ; Количество символов для чтения.
pNumEventsRead:PTR DWORD ; Указатель на события.
SetConsoleMode PROTO,
outHandle:DWORD, ; Дескриптор выхода.
dwMode:DWORD ; Режим флагов консоли.
ReadConsoleInput PROTO,
inHandle:DWORD, ; Дескриптор входа.
pInputRec:PTR INPUT_RECORD, ; Указатель на входную запись.
numRecs:DWORD, ; request number of recs
pNumRead:PTR DWORD ; ptr to number of bytes read
WriteConsoleOutputCharacter PROTO,
outHandle:DWORD, ; Дескриптор выхода консоли.
pBuffer:PTR BYTE, ; Указатель на буфер.
bufsize:DWORD, ; Размер буфера.
xyPos:COORD, ; Координаты первой ячейки.
pCount:PTR DWORD ; Счетчик.
ReadConsole PROTO,
handle:DWORD, ; Дескриптор входного устройства.
lpBuffer:PTR BYTE, ; Указатель на буфер.
nNumberOfBytesToRead:DWORD, ; Количество байт для чтения.
lpNumberOfBytesWritten:PTR DWORD,; Количество считанных байт.
lpReserved:DWORD ; Зарезервировано.
CreateFile PROTO,
pFilename:PTR BYTE, ; Указатель на имя файла.
desiredAccess:DWORD, ; Режим доступа.
shareMode:DWORD, ; Режим совместной работы.
IpSecurity:DWORD, ; Указатель на атрибуты защиты.
creationDisposition:DWORD, ; Опции создания файла.
flagsAndAttributes:DWORD, ; Атрибуты файла.
htemplate:DWORD ; Дескриптор шаблона файла.
WriteFile PROTO,
fileHandle:DWORD, ; Дескриптор выходного устройства.
Стр. 331
pBuffer:PTR BYTE, ; Указатель на буфер.
nBufsize:DWORD, ; Размер буфера.
pBytesWritten:PTR DWORD, ; Число записанных байт.
pOverlapped:PTR DWORD ; Указатель на информацию асинхронного режима.
WriteConsoleOutputAttribute PROTO,
outHandle:DWORD, ; Дескриптор выходного устройства.
pAttribute:PTR WORD, ; Атрибуты записи.
nLength:DWORD, ; Количество ячеек.
xyCoord:COORD, ; Координаты первой ячейки.
pCount:PTR DWORD ; Количество записанных ячеек.
WriteConsoleOutputCharacterA PROTO,
outHandle:DWORD, ; Дескриптор выходного устройства.
pBuffer:PTR BYTE, ; Указатель на буфер.
bufsize:DWORD, ; Размер буфера.
xyPos:COORD, ; Координаты первой ячейки.
pCount:PTR DWORD ; Счетчик.
SetConsoleCursorPosition PROTO,
outHandle:DWORD, ; Дескриптор выходного устройства.
coords:COORD ; Координаты X,Y экрана.
RECT STRUCT
left DWORD ?
top DWORD ?
right DWORD ?
bottom DWORD ?
RECT ENDS
MSGStruct STRUCT
msgWnd DWORD ?
msgMessage DWORD ?
msgWparam DWORD ?
msgLparam DWORD ?
msgTime DWORD ?
msgPt POINT <>
Стр. 332
отсутствует. 333
MSGStruct ENDS
WNDCLASS STRUC
style DWORD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
WNDCLASS ENDS
GetMessageA PROTO,
lpMsg:PTR BYTE,
hWnd:DWORD,
firstMsg:DWORD,
lastMsg:DWORD
Стр. 333
GetModuleHandleA PROTO,
pString:PTR BYTE
GetWindowDC PROTO, ; Получить текущий контекст объекта Windows.
hWind:DWORD
GetWindowRect PROTO, ; Получить размеры текущего объекта Windows.
hWind:DWORD,
pRect:PTR RECT
LoadCursorA PROTO, ; Загрузить ресурсы курсора.
hInstance:DWORD,
pString:PTR BYTE
LoadIconA PROTO, ; Загрузить ресурсы пиктограмм.
hInstance:DWORD,
pString:PTR BYTE
LocalFree PROTO, ; Освободить локальную память, используя ее дескриптор.
hMem:DWORD
MessageBoxA PROTO, ; Отобразить всплывающее сообщение.
hWnd:DWORD,
lpText:PTR BYTE,
lpCaption:PTR BYTE,
style:DWORD
PostQuitMessage PROTO, ; Сообщить Windows о закрытии приложения.
exitCode:DWORD
RegisterClassA PROTO, ; Зарегистрировать новый оконный класс.
pWndClass:PTR WNDCLASS
Comment !
SelectObject PROTO, ; GDI32.LIB
hdc:DWORD
hGDIobject:DWORD
DeleteObject PROTO, ; GDI32.LIB
hGDIobject:DWORD
!
Стр. 334
отсутствует. 335
;MB_ICONERROR = 010h
MB_ICONHAND = 10h
MB_ICONQUESTION = 20h
MB_ICONEXCLAMATION = 30h
MB_ICONASTERISK = 40h
MB_USERICON = 80h
MB_ICONWARNING = MB_ICONEXCLAMATION
MB_ICONERROR = MB_ICONHAND
MB_ICONINFORMATION = MB_ICONASTERISK
MB_ICONSTOP = MB_ICONHAND
MAIN_WINDOW_STYLE =
WS_VISIBLE+WS_DLGFRAME+WS_CAPTION+WS_BORDER+WS_SYSMENU \
+WS_MAXIMIZEBOX+WS_MINIMIZEBOX+WS_THICKFRAME
CONSOLE_SCREEN_BUFFER_INFO STRUCT
Стр. 335
dwSize COORD <>
dwCursorPos COORD <>
wAttributes WORD ?
srWindow SMALL_RECT <>
maxWinSize COORD <>
CONSOLE_SCREEN_BUFFER_INFO ENDS
.LIST
mNewLine MACRO
- Новая строка.
Стр. 336
отсутствует. 337
mNewLine MACRO
mWrite <0dh,0ah>
ENDM
;------------------------------------------------------
mClrscr MACRO
call Clrscr
ENDM
;----------------------------------------------------
mDump MACRO varName:REQ, useLabel
IFB <varName>
EXITM
ENDIF
call Crlf
IFNB <useLabel>
mWrite "Variable name: &varName"
ELSE
mWrite " "
ENDIF
mDumpMem OFFSET varName, LENGTHOF varName, TYPE varName
ENDM
;------------------------------------------------------
mDumpMem MACRO address:REQ, itemCount:REQ, componentSize:REQ
IFB <address>
EXITM
ENDIF
IFB <itemCount>
EXITM
ENDIF
IFB <componentSize>
EXITM
ENDIF
push ebx
push ecx
push esi
mov esi,address
mov ecx,itemCount
mov ebx,componentSize
call DumpMem
pop esi
pop ecx
pop ebx
ENDM
;------------------------------------------------------
mGotoxy MACRO X:REQ, Y:REQ
IFB <X>
EXITM
ENDIF
IFB <Y>
EXITM
ENDIF
Стр. 337
push edx
mov dh,Y
mov dl,X
call Gotoxy
pop edx
ENDM
;------------------------------------------------------
mReadStr MACRO varName:REQ
IFB <varName>
EXITM
ENDIF
push ecx
push edx
mov edx,OFFSET varName
mov ecx,(SIZEOF varName) - 1
call ReadString
pop edx
pop ecx
ENDM
;---------------------------------------------------
mShow MACRO itsName:REQ, format:=<HIN>
LOCAL tempStr
; format — строка из следующих символов:
; H - шестнадцатеричное
; D - десятичное без знака
; I - десятичное со знаком
; B - двоичное
; N - добавление перевода каретки
; Отображение в каждом указанном формате
.data
tempStr BYTE " &itsName = ",0
.code
pushad
IF (OPATTR (itsName)) AND 00010000b
MSHOWITSNAMETYPE = 0
FOR reg8,<al,ah,bl,bh,cl,ch,dl,dh>
IFIDNI <itsName>,<reg8>
MSHOWITSNAMETYPE = 1
movzx ecx,itsName
movsx edx,itsName
ENDIF
ENDM
FOR reg16,<ax,bx,cx,dx,si,di,bp,sp>
IFIDNI <itsName>,<reg16>
MSHOWITSNAMETYPE = 2
movzx ecx,itsName
movsx edx,itsName
ENDIF
ENDM
FOR regseg,<cs,ds,es,fs,gs,ss>
IFIDNI <itsName>,<regseg>
MSHOWITSNAMETYPE = 2
mov ax,itsName
movsx edx,ax
Стр. 338
отсутствует. 339
movzx ecx,ax
ENDIF
ENDM
FOR reg32,<eax,ebx,ecx,edx,esi,edi,ebp,esp>
IFIDNI <itsName>,<reg32>
MSHOWITSNAMETYPE = 4
mov ecx,itsName
mov edx,itsName
ENDIF
ENDM
ELSE
MSHOWITSNAMETYPE = TYPE itsName
IF MSHOWITSNAMETYPE EQ 4
mov ecx,itsName
mov edx,ecx
ELSE
movzx ecx,itsName
movsx edx,itsName
ENDIF
ENDIF
push edx
mov edx,OFFSET tempStr
call WriteString
pop edx
FORC fmt,<format>
IFIDNI <fmt>,<H> ;; формат H.
mov eax,ecx
mov ebx,MSHOWITSNAMETYPE
call WriteHexB
mWrite "h "
ENDIF
IFIDNI <fmt>,<N> ;; N
call Crlf
ENDIF
Стр. 339
ENDM
popad
ENDM
;---------------------------------------------------
; Альтернативное имя для mShowRegister:
ShowRegister TEXTEQU <mShowRegister>
;---------------------------------------------------
mShowRegister MACRO regName, regValue
LOCAL tempStr
.data
tempStr BYTE " ®Name=",0
.code
push eax
push edx
mov edx,OFFSET tempStr
call WriteString
pop edx
mov eax,regValue
call WriteHex
pop eax
ENDM
;------------------------------------------------------
mWrite MACRO text:REQ
LOCAL string
IFB <text>
EXITM
ENDIF
.data ;; Локальные данные.
string BYTE text,0
.code
push edx
mov edx,OFFSET string
call WriteString
pop edx
ENDM
;------------------------------------------------------
mWriteLn MACRO text:REQ
IFB <text>
EXITM
ENDIF
mWrite text
call Crlf
ENDM
;------------------------------------------------------
mWriteSpace MACRO count
LOCAL spaces
.data
IFB <count>
spaces BYTE ' ',0
ELSE
spaces BYTE count DUP(' '),0
ENDIF
.code
push edx
Стр. 340
отсутствует. 341
;------------------------------------------------------
mWriteStr MACRO buffer:REQ
IFB <buffer>
EXITM
ENDIF
push edx
mov edx,OFFSET buffer
call WriteString
pop edx
ENDM
.LIST
Стр. 341
Продолжение таблицы
ECHO Отображение сообщений или переключение режима повторения (эхо)
ENDLOCAL Окончание изменений локализации среды в командных файлах
ERASE Удаление одного или нескольких файлов
EXIT Выход из программы командного интерпретатора CMD.EXE
FC Сравнение нескольких файлов и отображение различий между ними
FIND Поиск текстовой строки в одном или нескольких файлах
FINDSTR Поиск строк в файлах
FOR Запуск определенных команд для каждого файла из набора файлов
FORMAT Форматирование диска для использования с Windows
FTYPE Отображение или модификация типов файлов, используемых в перечне соответствий
типов и расширений
GOTO Перенаправляет интерпретатор команд на строку с меткой в командной программе
GRAFTABL Отображает кодовую страницу для расширенного набора символов
HELP Отображает справочную информацию о командах
IF Оператор условия в командных программах
LABEL Создает, изменяет или удаляет метку тома
MD Создает каталог
MKDIR Создает каталог
MODE Конфигурация системных устройств
MORE Отображает последовательно экраны при выводе большого количества информации
MOVE Перемещает один или несколько файлов из одного каталога в другой
PATH Отображает или устанавливает пути для поиска файлов
PAUSE Приостанавливает выполнение командного файла и отображает сообщение
POPD Восстанавливает прежнее значение текущего каталога, сохраненного командой PUSHD
PRINT Выдача на печать текстового файла
PROMPT Изменение вида запроса
PUSHD Сохраняет текущий каталог перед изменением
RD Удаляет каталог
RECOVER Восстанавливает доступную информацию с запорченных дисков
REM Используется для записи комментариев в командных файлах или файлах CONFIG.SYS
REN Переименовывает файлы
RENAME Переименовывает файлы
REPLACE Заменяет файл
RMDIR Удаляет каталоги
SET Отображает, устанавливает или удаляет переменные окружения Windows
SETLOCAL Начало изменений по локализации в командном файле
SHIFT Сдвигает позиции заменяемых параметров в командных файлах
SORT Сортировка
START Запуск определенной команды или программы в отдельном окне
Стр. 342
отсутствует. 343
Окончание таблицы
SUBST Создание виртуального диска
TIME Отображение или установка системного времени
TITLE Установка заголовка для окна с сеансом CMD.EXE
TREE Графическое отображение структуры каталогов
TYPE Отображение содержимого текстового файла
VER Отображение версии Windows
VERIFY Запускает проверку правильности записи файлов на диск
VOL Отображает метку тома и серийный номер
XCOPY Копирует файлы и каталоги
Стр. 343
Приложение
Ответы на контрольные
вопросы
Глава 2
1. Процессор непосредственно производит вычислительные действия, которые ле&
жат в основе выполнения любой программы. Можно сказать, что процессор &&&& это
и есть компьютер, так как именно под определенный тип процессора разрабаты&
ваются все остальные узлы компьютера.
2. Процессор, шина, память, устройства ввода&вывода.
3. Операционная система представляет программу с набором команд, которая управ&
ляет всей аппаратной частью компьютера.
4. Для первых компьютеров операционная система разрабатывалась с учетом аппа&
ратной части. В настоящее время это взаимозависимый процесс и при разработке
операционной системы учитывается аппаратная часть, так же как и при разработ&
ке компьютера учитываются возможности операционной системы.
5. Потому что изменяется аппаратная часть.
6. Windows, Linux.
7. Для хранения выполняемых программ и результатов промежуточных вычислений.
8. Кэш&память необходима для согласования скорости работы процессора и опера&
тивной памяти. Без кэш&памяти невозможно получить высокое быстродействие.
9. Регистры находятся непосредственно в процессоре, поэтому их можно рассматри&
вать как сверхоперативную память, где сохраняются как необходимые при выпол&
нении программ данные, так и результаты промежуточных вычислений.
10. Аккумулятор, базовый регистр, счетчик, регистр данных.
11. CH, 8 бит.
12. Счетчик.
13. 32 бита.
14. При доступе к оперативной памяти происходит выход за пределы процессора, т.е. вы&
полняется последовательное обращение к шине и устройству памяти. Регистры на&
ходятся непосредственно в процессоре и при обращении к ним нет необходимости
выполнять дополнительные операции.
15. Защищенный режим позволяет использовать плоскую модель памяти, что повы&
шает эффективность выполнения больших программ. Все программы для опера&
ционной системы Windows должны использовать плоскую модель памяти.
16. Нет.
Стр. 344
отсутствует. 345
Глава 3
1. Да.
2. Можно.
3. 8, 16, 32.
4. От 0 до 2^16–1.
5. Как команды, так и данные размещаются в оперативной памяти в виде набора чи&
сел, но команды размещаются в участке памяти, выделенном для команд (сегмент
code), а данные располагаются в сегменте данных.
6. Основание системы счисления &&&& это число знаков, используемых для отображе&
ния чисел. В десятичной системе счисления это цифры: 1, 2, 3, 4, 5, 6, 7, 8, 9, 0.
В шестнадцатеричной системе счисления в качестве цифр используются следую&
щие символы: 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, F.
7. Записанные в шестнадцатеричной системе счисления числа хорошо воспринима&
ются человеком, с ними удобно работать и, самое главное, они легко преобразо&
вываются в двоичные числа и обратно.
8. Числа без знака используют все восемь бит для получения значения. Байт со зна&
ком использует только семь бит для получения значения, а старший восьмой бит
зарезервирован для знака, при этом 0 соответствует положительному значению,
а 1 &&&& отрицательному.
9. Дополнение до двух (двоичное дополнение) &&&& это число, которое получается из ис&
ходного числа после изменения всех единиц на нули, а нулей на единицы (инвер&
сия) и прибавления к полученному числу единицы.
10. Строка символов представляет в памяти последовательность байт. Например, чи&
словым кодам строки ‘‘ABC123’’ будет соответствовать последовательность значе&
ний 41h, 42h, 43h, 31n, 32h и 33h.
Стр. 345
11. Применяется двоичное представление числа, если числа используются для вычис&
лений. И наоборот, лучше хранить коды ASCII, если данные значения будут ис&
пользоваться для отображения символов на экране. Например, число 123 можно
сохранить в памяти двумя способами: как последовательность кодов ASCII для чи&
сел 1, 2 и 3 или как один байт со значением 123.
12. Константы &&&& это неизменные значения, обращение к которым происходит непо&
средственно. Переменные &&&& это значения, которые могут изменяться в процессе
выполнения программы. Для обращения к этим значениям используются ссылки.
13. Утверждения бывают двух типов: команды и директивы.
14. С помощью метода ‘‘сверху вниз’’.
15. Ставится задача и составляется проект программы. Вводятся команды программы
в компьютер с помощью редактора. Транслируется программа с помощью ассембле&
ра. Результат работы ассемблера преобразуется в исполняемый модуль с помощью
компоновщика. Выполнение программы. Проверка результатов и поиск ошибок.
16. TITLE, .386, .MODEL, .STACK, .DATA, .CODE, main PROC, main ENDP, END main.
17. Отладчик представляет собой программу, которая позволяет отображать на экране
значения необходимых переменных, получать состояние всех регистров и ячеек
памяти при пошаговом выполнении программы, вносить изменения в программу,
указывать точки останова и многое другое.
18. DOS работает в режиме эмуляции.
19. Нет.
Глава 4
1. /SUBSYSTEM:CONSOLE.
2. Функции Windows API, работающие с текстом, обычно имеют две версии: одна для
работы с 8&разрядными символами ASCII/ANSI (имена оканчиваются символом A)
и другая для работы с расширенным набором символов, включая Unicode (имена
оканчиваются символом W).
3. Windows NT, 2000 и XP.
4. Типы MASM: BYTE, PTR BYTE, DWORD, SDWORD, WORD.
5. Дескриптор является 32&разрядным целым числом без знака, которое уникально
идентифицирует объект.
6. Функция GetStdHandle возвращает дескриптор потока для консоли: вход, выход
или выход ошибок.
7. Прототип функции &&&& это предварительное описание заголовка функции, необхо&
димое для сообщения системе о наличии такой функции.
8. В подключаемом файле описываются прототипы функций.
9. Структуры данных, такие как COORD и SMALL_RECT. Структура COORD содержит
координаты экрана X и Y, измеряемые в символах. Структура SMALL_RECT содер&
жит координаты окна, также измеряемые в символах.
10. Функция ReadConsoleOutputCharacter.
11. Библиотека загрузочных модулей служит для хранения отлаженных процедур.
12. *.obj, *.lib.
Стр. 346
отсутствует. 347
13. Все переменные, константы или процедуры, объявленные внутри модуля, являют&
ся закрытыми по умолчанию. Такая концепция называется инкапсуляцией.
14. Функция CreateFile или создает новый файл или открывает уже существующий.
15. Байт с кодом символа и байт атрибутов.
Глава 5
1. Переменная является символическим именем для определенного места в памяти,
где размещены данные.
2. Директивы размещения данных используются для заполнения памяти.
3. Число значений, распределяемых с помощью одной директивы размещения дан&
ных, может быть ограничено только размером доступной памяти.
4. Строка символов может быть представлена переменной, которая указывает сме&
щение начала строки.
5. С помощью оператора DUP можно продублировать одну или несколько перемен&
ных после размещения их в памяти.
6. Указателем называется переменная, в которой сохраняется смещение другой пе&
ременной или подпрограммы.
7. Ассемблер переставляет байты слова, когда сохраняет его в памяти. Последний
байт слова будет иметь младший адрес в памяти.
8. Константа является неизменяемым значением, которому присвоено символиче&
ское имя.
9. Да.
10. С помощью директивы .MODEL можно выбрать одну из стандартных моделей па&
мяти для программ на языке ассемблера.
11. Tiny (тонкая), small (малая), medium (средняя), compact (компактная), large
(большая), huge (огромная), flat (плоская).
12. Сегменты являются строительными блоками программы и каждый из них предна&
значен для определенной цели.
13. Для создания участков памяти с однородными данными, т.е. программа, данные, стек.
14. При плоской модели памяти.
15. Оператор SIZE возвращает общее число байт, выделенных для переменной. Опе&
ратор SIZE рассчитывается как длина (LENGTH), умноженная на тип (TYPE).
16. Для использования соответствующего набора команд.
17. 184100h.
18. Когда с помощью директив DB, DW, DD или других создаются переменные, ассемб&
лер присваивает им определенные атрибуты (байт, слово, двойное слово и т.д.) в за&
висимости от их размера. Этот атрибут проверяется, когда переменная использует&
ся, и если ее тип не соответствует тому, который необходим, происходит ошибка.
19. Флаг переполнения устанавливается, когда в результате арифметической операции
получается число со знаком, превышающее максимально допустимое для операн&
да&получателя. Установленный флаг переполнения говорит о том, что в операнде&
получателе находится некорректное число.
Стр. 347
20. Регистры, непосредственные и косвенные операнды. Базовый и индексный опе&
ранды. Базо&индексные операнды. Базо&индексные операнды с дополнительным
смещением.
21. Косвенным операндом является регистр, который содержит смещение данных
в памяти.
22. В языке ассемблера выражением является комбинация операторов и операндов,
которые преобразовываются ассемблером в числовую константу.
23. Арифметические операторы, логические операторы.
Глава 6
1. Подпрограммы сервисных прерываний оказывают существенную помощь при ра&
боте с консолью, диском, принтером при вводе&выводе или контроле времени
и размещении памяти. Эти подпрограммы позволяют не вдаваться в детали про&
граммирования на уровне аппаратуры и обеспечивают работу программы на лю&
бых устройствах с процессорами архитектуры x86.
2. Указатель стека содержит адрес последнего элемента, помещенного в стек.
3. INT 21h (подпрограммы DOS). Подпрограммы для ввода&вывода, управления
файлами и управления памятью, называемые вызовами функций DOS.
4. Обобщающий термин прерывание используется для двух случаев: аппаратное пре&
рывание &&&& это сигнал от любого устройства системы для процессора, который по
этому сигналу должен обслужить данное устройство, и программное прерывание,
которое создается программами BIOS или DOS для вызова сервисных подпро&
грамм.
5. 0 && 255.
6. Ссылки на точки входа подпрограмм.
7. Подпрограммы прерываний BIOS работают быстрее.
8. Прикладные программы не могут непосредственно управлять аппаратными уст&
ройствами по той простой причине, что в разных системах это делается по&
разному. Поэтому не будет переносимости программ.
9. INT 16h.
10. При написании программ для 16&разрядного режима можно использовать преры&
вание INT 21h, предназначенное для вызова функций DOS. При этом может вы&
зваться около 90 различных функций.
11. 02h (вывод символа). Функция посылает символ на стандартное устройство вывода.
12. Скан&код &&&& это код цифровой клавиши, определяемый по месту расположения
клавиши на клавиатуре.
13. 0Dh.
14. Атрибут цвета, негативного отображения, мигания, подчеркивания, яркости.
15. 0Ah.
16. 00h.
17. В сторону младших адресов.
18. LIFO (last&in first&out) в переводе на русский означает ‘‘последним вошел &&&& пер&
вым вышел’’.
Стр. 348
отсутствует. 349
Глава 7
1. AND, OR, XOR, NOT, NEG, TEST, BT, BTR, BTC, BTS, CMP.
2. Операторы AND, OR, NOT и XOR выполняют поразрядные операции для целых чи&
сел во время трансляции.
3. Команда JMP &&&& это команда безусловного перехода, а команда JZ &&&& это услов&
ный переход.
4. Флаг нуля.
5. Флаги нуля и переполнения.
6. JCXZ.
7. Это условные переходы, основанные на общем сравнении. Процессор делает пе&
реход на основе анализа флагов нуля, переноса, паритета или регистров CX и ECX.
При этом по команде JL совершается переход, если оператор 1 меньше операто&
ра 2 (op1<op2). А по команде JNE совершается переход, если операторы не равны.
8. По команде JA совершается переход, если оператор 1 больше оператора 2 (op1>op2).
А по команде JAE совершается переход, если оператор 1 больше или равен опера&
тору 2 (op1>=op2).
9. Нет.
10. Флаг переноса.
Глава 8
1. Для подсчета суммы нужно сложить два числа в кодировке ASCII, но после сложе&
ния необходимо откорректировать результат. Для подобных операций в языке ас&
семблера можно использовать команду корректировки сложения AAA.
2. Команда SAL.
3. Команда SHR.
4. Команда SAR.
5. Бит, находившийся ранее во флаге переноса, теряется.
6. Произвести сдвиг на 4 бита влево.
7. Произвести сдвиг на 2 бита вправо.
Глава 9
1. myStruct STRUC
first DB ?
second DB ?
myStruct ENDS
2. mPushData MACRO
PUSH EAX
PUSH EBX
PUSH ECX
PUSH EDX
ENDM
3. К более высокой скорости работы.
Стр. 349
4. mWrite MACRO text
LOCAL string ;; Локальные метки.
.data
string BYTE text,0 ;; Определение строки.
.code
PUSH EDX
MOV EDX,OFFSET string
CALL Writestring
POP EDX
ENDM
5. Директива условия IFB позволяет выполнять проверку пропущенных аргументов.
6. Директива IFIDN <арг1>,<арг2> разрешает трансляцию, если оба аргумента рав&
ны. Сравнение можно сделать чувствительным к регистру, если используется ди&
ректива IFIDNI.
7. EXITM.
8. Директива IFIDNI сравнивает два идентификатора с учетом регистра, включая
имена параметров макроопределения. Директива IFIDN выполняет проверку без
учета регистра.
9. Директива IFDEF разрешает трансляцию, если указываемое вместе с ней имя оп&
ределено.
10. ENDIF.
11. mWriteLn MACRO text:=<" ">
mWrite text
CALL Crlf
ENDM
12. Оператор замены.
13. Оператор выделения символа.
Глава 10
1. Структура POINT определяет координаты X и Y точки экрана, измеренные в пик&
селях. Это ее свойство можно использовать при работе с графическими объектами,
окнами и при щелчках мыши.
2. Структура WNDCLASS определяет класс окна. Каждое окно в программе должно
принадлежать определенному классу и в каждой программе должен быть указан
класс основного окна. Этот класс должен быть зарегистрирован в операционной
системе перед тем, как произойдет обращение к основному окну.
3. IpfnWndProc DWORD ? ; Указатель на функцию
WinProc. style DWORD ? ; Опции стиля окна.
hlnstance DWORD ? ; Дескриптор текущей программы.
4. INVOKE MessageBox, hWnd, ADDR QuestionText,
ADDR QuestionTitle, MB_OK + MB_ICONQUESTION.
5. MB_OK и MB_YESNO.
6. Процедура WinMain получает дескриптор текущей программы, загружает пикто&
граммы и курсоры текущей программы; регистрирует класс основного окна програм&
мы и идентифицирует процедуру, которая будет выполнять обработку сообщений
Стр. 350
отсутствует. 351
для окна; создает основное окно; отображает и обновляет основное окно; начина&
ет циклический процесс приема и диспетчеризации сообщений.
7. Процедура WinProc принимает и обрабатывает все сообщения о событиях, отно&
сящиеся к окну. Большинство событий создает пользователь, щелкая мышкой
и перетаскивая объекты, нажимая клавиши клавиатуры и т.д. Процедура декоди&
рует каждое сообщение о событии, и если сообщение распознано, то обращается
к соответствующим функциям для выполнения задач, связанных с окном.
8. Процедура ErrorHandler будет вызываться, если возникнет сбой при регистра&
ции и создании основного окна программы.
9. Не зная языка ассемблера или не зная, как его использовать с языками высокого
уровня, программисты будут лишены мощного и гибкого инструмента програм&
мирования. Используя языки ассемблера и высокого уровня, можно совместить
отличный интерфейс пользователя и быстродействие разрабатываемой программы.
Стр. 351
Ïðåäìåòíûé óêàçàòåëü
A Входной буфер, 96
Выборка, 28
ASCII, 60 Вызов, 169
дальний, 268
B Выполнение, 28
Выражение, 146
BIOS, 37 константное, 62
L Д
LIFO, 167 Дальний указатель, 97
LOCAL, 234 Декодирование, 28
Дескриптор, 73; 98
А Директива, 63; 64
Дополнение
Адрес
до двух, 58; 192
возврата, 163
до единицы, 191
относительный, 49
физический, 49 Драйвер, 51
эффективный, 49 Драйвер устройства, 19
Альтернативное имя, 152
Аргумент, 168; 236 З
Арифметический сопроцессор, 141
Ассемблер, 18 Заголовочный файл, 98
Ассоциативность операторов, 150
И
Б Имя, 64
Базовое число, 54; 55 Инкапсуляция, 107
Байт, 54 Интерфейс прикладного
атрибутов, 179 программирования, 96
Бит, 54
Булевы команды, 188
К
В Ключевое слово, 64
Кодировка ASCII, 223
Ветвление, 153 Команда, 63
Виртуальная память, 48 Командная консоль, 68
Вложение процедур, 164 Командный файл, 84
Внутренний счетчик команд, 272
Комментарии, 231
Возврат, 169
Комментарий, 61
Вход
активный, 262
Компоновщик, 18; 65
неактивный, 262 Консоль, 272
Стр. 352
отсутствует. 353
Стр. 353
состояния и управления, 25 Т
флагов, 25; 27
Регистр флагов, 30 Таблица векторов прерываний, 175
Регистры, 24 Тип
Режим команды, 63
виртуальный, 22 Трассировка, 28
защищенный, 21; 48
реальный, 21; 48
системного управления, 48
У
Указатель, 132; 150
С Упакованные десятичные числа, 225
Управляющие клавиши, 265
Сегмент Условный переход, 195
данных, 70 Утверждение, 63
кодов, 70 Утилиты, 18
логический, 136
программный, 136
стека, 70 Ф
Сегментация, 48 Файл, 45
Симметричная многопроцессорная Фактор повторения, 185
обработка, 47 Функция, 169
Система команд, 20
Системная плата, 34
Скан&код, 265 Ш
Слово, 54 Шестнадцатеричные символы, 55
Смещение, 27; 49 Шина
переменной, 270 PCI, 35
Совместимость сверху вниз, 20 адреса, 24
Стек, 70; 166 внутренняя, 24
указатель, 167 данных, 24
Стековый фрейм, 168 системная, 24; 34
Страница, 181
Страничная организация памяти, 46
Строка
Э
символов, 131 Экранный буфер, 96
Структура, 227 Элемент управления, 73
Суперскалярная архитектура, 22 Эффективный адрес, 144
Счетчик сдвига, 209
Я
Язык ассемблера, 18
Стр. 354
Научнопопулярное издание
Стр. 355
ЯЗЫК АССЕМБЛЕРА
ДЛЯ ПРОЦЕССОРОВ INTEL
Стр. 356
МОДЕРНИЗАЦИЯ И РЕМОНТ ПК
17-е издание
Стр. 357
MICROSOFT WINDOWS VISTA
САМОУЧИТЕЛЬ
Стр. 358
MICROSOFT WINDOWS XP
САМОУЧИТЕЛЬ
2 ИЗДАНИЕ
О.А. Меженный Книга предназначена для
тех, кто хочет научиться
работать с Windows ХР. Здесь
читатель найдет необходимые
сведения об интерфейсе,
справочной системе и методах
использования окон и
программ этой операционной
системы, описание приемов
манипулирования файлами и
папками, инструкции по работе
с Интернет, электронной
почтой и мультимедиа,
а также рекомендации
настройке компьютера для
обеспечения работы на нем
нескольких пользователей.
Материал рассчитан прежде
всего на читателей, не
имеющих опыта использования
предыдущих версий этой
операционной системы. В то
же время книга может быть
полезна и тем, кому ранее
приходилось иметь дело с
предыдущими версиями
www.dialektika.com Windows и кто теперь
стремится освоить более новую
версию этой операционной
системы (Windows ХР SP2).
Стр. 359
JAVASCRIPT
КАРМАННЫЙ СПРАВОЧНИК
Кристиан Уэнц
Стр. 360
LINUX
КАРМАННЫЙ СПРАВОЧНИК
Стр. 361
ЯЗЫК
ПРОГРАММИРОВАНИЯ C
второе издание
Брайан Керниган,
Деннис Ритчи
Стр. 362
JAVA 2
БИБЛИОТЕКА ПРОФЕССИОНАЛА
ТОМ 1. ОСНОВЫ
СЕДЬМОЕ ИЗДАНИЕ
Стр. 363
JAVA 2
БИБЛИОТЕКА ПРОФЕССИОНАЛА
ТОМ II. ТОНКОСТИ ПРОГРАММИРОВАНИЯ
СЕДЬМОЕ ИЗДАНИЕ
К. Хорстманн В книге уделено внимание
Г. Корнелл таким сложным вопросам, как
поддержка распределенных
объектов; в частности,
читатель найдет информацию
о технологиях RMI, CORBA
и SOAP. Здесь продолжено
обсуждение Swing и AWT,
начатое в первом томе.
При разработке Java-
программ важную роль
играют компоненты JavaBeans;
им посвящена одна из глав
книги. Не забыты и вопросы
безопасности. В книге
подробно рассматриваются
загрузчики классов,
диспетчеры защиты, средства
шифрования, авторизации и
аутентификации. Благодаря
разнообразию и глубине
излагаемого материала книга,
несомненно, будет полезна
как начинающим, так и
опытным разработчикам.
www.williamspublishing.com
Стр. 364
DELPHI™
ДЛЯ “ЧАЙНИКОВ”
Стр. 365
HTML И CSS В ПРИМЕРАХ,
ТИПОВЫХ РЕШЕНИЯХ И ЗАДАЧАХ
Стр. 366
WINDOWS VISTA
ДЛЯ “ЧАЙНИКОВ”
Стр. 367
AUTODESK 3DS MAX 9
ДЛЯ “ЧАЙНИКОВ”
www.dialektika.com
Стр. 368