Текст
                    АССЕМБЛЕР
Язык и программирование
для
IBM PC


® IBM PC Assembly Language and Programming PETER ABEL British Columbia Institute of Technology Prentice-Hall International, Inc
Питер Абель АССЕМБЛЕР Язык и программирование для IBM PC Перевод с английского под реД. С. М. Молявко Киев «Век+» ♦ Москва «ЭНТРОП» 2005
ББК 32.973-01 А14 УДК 681.3-06 Scan+DjVu: AlVaKo 26/02/2025 Перевод с английского Питер Абель А14 Ассемблер. Язык и программирование для IBM PC: пер. с англ. - К.: Век+, М: ЭНТРОП, 2005. - 736 с. ISBN 966-7140-30-Х В книге содержатся сведения о функциональной структуре компьютера, машинном языке и языке Ассемблер, рассмотрены арифметические операции, вопросы работы с клавиатурой, экраном, дисковыми накопителями, подпрограммы, оверлеи и многое другое. Приведено большое количество справочной информации. Книгу можно использовать и как учебник, и как справочник. Изложение материала методически продумано, приводится большое количество примеров. Книга предназначена для студентов и преподавателей вузов, но также будет полезна в качестве справочного руководства и программистам, активно использующим язык Ассемблер в своей работе. ББК 32.973-01 Все права защищены законодательством Украины и международным законодательством об авторском праве. Никакая часть этой книги ни в каких целях не может быть воспроизведена в любой форме и любыми средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель или иные средства копирования или сохранения информации без письменного разрешения издательства. ISBN 0-13-030655-Х (англ.) ©Prentice-Hall International, Inc., 2001 ISBN 966-7140-30-Х (укр.) © Перевод на русский язык и оформление, ТОВ «Век+», 2005
Оглавление Введение 13 Часть I. Основы аппаратного и программного обеспечения PC Глава 1. Основные свойства аппаратуры PC 19 Введение 19 Биты и байты 19 Двоичная система счисления 21 Шестнадцатеричные числа 25 Таблица ASCII 27 Компоненты PC , 27 Внутренняя память 31 Сегменты и адресация 33 Регистры 35 Аппаратные прерывания 41 Ключевые моменты 42 Вопросы и упражнения 43 Глава 2. Адресация и выполнение инструкций 45 Введение 45 Операционная система 45 Загрузка посредством BIOS 46 Загрузчик системных программ 47 Стек 48 Выполнение инструкций и адресация 51 Операнды инструкций 53 Защищенный режим 54 Ключевые моменты 55 Вопросы и упражнения для повторения 55 Глава 3. Память компьютера и отслеживание исполнения инструкций 57 Введение 57 Использование программы DEBUG 57 Просмотр областей памяти 59 Пример на машинном языке I: использование непосредственных (immediate) данных 62 Пример на машинном языке II: использование ранее определенных данных 67 Программа на языке Ассемблера 71 Использование инструкции INT г 73 Оглавление 5
Использование оператора PTR 75 Ключевые моменты 77 Вопросы и упражнения 77 Часть II. Основы программирования на языке Ассемблер Глава 4. Требования к написанию программ на языке Ассемблер 82 Введение 82 Особенности языка Ассемблер 83 Директивы определения сегментов 91 Упрощенные директивы сегментов 95 Инициализация в защищенном режиме 98 Определение типов данных 98 Вопросы и упражнения 109 Глава 5. Трансляция, компоновка и исполнение программ .t 111 Введение 111 Подготовка программы к преобразованию и исполнению 111 Двухпроходный ассемблер *. 118 Компоновка объектного файла 120 Листинг с перекрестными ссылками 123 Диагностика ошибок 124 Счетчик положения в ассемблере 125 Написание программ .СОМ 126 Ключевые моменты : 130 Вопросы и упражнения 131 Глава 6. Символьные инструкции и адресация 134 Введение 134 Обзор набора символьных инструкций 134 Инструкции перемещения данных t 138 Основные арифметические инструкции 141 Операции блочного перемещения 141 Инструкция INT 143 Режимы адресации 144 Префикс переопределения сегмента 149 Адреса типа NEAR и FAR 149 Выравнивание адресов данных 150 Ключевые моменты 150 Вопросы и упражнения 151 6 Ассемблер. Язык и программирование для IBM PC
Глава 7. Логические операции и управляющие структуры 154 Введение 154 Адреса типа Short, Near и Far 155 Инструкция JMP ..... 156 Инструкция LOOP 158 Регистр флагов - Flags 160 Инструкция CMP 162 Инструкции условного перехода 162 Вызов процедур 167 Взаимодействие исполняемой программы со стеком 170 Булевы (логические) операции 175 Сдвиг битов 178 Циклический сдвиг битов 182 Проектирование программы 185 Ключевые моменты 187 Вопросы и упражнения 187 Часть III. Работа с клавиатурой и видео Глава 8. Введение в управление видео и клавиатурой 191 Введение 191 Свойства экрана 192 Установка курсора 193 Очистка экрана 193 Функция 09Н прерывания INT 21Н для вывода на экран 194 Функция ОАН прерывания INT21H для ввода с клавиатуры 197 Функция 02Н прерывания INT 21Н для вывода на экран 203 Дескрипторы файлов 204 Функция 40Н прерывания INT21H для вывода на дисплей 205 Функция 3FH прерывания INT 21Н для ввода с клавиатуры 206 Ключевые моменты 208 Вопросы и упражнения 209 Глава 9. Видеосистема 211 Введение 211 Компоненты видеосистемы 212 Видеорежимы 214 Атрибуты 215 Функции прерывания INT 10H 218 Использование графических режимов 241 Непосредственный вывод на экран 247 Символы ASCII для рамок и меню 250 Ключевые моменты 251 Вопросы и упражнения 252 Оглавление 7
Глава 10. Работа с клавиатурой 254 Введение 254 Области данных клавиатуры в BIOS 255 Функции прерывания INT 21Н для ввода с клавиатуры 257 Функции прерывания INT 16Н для ввода с клавиатуры 258 Дополнительные функциональные клавиши и скан-коды 261 Прерывание BIOS INT 09H и буфер клавиатуры 268 Ключевые моменты 273 Вопросы и упражнения 273 Часть IV. Обработка данных Глава 11. Обработка строковых данных 276 Введение 276 Свойства строковых операций 276 Инструкция MOVS: перемещение строковых данных 279 Инструкция LODS: загрузить строку 281 Инструкция STOS: сохранение строки 282 Программа: использование LODS и STOS для редактирования данных 283 Инструкция CMPS: сравнение строк 288 Инструкция SCAS: просмотр строки 290 Альтернативное кодирование строковых инструкций 291 Повторение фрагмента 292 Ключевые моменты 293 Вопросы и упражнения 294 Глава 12. Арифметика: обработка двоичных чисел 296 Введение 296 Обработка знаковых и беззнаковых двоичных чисел 297 Сложение и вычитание двоичных чисел 299 Умножение двоичных чисел 305 Деление двоичных чисел 313 Ключевые моменты 321 Вопросы и упражнения 322 Глава 13. Арифметика: работа с данными форматов ASCII и BCD 324 Введение 324 Данные в десятичном формате 325 Обработка ASCII-данных 326 Обработка упакованных двоично-десятичных чисел 334 Преобразование ASCII-чисел в двоичную форму 336 Преобразование двоичных данных в ASCII-формат 338 8 Ассемблер. Язык и программирование для IBM PC
Сдвиги и округление результатов 339 Ключевые моменты 346 Вопросы и упражнения 347 Глава 14. Определение и обработка таблиц 349 Введение 349 Определение таблиц 349 Прямая адресация элементов таблицы 351 Поиск в таблицах 355 Поиск в таблице при помощи сравнения строк 358 Инструкция XLAT (преобразовать) 360 Сортировка элементов таблицы 364 Таблицы адресов 368 Двумерные таблицы и массивы 369 Ключевые моменты , 373 Вопросы и упражнения 373 Часть У. Усовершенствованные операции ввода/вывода Глава 15. Работа с мышью 376 Введение 376 Основные операции при работе с мышью 378 Программа: вывод позиции указателя мыши 380 Дополнительные операции работы с мышью 384 Программа: использование мыши для работы с меню 388 Ключевые моменты 393 Вопросы и упражнения 393 Глава 16. Устройство и принципы работы дисковых накопителей 395 Введение 395 Характеристики дисковых накопителей 395 Системная область диска и область данных 399 Загрузочная запись 401 Структура директорий 402 Таблица размещения файлов 403 Работа с файлами на диске 410 Ключевые моменты 411 Вопросы и упражнения 412 Глава 17. Дисковые накопители: запись и чтение файлов 413 Введение 413 Дескрипторы файлов 414 Возвращаемые коды ошибок 414 Файловые указатели 415 Оглавление g
Создание файлов 416 Чтение файлов с диска 421 Произвольный доступ к файлам 426 Ключевые моменты 437 Вопросы и упражнения 438 Глава 18. Дисковые накопители: функции поддержки дисков и файлов прерывания INT 21H 440 Введение 440 Операции для работы с дисками 441 Функции для работы со структурой директорий и FAT 455 Функции для работы с файлами на дисках 458 Ключевые моменты 470 Вопросы и упражнения 470 Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 472 Введение 472 Байт состояния BIOS 473 Основные функции прерывания INT 13H 474 Другие операции INT 13H для работы с диском 479 Ключевые моменты 485 Вопросы и упражнения 486 Глава 20. Устройства и методы печати 487 Введение 487 Общие символы управления принтерами 487 Функция 40Н прерывания INT21H: печать символов 488 Специальные символы управления печатью 498 Функции печати BIOS прерывания INT 17H 499 Ключевые моменты 501 Вопросы и упражнения 502 Часть VI. Специальные темы Глава 21. Определение и использование макросов 504 Введение 504 Простые определения макросов 505 Использование параметров в макросах 507 Использование комментариев в макросах 509 Вложенные макросы 511 Макродирективы 512 Ключевые моменты 522 Вопросы и упражнения 523 10 Ассемблер. Язык и программирование для IBM PC
Глава 22. Связь с подпрограммами 524 Введение 524 Директива SEGMENT 525 Вызовы внутри сегмента 527 Межсегментные вызовы 528 Атрибуты PUBLIC и EXTERN 529 Использование EXTRN и PUBLIC для точки входа 531 Определение сегмента кода как PUBLIC 534 Использование упрощенных сегментных директив 537 Передача параметров подпрограмме 539 Инструкции ENTER и LEAVE 543 Компоновка программы на языке C/C++ с программой на ассемблере 545 Ключевые моменты 550 Вопросы и упражнения 550 Глава 23. Загрузка программы и оверлеи 552 Введение 552 Префикс сегмента программы 552 Область верхней памяти 556 Стратегия выделения памяти 557 Загрузчик программ 559 Выделение и освобождение памяти 564 Загрузка или выполнение подпрограммы (функции) 566 Оверлеи программ 570 Резидентные программы 575 Ключевые моменты 580 Вопросы и упражнения 581 Часть VII. Обзорная информация Глава 24. Области данных BIOS, прерывания и порты 583 Введение 583 Процесс загрузки 583 Область данных BIOS 584 Обработчики прерываний 589 Прерывания BIOS 591 Функции прерывания INT 21H 595 Порты -. 603 Ввод/вывод строк 606 Генерация звука 607 Ключевые моменты 609 Вопросы и упражнения 610 Оглавление 11
Глава 25. Операторы и директивы .611 Введение 611 Спецификаторы типов 611 Операторы 611 Директивы 618 Глава 26. Набор инструкций PC 639 Введение 639 Обращение к регистрам 639 Байт режима адресации 641 Набор инструкций 644 Часть VIII. Приложения Приложение А. Преобразования чисел между шестнадцатеричной и десятичной системами счисления 682 Приложение В. Набор символов ASCII 685 Приложение С. Программа DEBUG 687 Приложение D. Зарезервированные слова 696 Приложение Е. Трансляция и компоновка программ 698 Приложение F. Клавиатурные скан-коды и ASCII-коды 705 Предметный указатель 710 12 Ассемблер. Язык и программирование для IBM PC
Введение Сердце персонального компьютера - микропроцессор, выполняющий арифметические, логические и вспомогательные операции, необходимые для решения задач. Первые микропроцессоры появились в конце 1960-х годов, когда исследователи и разработчики создали интегральную схему (ИС), содержавшую различные электронные компоненты на едином кристалле полупроводника. В начале 70-х годов фирма Intel предложила процессор 8008 - представитель первого поколения микропроцессоров. (Обычно в качестве первого микропроцессора упоминается Intel 4004. - Прим.ред.) К 1974 году на базе 8008 был создан процессор 8080 - популярный микропроцессор второго поколения, получивший широкое распространение. В 1978 году Intel создала процессор третьего поколения 8086, олицетворявший собой существенный прогресс с точки зрения технологии. Процессор 8088 - разновидность 8086 со слегка упрощенной структурой - позволял организовывать более эффективное взаимодействие с устройствами ввода/вывода, наиболее распространенными в то время. В 1981 году процессор 8088 был выбран фирмой IBM для разрабатывавшегося ей персонального компьютера. В дальнейшем архитектура процессоров 8086 расширялась и дополнялась, и на базе этой архитектуры создавались все новые и новые процессоры - 80286, 80386, 80486, Pentium, Pentium II и т.д. Каждое семейство процессоров имеет собственный уникальный набор инструкций, используемый для выполнения операций, например, для ввода с клавиатуры, вывода данных на экран и выполнения арифметических вычислений. Этот набор инструкций (машинный язык) слишком сложен для понимания и непосредственного использования при создании программ. Поэтому для процессоров существуют языки ассемблеров, представляющие инструкции машинных языков в более доступной для человеческого понимания символьной форме. Уровни языков программирования Любой язык программирования относится к одному из следующих уровней. • Машинный язык - представляет собой совокупность машинных инструкций, непосредственно исполняемых процессором. Последовательности таких инструкций составляют операционную систему и встроенное в аппаратную часть компьютера программное обеспечение. • Низкоуровневые языки программирования (ассемблеры) - предназначаются для конкретных семейств процессоров. Символьные инструкции этих языков непосредственно соответствуют инструкциям машинных языков, и программы на ассемблере легко транслируются в машинный код. Введение 13
• Высокоуровневые языки, например, C++ или Visual BASIC, - созданы для того, чтобы можно было сосредоточить усилия программистов на решении прикладных задач, не отвлекая их на аппаратные особенности конкретных машин. Команды языков высокого уровня обычно преобразуются в сложные последовательности машинных инструкций. Преимущества языка ассемблера Знание и использование языка ассемблера дает следующие преимущества: • ясен способ взаимодействия программы с операционной системой, процессором и BIOS; • доступен выбор способа представления и хранения данных в оперативной памяти и на внешних накопителях; • можно видеть, как процессор получает инструкции, как инструкции выполняются и как обрабатываются данные; • доступно непосредственное управление внешними устройствами; Кроме того, язык ассемблера полезен благодаря следующим свойствам: • программа, написанная на ассемблере, значительно компактнее и быстрее выполняется, чем написанная на языке программирования высокого уровня; • ассемблер дает возможность решать узкоспециальные задачи, которые трудно, если вообще возможно, решить на языке высокого уровня; • хотя большинство программистов разрабатывают новые приложения с использованием языков высокого уровня, программы, которые легче сопровождать и ' модифицировать, или части программ, критичные к скорости выполнения, весьма часто пишутся на ассемблере; • резидентные программы (находящиеся в памяти во время выполнения других программ) и обработчики прерываний (выполняющие, например, операции ввода/вывода) почти всегда создаются на языке ассемблера. Для изучения ассемблера необходимы: • доступ к IBM-совместимому компьютеру (любой модели); • операционная система Windows 95/98 (подходит и Windows Me/NT/2000/XP - прим.ред.) или DOS и умение работать в ней; в простой операционной системе, вроде DOS, дающей больше возможностей для свободного экспериментирования, гораздо проще изучить тонкости языка; после этого можно переходить к среде Windows; • копия транслятора с языка ассемблера (например, фирмы Microsoft, Borland или SLR Systems). 14 Ассемблер. Язык и программирование для IBM PC
Последующее не обязательно, но также может быть полезно при изучении ассемблера: • знание языков программирования - такое знание может облегчить для вас понимание некоторых моментов; • знание электроники и схемотехники; книга содержит всю информацию об аппаратуре, которая потребуется вам для программирования на ассемблере. Цель книги Чтобы помочь читателям в изучении программирования на ассемблере, в начале книги описываются основные компоненты аппаратной части компьютера и языка программирования Ассемблер, а в дальнейшем постепенно добавляются все новые инструкции и возможности. Кроме того, текст включает разбор примеров программ. Примеры, содержащие ассемблерные программы, сделаны максимально простыми для понимания, хотя опытный программист мог бы решить поставленные в примерах задачи более эффективно, но менее понятно. В программах не используются макроинструкции (рассмотренные в главе 21); хотя профессиональные программисты широко их применяют. Использование макроинструкций в книге, подобной этой, помешало бы изучению, собственно, языка программирования. После изучения основ вы можете перейти к более профессиональным приемам. Порядок изучения Книгу можно использовать и как учебник, и как справочник. Чтобы сделать затраты времени и ресурсов минимальными, внимательно изучайте каждую главу, стараясь разобраться в материале, даже если он не понятен "с первого захода". Используйте примеры программ и заставьте их работать на вашей машине. Кроме того, выполняйте упражнения, приведенные в конце каждой главы. Первые восемь глав содержат основной материал по языку ассемблера. После изучения этих глав можно перейти к главам 9, 11, 12, 14, 15, 16, 20, 21 или 22. Главы с 24 по 26 носят справочный характер. Следующие главы связаны между собой и посвящены смежным темам: • с 8 по 10 (работа с клавиатурой и экраном); • 12 и 13 (арифметические операции); • с 16 по 19 (работа с дисковыми накопителями); • 22 и 23 (подпрограммы и оверлеи). Введение 15
После завершения изучения этой книги вы будете: • знать принципы работы аппаратной части компьютера; • понимать машинные коды инструкций и шестнадцатеричный код; • разбираться в процессе трансляции, компоновки и выполнения программ; • проектировать программы на языке ассемблера, работающие с клавиатурой и экраном, выполняющие арифметические операции, преобразование между двоичными кодами и ASCII, поиск в таблицах и сортировку, а также ввод/вывод на дисковые устройства; • уметь отслеживать выполнение машинных инструкций при отладке программ; • готовить собственные макроинструкции для облегчения создания программ; • компоновать раздельно оттранслированные программы в единый исполняемый модуль. Изучение языка ассемблера и написание работающих программ - интересное, но непростое занятие. Однако, ожидаемые результаты способны оправдать затраты времени и ресурсов. Замечания по пятой редакции Пятая редакция книги (в оригинале, т.е. на английском языке) содержит значительное количество изменений по сравнению с предыдущими редакциями: • более подробно описаны процессоры Pentium; • приведено большее количество примеров и упражнений; • упрощено введение в операции прерываний; • существенно реорганизованы и пересмотрены объяснения в тексте; • приведено больше материала о защищенном режиме работы, передаче параметров, использовании стека, режимах адресации, видеоподсистеме и функциях обработчика прерываний INT 10H, работе с массивами, подпрограммах и портах; • пересмотрен и расширен набор вопросов в концах глав. Те, кто работал с четвертой редакцией книги, заметят, что содержание главы 7 (о программах типа .СОМ) совмещено в этой редакции с главой 5. Кроме того, глава 21 была разделена: материал по работе с мышью выделен в отдельную главу 15, а остальной материал главы 21 (порты, строковый ввод/вывод и звук) совмещен с информацией о BIOS и программных прерываниях в главе 24. 16 Ассемблер. Язык и программирование для IBM PC
Ответы на некоторые вопросы (естественно, на английском языке - прим.ред.) и программы, приведенные в книге, доступны в Интернет по адресу http://cwx.prenhall.com/bookbind/pubbooks/abel/. Благодарности Автор благодарен за помощь и сотрудничество всем, кто внес свой вклад, предложения, замечания и исправления. Введение 1J 2 —5-741
Часть I Основы аппаратного и программного обеспечения Глава 1. Основные свойства аппаратуры PC Глава 2. Адресация и выполнение инструкций Глава 3. Память компьютера и отслеживание исполнения инструкций
Глава 1 Основные свойства аппаратуры PC Цель: разъяснить основные принципы функционирования микрокомпьютеров и организации программ. Введение Написание программы на языке ассемблера требует знаний об аппаратуре компьютера (его архитектуре) и деталях его набора инструкций. В этой главе рассматриваются основные элементы - биты, байты, регистры, память, процессор и шина данных. Оставшаяся часть книги развивает затронутые здесь темы. Основные составляющие аппаратной части компьютера - процессор, память и регистры (регистры - специальные компоненты процессора, предназначенные для хранения адресов и данных). Внешние устройства компьютера (периферия) - это клавиатура, дисплей, дисковый накопитель (винчестер), привод компакт- дисков и другие устройства. Программное обеспечение состоит из операционной системы, различных прикладных программ и файлов данных, хранящихся на диске. Для выполнения (или запуска) программа копируется с диска во внутреннюю (оперативную) память компьютера. (Именно объем этой памяти подразумевается, когда, например, говорят, что у компьютера 64 мегабайта памяти). Процессор выполняет инструкции программы, используя регистры для хранения исходных данных и результатов требуемых арифметических, логических операций и адресации. Биты и байты Наименьшая единица информации, хранимая в компьютере, - бит. Бит может> быть в одном из двух состояний: выключено - это состояние обозначается 0, и включено - это состояние обозначается 1. Один единственный бит не содержит много информации, но большое их количество может хранить весьма большой ее объем. Байты Группа из девяти связанных битов называется байт. Эта группа представляет собой единицу объема информации, хранимой в оперативной памяти и Глава 1. Основные свойства аппаратуры PC -J 9 2*
внешних устройствах (например, на диске). Каждый байт содержит восемь бит для хранения данных и один -для хранения данных проверки четности. (В некоторых случаях, в частности, при передаче информации по электронным сетям, под байтом понимают группу из восьми связанных битов, при этом для проверки четности используется восьмой бит или проверка четности вообще не ведется. С другой стороны, т.к. проверка четности реализуется на аппаратном уровне, то в программировании под байтом также понимают группу из восьми связанных битов. - Прим.ред.) 0 0 0 0 0 биты данных 0 0 0 1 четность Восемь бит данных составляют основу двоичной арифметики и представления таких символов, как буква А и символ звездочки (*). Восемь бит данных, содержащихся в байте, позволяют представить до 28 (256) различных сочетаний значений включено/выключено, начиная от 00000000 до 11111111. Например, букве А соответствует сочетание 01000001, а символу * - 00101010, хотя запоминать эти сочетания нет необходимости. Согласно правилу контроля четности, количество бит, находящихся в единичном состоянии в одном байте, должно всегда быть нечетным. Поскольку сочетание 00101010 содержит три единичных бита, то девятый бит, содержащий значение четности, устанавливается процессором в 0. Поскольку сочетание 01100000 содержит два бита во включенном состоянии, то бит четности в этом байте будет установлен в 1. Когда инструкция ссылается на определенный байт в памяти, процессор проверяет выполнение правила четности для этого байта. Если оно не выполняется, система считает, что данные были повреждены, и выводит сообщение об ошибке проверки четности. Эта ошибка может быть результатом ошибки аппаратуры или электрической помехи; в любом случае, это редкое событие. Как компьютер "узнает", что сочетание бит 01000001 соответствует букве А? Когда вы нажимаете клавишу А на клавиатуре, компьютер передает сигнал от этой клавиши в память и устанавливает байт (в расположении вашей программы) в значение 01000001. Вы можете перемещать содержимое этого байта в памяти по собственному усмотрению или вывести его на дисплей или принтер в виде буквы А. Обработка бита четности - автоматическая функция аппаратуры, и мы больше не будем ее касаться. Таким образом, далее в книге везде подразумевается, что байт содержит восемь бит, которые нумеруются справа налево от 0 до 7, как показано ниже для буквы А: Содержимое битов (соответствует букве А): Номер бита: 01000001 76543210 20 Часть I. Основы аппаратного и программного обеспечения PC
Связанные байты Программа может рассматривать группу из одного или более байт как единицу данных, например, время или расстояние. Группа байт, хранящих определенное значение, обычно называется полем или элементом данных (data item). Процессор также поддерживает определенные форматы данных, удобные для его внутренней архитектуры. • Слово (word): 2 байта (16 бит). • Двойное слово (doubleword): 4 байта (32 бита). • Счетверенное слово (quadword): 8 байт (64 бита). • Параграф (paragraph): 16 байт (128 бит). • Килобайт (Kilobyte, KB, КБ, Кбайт, К): число 2ю равно 1024, и именно это количество байт соответствует килобайту, поэтому 640 килобайт памяти равны 640x1024=655360 байт. • Мегабайт (Megabyte, MB, МБ, Мбайт, М): 220 (1048576) байт. • Гигабайт (Gigabyte, GB, ГБ, Гбайт, G): 230 (1073741824) байт. Биты в слове обозначаются числами 0 до 15 справа налево, как показано ниже для букв PC, причем буква Р (01010000) хранится в старшем байте, а С (01000011) - в младшем байте. Содержимое бита {PC) : Номер бита: Каждый байт в памяти имеет уникальный адрес. Первый байт в начале памяти имеет адрес 0, второй - 1 и так далее. Двоичная система счисления Поскольку компьютер может различать только состояния выключено и включено (1 и 0), он работает с системой счисления, основанной на числе 2 (двоичной). Фактически, само слово бит (английское bit) - это сокращение от слов "binary digit" - в переводе "двоичная цифра". Набор битов может представлять любое числовое значение. Значение двоичного числа определяется наличием единичных битов и их положением в последовательности. Как и в десятичных числах, позиции разрядов соответствуют возрастающим степеням (но двух, а не десяти) справа налево. В следующем восьмиразрядном числе все биты установлены в 1. 01010000 15 14 13 12 11 10 9 8 01000011 76543210 Глава 1. Основные свойства аппаратуры PC 21
Значение бита: Значение позиции Номер бита: (вес разряда): 1 128 7 1 64 6 1 32 5 1 16 4 1 8 3 1 4 2 1 2 1 1 1 0 0 128 1 64 0 32 0 16 0 8 0 4 0 2 1 1 Крайний правый бит хранит число 1 (2°), следующий за ним слева - 2 (21), следующий - 4 (22) и так далее вплоть до крайнего левого, хранящего число 128 (27). Число 11111111 соответствует 1+2+4+8+16+32+64+128 = 255 (или 28- 1). Число, хранящееся в определенном установленном в 1 разряде, называется его весом. Вычислим значение двоичного числа 01000001: Значение бита: Вес разряда (бита): Путем вычислений получим, что этому двоичному числу соответствует десятичное число 65. Однако вы также можете вспомнить, что значение байта 01000001 соответствует букве А. Действительно, биты 01000001 могут представлять как число 65, так и букву А. Например: • если программа производит над данными арифметические операции и соответственно представляет их, то значение 01000001 представляет число 65; • если программа использует данные для описательных целей (в виде текста, например), то значение 01000001 представляет символ А. Когда вы начнете программировать, вы увидите это различие более отчетливо, поскольку вы определяете каждый элемент данных с какой-то определенной целью, т. е. определяете числовые объекты для арифметических операций и текстовые (описательные) данные для вывода. Практически два способа использования данных редко служат источником недоразумений. Двоичное число не ограничивается 8-ю разрядами. 16 (или 32)-разрядный процессор эффективно обрабатывает числа с разрядностью 16 (или 32) разряда. 16 бит позволяют представлять числа от 0 до 216-1 (65535), а 32 - от 0 до 232-1 (4294967295). Двоичная арифметика Поскольку микрокомпьютер выполняет арифметические действия только над двоичными числами, программист на ассемблере должен быть знаком с двоичным форматом чисел и двоичным сложением. Следующие четыре примера иллюстрируют простейшие операции сложения битов. 22 Часть I. Основы аппаратного и программного обеспечения PC
0 + 0 0 +1 1 +1 1 +1 +1 10 11 Последние два примера иллюстрируют перенос бита в следующий слева (старший) разряд. Давайте попробуем сложить двоичные числа 01000001 и 00101010. На этот раз отнесемся к ним как к числам (65 и 42 соответственно), а не как к символам (А и *): Десятичная 65 +42 107 Двоичная 01000001 +00101010 01101011 Чтобы убедиться, что сумма правильна (01101011 равно 107), просуммируйте значения весов единичных битов. Попытайтесь просуммировать в двоичной и десятичной формах числа 60 и 53. Десятичная 60 +53 113 Двоичная 00111100 +00110101 01110001 Снова убедитесь в том, что результат правилен (01110001 равно 113). Отрицательные двоичные числа Двоичное число со знаком (применяемое в арифметических операциях) считается положительным, если его левый (старший) бит равен 0, и отрицательным, если он равен 1. Однако представление двоичного числа как отрицательного не сводится к простой установке старшего бита в 1, ведь если для 01000001 (65) записать 11000001, то это число не равно -65. Вместо этого необходимо записать число в комплементарной {дополняющей) форме. Чтобы получить из числа его отрицательный эквивалент, необходимо проинверти- ровать значения битов и добавить единицу. Например, попытаемся найти двоичное дополнение к числу 01000001 (65): Число +65 Инвертируем биты Добавляем 1 Результат(число -65) 01000001 10111110 +1 10111111 Двоичное число со знаком отрицательно, когда его старший бит равен 1. И процессор обрабатывает его соответствующим образом. Однако если вы сложите веса единичных битов, чтобы выяснить десятичный эквивалент отрицательного двоичного числа, вы не получите правильного результата (10111111 не равно 65). Глава 1. Основные свойства аппаратуры PC 23
Для определения модуля отрицательного двоичного числа найдите его двоичное дополнение (инвертируйте биты и добавьте 1), а затем сложите веса единичных битов: Число -65 Инвертируем биты Добавляем 1 Результат (число +65) 10111111 01000000 +1 01000001 Чтобы убедиться, что данная процедура работает, проверим, равна ли сумма чисел 10111111 и 01000001 (-65 и 65) нулю: Десятичная Двоичная -65 10111111 +65 +01000001 00 (1)00000000 Обратите внимание на возникшее переполнение: в сумме 8 битов равны 0, но старший (9-й) бит равен 1. Однако поскольку в примере предусмотрена и запись в знаковый бит, и чтение из него, результат считается правильным. Для выполнения вычитания в двоичной форме преобразуйте вычитаемое число в его двоичное дополнение и сложите его с уменьшаемым. Допустим, требуется отнять 42 от 65. Двоичное представление числа 42 есть 00101010, а его двоичное дополнение (т.е. -42) - 11010110. Сложив 65 и -42, мы получим число 23, как показано ниже. Десятичная Двоичная 65 01000001 +(-42) +11010110 23 (1)00010111 Результат - число 23. Снова заметим, что операции установки 9-го разряда в 1 и 0 корректны. Если представление двоичного дополнения не слишком ясно, задайтесь следующим вопросом: какое число вы должны добавить к двоичному 00000001, чтобы получить 00000000? В случае десятичных чисел ответ равен -1. Двоичное дополнение к 00000001 есть число 11111111, и если сложить 1 (00000001) и -1 (11111111), мы получим 0 (00000000): Десятичная Двоичная 1 00000001 +(-1) 11111111 0 (1)00000000 24 Часть I. Основы аппаратного и программного обеспечения PC
Игнорируя перенос единицы в старший разряд, убеждаемся, что число 11111111 (в двоичном представлении) равно -1 (в десятичном). Следующая таблица иллюстрирует значения небольших отрицательных и положительных двоичных чисел: 3 2 1 0 -1 -2 -3 00000011 00000010 00000001 00000000 11111111 11111110 11111101 Между прочим, сумма весов нулевых битов в отрицательном двоичном числе равна его модулю, уменьшенному на единицу; чтобы найти абсолютное значение отрицательного двоичного числа, просуммируйте веса его нулевых битов и добавьте к сумме 1. Только что изложенный материал по двоичной арифметике и отрицательным двоичным числам особенно пригодится вам при изучении глав 12 и 13, посвященных арифметике. Шестнадцатеричные числа Хотя байт может содержать любую из 256 комбинаций битов, многие из них нельзя представить на экране в виде символов, например, байты, соответствующие операциям табуляции, перевода строки, клавишам Escape или Enter. Поэтому разработчики компьютеров создали метод краткой записи двоичных чисел, в котором байт делится на половинки и каждая из них записывается отдельно. Представьте, что вы хотите просмотреть содержимое четырех последовательных байтов памяти (двойного слова) в памяти компьютера. Согласно только что изложенному методу, это будет выглядеть так: 0101 1001 5 9 ООН 0101 3 5 1010 1001 10 9 1100 1110 12 14 Поскольку десятичные числа 10, 12 и 14 имеют два разряда, расширим систему счисления так: 10 = А, 11 = В, 12 = С, 13 = D, 14 = Е и 15 = F. Такая система включает в себя "цифры" от 0 до F (всего таких "цифр" будет шестнадцать) Глава 1. Основные свойства аппаратуры PC 25
и называется шестнадцатеричной (hexadecimal, или hex). С ее применением только что рассмотренное двойное слово примет такой вид: Двоичное Шестнадцатеричное 0101 5 1001 9 ООН 3 0101 5 1010 А 1001 9 1100 С 1110 Е ' На следующем рисунке показано соответствие между двоичными, десятичными и шестнадцатеричными числами. Двоичное 0000 0001 0010 ООН 0100 - 0101 ОНО 0111 Десятичное 0 1 2 3 4 5 6 7 Шестнадцатеричное 0 1 2 3 4 5 6 7 Двоичное 1000 1001 1010 1011 1100 1101 1110 1111 Десятичное 8 9 10 11' 12 13 14 15 Шестнадцатеричное 8 9 А В С D Е F Рис. 1.1. Системы счисления Числа, представленные в шестнадцатеричной системе счисления, широко применяются в языке ассемблера. Листинг программы на этом языке показывает в шестнадцатеричной форме записи все адреса, инструкции машинного кода и содержимое констант данных. Для отладки программ можно использовать программу DEBUG, также показывающую все адреса и содержимое байтов в шестнадцатеричной записи. Вы скоро привыкнете к работе с шестнадцатеричными числами. Помните, что за числом F следует число 10, соответствующее десятичному числу 16. Далее показаны простейшие примеры сложения шестнадцатеричных чисел. 7 +3 А 6 +7 D F + 1 10 F +F IE 10 + 30 . 40 38 18 50 FF + 1 100 Заметим также, что 40 в шестнадцатеричной форме соответствует 64 в десятичной, 100 соответствует 256, а 1000 соответствует 4096. В шестнадцатеричной системе счисления 38 + 18 = 50, а 8 + 8 = 10. Чтобы показать шестнадцатеричное число в программе на ассемблере, добавляется буква Н сразу же после записи числа, например, 25Н (шестнадцатеричное 25 , т. е. десятичное 37). Язык ассемблера требует, чтобы шестнадцатеричное число всегда начиналось с цифры 0—9, поэтому число В8Н необходимо записывать в виде 0В8Н. В этой книге шестнадцатеричное значение предваряется 26 Часть I. Основы аппаратного и программного обеспечения PC
буквами hex или завершается буквой Н (например, hex 3D или 3DH); двоичное число при записи предваряется словом binary или завершается буквой В (например, binary 01001100 или 01001100В); десятичное число записывается без особых пометок (например, 264). Исключения встречаются в местах, где система счисления очевидна из контекста. Приложение А содержит описание методов преобразования из шестнадца- теричной системы счисления в десятичную и наоборот. Таблица ASCII Данные в компьютере могут быть представлены в цифровой форме (двоичные данные, над которыми проводятся арифметические операции) или алфавитно- цифровой форме (символы и описательные данные). Вторая форма данных всегда используется для ввода с клавиатуры и вывода на экран или принтер. Чтобы привести данные к общей (единой) форме представления, разработчики микрокомпьютеров выработали стандарт ASCII (American Standard Code for Information Interchange - Американский стандартный код для обмена информацией). Так как байт содержит 8 бит, PC использует 8-разрядный код ASCII, содержащий 28 или 256 символов, многие из которых есть на клавиатуре. Например, вы уже видели ASCII-код для буквы А: 01000001 (41Н). К счастью, вам не нужно запоминать всю кодовую таблицу ASCII; в случае необходимости вы можете обратиться к приложению В за полным списком кодов, а в главе 8 показано, как вывести большинство этих символов на экран. Компоненты PC Основная часть PC - системная (или материнская) плата. На ней установлены процессор (или процессоры - для многопроцессорных систем - прим.ред.), сопроцессоры (точнее, математический сопроцессор - для некоторых компьютеров до 386-й серии - и контроллеры - прим.ред.), основная память, разъемы и слоты для внешних устройств. Соединения и слоты дают доступ к таким устройствам, как постоянные запоминающие устройства (ПЗУ, английская аббревиатура - ROM), основная память (оперативное запоминающее устройство - ОЗУ, английская аббревиатура - RAM), жесткий диск, привод CD-ROM, дополнительная память, видеоустройства, клавиатура, мышь, параллельные и последовательные порты, синтезаторы звука и кэш-память. Процессор использует быструю кэш-память для уменьшения потребности в обращениях к медленной основной памяти. Шина, состоящая из проводников на системной плате, связывает компоненты между собой. Она обеспечивает передачу данных между процессором, памятью Глава 1. Основные свойства аппаратуры PC 27
и внешними устройствами. Например, когда программа требует чтения данных с внешнего устройства хранения, процессор определяет адрес ячейки в памяти, куда эти данные должны быть доставлены, и передает его адресной шине. Устройство памяти передает данные шине данных и сообщает процессору, что данные готовы. После этого процессор переносит данные с шины в память по выбранному адресу. Блок питания преобразует стандартные 220 В сетевого переменного тока в постоянный ток с напряжением, необходимым для питания компонентов компьютера. Типичная потребляемая мощность компьютера - порядка 250—300 Ватт. Процессор Процессор - это мозг PC. Его также называют центральным процессорным устройством (ЦПУ, английская аббревиатура - CPU). В ШМ PC и совместимых с ними машинах используются процессоры архитектуры х86, названной так в память о ее родоначальнике - процессоре '8086. Процессоры этой архитектуры различаются по быстродействию, разрядности шин и регистров, объему адресуемой памяти и другим параметрам. Внутренний тактовый генератор синхронизирует и контролирует все операции процессора. Основная единица скорости - тактовая частота - измеряется в мегагерцах (миллионах циклов в секунду, сокращенно МГц, английская аббревиатура MHz) или гигагерцах (ГГц, английская аббревиатура GHz). 1 ГГц = 1000 МГц. Далее следует краткое описание процессоров ШМ PC. 8088. Имеет 8-разрядную шину данных и 16-разрядные внутренние регистры, может адресовать до 1 МБ основной памяти (20-разрядная шина адреса). Хотя регистры могут обрабатывать два байта данных одновременно, внешняя шина может пропускать только один. Такая архитектура была выбрана для удешевления систем на базе этого процессора ценой некоторого уменьшения производительности. Процессор работал на тактовых частотах от 4,77 МГц в так называемом реальном режиме (real mode) с выполнением только одной программы одновременно с действительной ("real") адресацией сегментных регистров. 8086. Аналогичен 8088, но имеет 16-разрядную внешнюю шину данных и, соответственно, несколько большую производительность. 80286. Быстрее, чем описанные выше, имеет расширенный набор инструкций и может адресовать до 16 МБайт памяти (24-разрядная шина данных). Этот процессор (и последующие) может работать в реальном (real) или защищенном (protected) режимах. Защищенный режим позволяет таким оцерационным системам, как Windows, работать в многозадачном режиме (имитировать 28 Часть I. Основы аппаратного и программного обеспечения PC
одновременное выполнение более одной задачи путем быстрого переключения между ними) и защищать задачи от влияния друг на друга. 80386. Имеет 32-битовые регистры и 32-разрядные внешние шины данных и адреса. Как следствие, может адресовать до 4 ГБ памяти. Кроме реального и защищенного режима, поддерживает так называемый виртуальный {virtual) режим, позволяющий воспринимать участки диска как дополнительную оперативную память. Благодаря этой возможности существенно увеличивалась доступная одновременно запущенным программам память (хотя быстродействие такой виртуальной памяти значительно ниже, чем обычной). Был первым процессором, у которого ядро могло работать на большей частоте, чем системная шина, за счет чего также увеличивалось быстродействие. 80486. Также имеет 32-разрядные регистры и шины данных и адреса. Кроме того, имеет встроенный в чип математический сопроцессор (как и все последующие процессоры) и быстродействующую кэш-память (присоединенную к процессорной шине), в которой хранятся копии недавно использованных инструкций и данных. К этим копиям процессор может получить доступ быстрее, чем к тем данным, которые находятся в основной памяти. Pentium. Имеет 32-разрядные регистры, 64-разрядную шину данных, и раздельную кэш-память для инструкций и данных. Фирма Intel применила название Pentium, поскольку имена, в отличие от чисел, могут быть объектом патентования. Суперскалярная архитектура позволяет процессору Pentium обрабатывать и выполнять более одной инструкции за такт. Pentium II. Имеет двойную независимую шину (Dual Independent Bus) для обеспечения отдельного канала связи с кэш-памятью. Предыдущие процессоры связывались с кэш-памятью по системной шине, что вызывало задержки. Celeron. В отличие от Pentium II не имеет встроенного в чип кэша второго уровня. За счет этого снижаются быстродействие и цена. Pentium III и Pentium IV. Дальнейшее развитие процессоров Intel, работают на более высоких тактовых частотах, т.к. реализуется на лучшей технологической базе (разрешение при фотолитографии). Также имеют двойную независимую шину, встроенную в чип кэш-память второго уровня и 64- разрядную шину данных. (Кроме перечисленных процессоров, при производстве ЮМ РС-совместимых компьютеров используются процессоры и других фирм, например, AMD (K5, Кб, Athlon, Duron), Cyrix СЗ и другие. - Прим.ред.). Процессоры вплоть до 80486 имели однопотоковую {single-stage pipeline) архитектуру, не позволявшую им выполнять более одной инструкции в один такт. Процессоры Pentium и более поздние могут разделять инструкцию на одновременно выполняемые действия, использующие различные структуры. Глава 1. Основные свойства аппаратуры PC 29
У процессора Pentium глубина разделения равна 5, у процессоров Pentium II и Celeron- 12. Одна из проблем, стоящих перед разработчиками новых процессоров, такова: поскольку процессор работает намного быстрее, чем память (например, рабочая частота ядра процессора - 500 МГц, а памяти - 100 МГц), то в процессор включаются дополнительные возможности динамического выполнения (предсказание ветвления и другие). Предсказание ветвления, например, позволяет процессору просматривать код программы на несколько шагов вперед и решать, какие инструкции выполнять первыми. Исполняющее устройство и устройство шинного интерфейса Как показано на рисунке, процессор делится на два логических устройства: устройство исполнения (УИ) и шинный интерфейс (ШИ). УИ ответственно за выполнение инструкций, а ШИ - за доставку УИ данных и инструкций для обработки. УИ содержит арифметико-логическое устройство (АЛУ, английская аббревиатура - ALU), устройство управления (УУ) и регистры. Это позволяет выполнять арифметические и логические операции. УИ:Устройство исполнения ШИ: Шинный интерфейс Управление программой АН ВН СН DH 1 ^ 1 | BL I CL ! DL SP ВР SI DI АЛУ: арифметико-логическое УУ: Устроуство управления Регистр флагов Указатель инструкций CS DS SS ES Шина Очередь исполнения Рис. 1.2. Структура процессора 30 Часть I. Основы аппаратного и программного обеспечения PC
Основная функция ШИ - управление шиной, сегментными регистрами и очередью исполнения {instruction queue). ШИ управляет шинами, передающими данные в У И, память и внешние устройства ввода/вывода. Сегментные регистры управляют адресацией памяти. Еще одна функция ШИ - предоставлять доступ к инструкциям. Поскольку инструкции исполняемой программы находятся в памяти, ШИ должен получить к ним доступ и поместить их в очередь исполнения, размер которой меняется в зависимости от типа процессора.. Это свойство позволяет ШИ предсказывать и загружать необходимые инструкции заранее, так что в очереди всегда есть инструкции, готовые к исполнению. УИ и ШИ работают параллельно, и ШИ всегда на один шаг опережает УИ. УИ сообщает ШИ, когда ему необходим доступ к данным в памяти или к устройствам ввода/вывода. УИ также запрашивает инструкции для исполнения из очереди в ШИ. Самая верхняя инструкция - исполняемая в данный момент, а пока УИ занято выполнением данной инструкции, ШИ извлекает следующую инструкцию из памяти. Выполнение инструкций и их получение из памяти перекрываются во времени, за счет чего ускоряется работа. Программисты не могут получить доступ к этим элементам процессора. Внутренняя память Два типа внутренней памяти PC - это постоянная память (ПЗУ) и оперативная память (ОЗУ). Последняя также называется памятью с произвольным доступом. Байты в памяти нумеруются последовательно, начиная с 0, и каждый байт имеет свой уникальный адрес. На приведенном ниже рисунке показана структура внутренней памяти PC с процессором 8086 в качестве простого примера. Из первого мегабайта памяти 640 килобайт - основная оперативная память, большая часть которой доступна для непосредственного использования. Таблица векторов прерываний (interrupt vector table), размещенная в нижней области памяти, описана в главах 3 и 24, а видеопамять (video display area) - в гл. 9. Объем и назначение Начало (десятичное) 960 К 768 К 640 К Адрес (шестнадцатеричный) F0000 СОООО А0000 00000 64 К основное ПЗУ системы 192 К - расширенная область (ПЗУ) 128 К - видеопамять (ОЗУ) ~6 4 ~0~ ~К~ "(ОЗУ) ' Область BIOS Таблица векторов прерываний Рис. 1.3. Структура адресного пространства Глава 1. Основные свойства аппаратуры PC 31
ПЗУ ПЗУ состоит из специальных чипов, информация из которых, будучи однажды записана, в дальнейшем только считывается. В силу этого свойства записанные в таких чипах данные и инструкции не могут быть изменены. ПЗУ BIOS (Basic Input/Output System - основная система ввода/вывода) начинается с адреса 768 К и отвечает за устройства ввода/вывода, например, контроллер винчестера (жесткого диска). ПЗУ, начинающееся с адреса 960 К, отвечает за основные функции компьютера, например, самопроверку при включении, рисование точек при выводе графики и загрузку операционной системы с диска. Когда вы включаете компьютер, ПЗУ управляет различными проверками и загрузкой специальных данных системы с диска в ОЗУ. ОЗУ Программиста в основном интересует оперативная память (ОЗУ - оперативное запоминающее устройство). В нее можно записывать и из нее можно считывать информацию. Память ОЗУ доступна в качестве "рабочего пространства" для временного хранения данных и выполнения программ. Когда вы включаете компьютер, часть операционной системы загружается с диска в ОЗУ. Выполняемая программа находится в памяти и обычно выводит информацию на экран, принтер или диск. После завершения программы операционная система может загрузить на ее место другую программу. Выключение компьютера приводит к потере данных в ОЗУ, но не влияет на ПЗУ. Поэтому, если вы редактировали документ, вы должны сохранять его на диске перед выключением компьютера. При дальнейшем обсуждении ОЗУ обозначается общим термином память. Адресация данных в памяти В зависимости от модели процессор может обращаться к одному или более байтам памяти. Рассмотрим десятичное число 1315. Шестнадцатеричное представление этого числа 0529Н занимает два байта или одно слово в памяти. Слово состоит из старшего (более значащего) байта 05 и младшего (менее значащего) байта 29. Процессор хранит данные в памяти в инверсном порядке (reverse-byte sequence), то есть младший байт - в ячейке с меньшим адресом, а старший байт - в ячейке с большим адресом. Например, процессор передает значение 0529Н из регистра в память по адресам 04А26Н и 04А27Н так: 32 Часть I. Основы аппаратного и программного обеспечения PC
регистр 05 29 29 05 адрес 04А2 6Н адрес 04А27Н Младший значимый байт Старший значимый байт Процессор подразумевает, что данные в памяти хранятся именно в инверсном порядке, и обрабатывает их соответствующим образом. Когда процессор извлекает данные из памяти, он переставляет байты, помещая их в регистр в виде hex 05 29. Хотя этот процесс полностью автоматизирован, вы должны помнить о нем при написании и отладке программ на ассемблере. При программировании на ассемблере необходимо ясно представлять себе разницу между адресом ячейки памяти и ее значением. В только что рассмотренном примере значение ячейки с адресом 04А26Н равно 29Н, а с адресом 04А27Н-05Н. Есть две основные схемы адресации. 1. Абсолютный адрес, например, 04А26Н, представляющий собой 20-разрядное двоичное число, прямо указывающее на определенную ячейку памяти. 2. Адрес в системе сегмент:смещение, состоящий из начального адреса сегмента и значения смещения. Следующий параграф детально описывает эту схему. Сегменты и адресация Сегменты - это специальные области, определяемые в программе для хранения кода, данных и так называемого стека. Сегмент начинается на границе параграфа, то есть на адресе, нацело делящемся на 16 (hex 10). Хотя сегмент может располагаться в памяти практически где угодно и в реальном режиме может быть размером до 64 К, он требует ровно столько места, сколько нужно программе для работы - размещения данных и обрабатывающих их инструкций. Когда инструкция загружает адрес сегмента в сегментный регистр, эта операция автоматически изменяет четыре крайних справа нулевых бита. Вы можете определить любое количество сегментов. Для обращения к определенному сегменту необходимо только изменить значение в соответствующем сегментном регистре. В реальном режиме используются три основных сегмента - кодовый {code segment, CS), данных (data segment, DS), стека (stack segment, SS). Глава 1. Основные свойства аппаратуры PC 3 — 5-741 33
CS. Содержит инструкции, подлежащие выполнению. Обычно первая выполняемая инструкция находится в начале сегмента, и операционная система обращается к этому адресу для начала выполнения программы. Как видно из имени, регистр CS адресует сегмент кода. DS. Содержит данные программы, константы и рабочую область. Регистр DS адресует сегмент данных. SS. Содержит любые данные и адреса, которые программе нужно временно сохранить или использовать в собственных "вызываемых" подпрограммах. Регистр SS адресует сегмент стека. Границы сегмента Сегментный регистр имеет разрядность 16 и хранит стартовый адрес сегмента. На следующем рисунке показана графическая структура регистров CS, DS и SS и их связь с сегментами кода, данных и стека (регистры и сегменты не обязательно расположены в показанном на рисунке порядке). Другие сегменты - это ES (extra segment, дополнительный сегмент), а в процессорах 80386 и более поздних - FS и GS, предоставляющие дополнительные сегменты для хранения данных. SS DS CS Адрес Адрес Адрес Сегментные регистры ^~ 1 ^ 1 >- Сегмент стека Сегмент данных Сегмент кода Перераспределение г памяти * Память Рис. 1.4. Сегменты и сегментные регистры Как упоминалось выше, сегмент начинается с границы параграфа, то есть с адреса, нацело делящегося на 16 (ЮН). Рассмотрим сегмент данных, начинающийся с адреса 038Е0Н. Поскольку в этом и всех других случаях самый правый шестнадцатеричный разряд всегда нулевой, разработчики компьютера решили, что хранить его не нужно. Поэтому адрес 038ЕОН хранится в сегментном регистре 34 Часть I. Основы аппаратного и программного обеспечения PC
как 038ЕН, т.е. с отброшенными четырьмя младшими битами. При необходимости далее в книге такой адрес будет записываться в виде 038Е[0]Н. Смещения в сегментах В программе все ячейки памяти в сегменте нумеруются относительно стартового адреса сегмента. Расстояние в байтах от начала сегмента до любой другой ячейки называется смещением {offset или displacement). В реальном режиме двухбайтовое смещение может изменяться от 0000Н до FFFFH, то есть от 0 до 65535. Поэтому первый байт в сегменте имеет адрес 0, второй - адрес 1, и так далее до последнего байта с адресом 65535. Чтобы обратиться к любой ячейке памяти в сегменте, процессор сочетает адрес сегмента, хранящийся в сегментном регистре, со смещением требуемого байта, то есть его расстоянием от начала сегмента в байтах. Рассмотрим сегмент данных, начинающийся с адреса 038Е0Н. Регистр DS содержит значение 038Е[0]Н, т. е. адрес начала сегмента данных. Если инструкция обращается к ячейке со смещением 0032Н в сегменте данных, то требуемый адрес получается сложением смещения с адресом, хранящимся в регистре DS, как показано ниже. Адрес в регистре DS: 03 8E0H Смещение: + 0032Н Действительный адрес: 03 912Н Поэтому действительный адрес, на который сошлется инструкция, равен 03912Н. Заметьте, что программа может содержать один или более сегментов, которые могут начинаться где угодно в памяти и располагаться в любой последовательности. Адреса сегментов и смещения будут интересовать вас только иногда, поскольку процессор и язык ассемблера оперируют ими самостоятельно, но вы должны понимать их смысл. Регистры Регистры процессора позволяют выполнять арифметические операции, адресацию памяти и управлять исполнением инструкций. Программа обращается к регистрам по имени, например, CS, DS, SS. Биты в регистре нумеруются справа налево, начиная с 0: ...15, 14, 13, 12, 11, 10, 9, 8, 7, б, 5, 4, 3, 2, 1, 0 Глава 1. Основные свойства аппаратуры PC з* 35
Сегментные регистры Сегментный регистр адресует данные в области памяти, называемой текущим сегментом. Процессоры фирмы Intel, применяемые в ЮМ РС-совместимых компьютерах, обладают различными возможностями адресации. Адресация 8086/8088 Эти процессоры имеют 16-разрядные сегментные регистры, работающие в реальном режиме. Поскольку сегмент начинается с границы параграфа (адреса, нацело делящегося на 16 (ЮН)), то четыре крайних справа бита адреса равны 0. Как говорилось ранее, адрес сегмента хранится в сегментном регистре, и процессор отбрасывает 4 правых бита, так что nnnn[0]H преобразуется в nnnnH. Например, адрес 02С30Н преобразуется в 02СЗН. Процессор подразумевает, что крайние 4 бита справа равны 0, так что эффективная разрядность сегментных регистров равна 20. Максимальное значение адреса сегмента равно FFFF[0]H и соответствует десятичному 1048560. Адресация 80286 В реальном режиме процессор 80286 выполняет адресацию так же, как и 8086/8088. В защищенном режиме его 24-разрядная схема адресации дает максимальный адрес FFFFF[0]H и позволяет адресовать вплоть до 16 МБ памяти. Адресация 80386/80486/Pentium и последующих В реальном режиме эти процессоры адресуют память так же, как и 8086/8088. В защищенном режиме эти процессоры используют 48 бит для адресации, что позволяет адресовать сегменты до 4 ГБ. 16-битные сегментные регистры служат для выборки 32-битного адреса сегмента из памяти. Эти процессоры имеют шесть сегментных регистров: CS, DS, SS, ES, FS, GS. Регистр CS Содержит начальный адрес сегмента, содержащего код программы. Сумма содержимого этого регистра со значением смещения в указателе инструкций (instruction pointer, IP) указывает адрес инструкции, которая должна быть передана для исполнения. Для обычных целей программирования прямо ссылаться на этот регистр не нужно. Регистр DS Содержит начальный адрес сегмента данных программы. Инструкции используют этот адрес для поиска данных. Этот адрес, сложенный со смещением, заданным в инструкции, дает ссылку на определенную ячейку памяти, содержащую требуемые данные. 36 Часть I. Основы аппаратного и программного обеспечения PC
Регистр SS Позволяет реализовывать в памяти стек, который программа использует для временного хранения адресов и данных. Регистр SS хранит начальный адрес сегмента стека программы. Этот адрес в сумме со смещением, хранимым в указателе стека (stack pointer, SP), указывает на текущее слово в стеке. Для обычных целей программирования прямо ссылаться на этот регистр не нужно. Регистр ES Используется некоторыми строковыми (работающими с символьными данными) операциями для управления адресацией памяти. В этом контексте сегментный регистр ES связан с регистром индекса (index, DI). Если программа требует использования сегмента ES, вы должны проинициализировать регистр соответствующим значением начального адреса. Регистры FS и GS Дополнительные сегментные регистры, впервые введенные в процессоре 80386 для средств хранения. Регистры указателей Регистры указателей - это 32-разрядные регистры ELP, ESP, EBP; их младшие 16 разрядов - соответственно IP, SP и ВР. Указатель инструкций (instruction pointer, IP) 16-разрядный регистр IP содержит смещение адреса следующей инструкции, подлежащей исполнению. IP связан с CS (как CS:IP), т.е. регистр IP указывает текущую инструкцию внутри текущего кодового сегмента. Обычно к IP не обращаются непосредственно в программах, но его значение можно изменять, например, в программе DEBUG с целью тестирования выполнения программы. В процессоре 80386 впервые появился расширенный указатель инструкций - 32-разрядный регистр ЕП\ В следующем примере CS содержит 39В4[0]Н. IP содержит 514Н. Чтобы определить следующую подлежащую исполнению инструкцию, процессор суммирует адрес в CS со смещением в IP: Адрес начала сегмента в CS 3 9B40H Смещение в IP + 514H Адрес следующей выполняемой инструкции ЗА054Н При выполнении каждой инструкции процессор меняет значение смещения в регистре IP, и этот регистр постоянно указывает на следующую подлежащую исполнению инструкцию. Глава 1. Основные свойства аппаратуры PC 37
Регистры SP (stack pointer - указатель стека) и ВР (base poiner - базовый указатель) связаны с регистром SS и позволяют системе работать с данными в стековом сегменте. Процессор автоматически работает с этими регистрами. Указатель стека (SP) 16-разрядный регистр SP хранит значение смещения, которое в сочетании с регистром SS (SS:SP) указывает на текущее слово в стеке. В процессоре 80386 введен в оборот 32-разрядный расширенный регистр указателя стека - ESP. В следующем примере регистр SS содержит адрес сегмента 4ВВЗ[0]Н, а регистр SP - смещение 412Н. Для вычисления адреса текущего слова в стеке процессор суммирует значения регистров SS и SP: Адрес начала сегмента в SS 4BB30H Смещение в SP + 412H Адрес следующей выполняемой инструкции 4BF42H Базовый указатель (ВР) 16-разрядный регистр ВР обрабатывает такие ссылочные параметры, как данные и адреса, передаваемые программой через стек. Процессор сочетает адрес в SS со смещением в ВР. ВР может также сочетаться с DI и SI как базовый регистр специальной адресации. В процессоре 80386 впервые появился расширенный 32-разрядный регистр базового указателя - ЕВР. Регистры общего назначения 32-разрядные регистры общего назначения - это ЕАХ, ЕВХ, ЕСХ и EDX; их младшие 16 разрядов обозначаются соответственно АХ, ВХ, СХ, DX. Каждый из последних в свою очередь состоит из двух байтов, например, АХ - из старшего АН и младшего AL. Это свойство позволяет обращаться к разным частям регистра для обработки байтов, слов и двойных слов. Следующие ниже инструкции на языке ассемблера иллюстрируют запись нулей в регистры АХ, ВН и ЕСХ. MOV АХ, 00 MOV ВН, 00 MOV ЕСХ, 00 Регистр АХ АХ - основной аккумулятор, используется в арифметических операциях и операциях ввода/вывода. Например, инструкции сложения и умножения предполагают использование регистра АХ. Кроме того, некоторые инструкции преобразуются в более эффективный машинный код, если они используют именно регистр АХ. 8-разрядные АН и AL являются левой и правой частями АХ, а сам АХ - это правая часть регистра ЕАХ. 38 Часть I. Основы аппаратного и программного обеспечения PC
АН AL -< АХ ->- •ЕАХ >- Регистр ВХ ВХ называют базовым регистром, поскольку это единственный регистр общего назначения, который можно использовать в качестве указателя (индекса) для расширения адресации. Другое частое применение этого регистра - для вычислений. ВХ может также быть использован в сочетании с DI или SI, как базовый регистр для специальной адресации. 8-разрядные ВН и BL являются левой и правой частями ВХ, и сам ВХ - это правая часть регистра ЕВХ. ВН BL ^ -ВХ ->- ^ ЕВХ >- Регистр СХ СХ используется как регистр-счетчик. Он может содержать число повторений цикла в программе или величину, на которую нужно сдвинуть биты влево или вправо. СХ можно также использовать для различных вычислений. 8-разрядные СН и CL являются левой и правой частями СХ, и сам СХ - это правая часть регистра ЕСХ. СН CL ^ СХ ->- ^ ЕСХ >- Регистр DX DX - регистр данных. Он используется в некоторых операциях ввода/вывода, а операции умножения и деления больших чисел используют АХ и DX в паре. 8-разрядные DH и DL являются левой и правой частями DX, и сам DX - это правая часть регистра EDX. DH DL ^ DX ->- ^ EDX >- Регистры общего назначения доступны для сложения и вычитания 8-, 16- и 32-разрядных двоичных чисел: Глава 1. Основные свойства аппаратуры PC 39
MOV EAX, 337 ; поместить число 337 в ЕАХ (двойное слово) ADD CX, AX ; прибавить АХ к СХ (слово) SUB BL, AL ; отнять AL от BL (байт) Регистры индекса Регистры индекса - это 32-разрядные ESI и EDI. Их младшие слова (16- разрядные регистры) - соответственно, SI и DI. Эти регистры доступны для индексированной адресации и используются для достижения определенных целей при сложении и вычитании. Регистр SI 16-разрядный регистр индекса источника (source index register) используется в некоторых строковых (символьных) операциях. В этом случае SI связан с регистром DS. Процессор 80386 впервые представил расширенный 32-разрядный регистр ESI. Регистр DI 16-разрядный регистр индекса назначения (destination index register) также используется в строковых операциях. В этом случае DI связан с регистром ES. Процессор 80386 впервые представил расширенный 32-разрядный регистр EDI. Регистр флагов Биты 32-разрядного регистра Eflags указывают на состояние различных процессов и компонентов системы. Младшие 16 разрядов регистра Eflags - это, собственно, регистр Flags. 9 из его шестнадцати разрядов указывают на текущее состояние компьютера и результаты выполнения инструкции. Многие инструкции, например, арифметические и сравнения, изменяют состояние этих битов (флагов), и последующие инструкции могут их проверять для определения дальнейших действий. Далее кратко описаны эти флаги. OF (overflow, переполнение). Указывает на переполнение старшего разряда после арифметической операции. DF (direction, направление). Определяет правое или левое направление движения при сравнении или перемещении строковых (символьных) данных. IF (interrupt, прерывание). Указывает, будут ли внешние прерывания, например, ввод с клавиатуры, обработаны или проигнорированы. TF (trap, остановка). Разрешает работу процессора в пошаговом режиме. Программы-отладчики, такие как DEBUG, устанавливают этот флаг для 40 Часть I. Основы аппаратного и программного обеспечения PC
того, чтобы вы могли детально отслеживать изменения содержимого регистров и других параметров после каждой выполненной инструкции. SF (sign, знак). Содержит знак результата последней арифметической операции (0 - положительный, 1 - отрицательный). ZF (zero, ноль). Указывает результат последней арифметической операции (О - ненулевой результат, 1 - нулевой результат). AF (auxiliary carry, вспомогательный перенос). Содержит бит, переносимый из 3-го бита в 4-й бит в специализированных арифметических операциях. PF (parity, четность). Указывает на число единичных битов в результате последней операции. Четное число битов устанавливает положительную четность, нечетное - отрицательную. CF (carry, перенос). Содержит перенос из старшего разряда после последней арифметической операции. Содержит также значение последнего бита после операции сдвига и циклического сдвига (вращения). Флаги в регистре Flags расположены в порядке, показанном ниже (запоминать это нет необходимости). ODITSZ A P С Флаг: Номер бита: 15 14 13 12 11 10 9 8 7 б 5 4 3 2 1 О Флаги OF, SF, ZF и CF наиболее важны для программирования на ассемблере арифметических операций и операций сравнения, a DF - для строковых операций. Некоторые другие флаги используются для внутренних нужд, в основном связанных с защищенным режимом работы. Глава 7 содержит более подробную информацию по регистру Flags. Аппаратные прерывания Определенные события могут вынудить процессор приостановить выполнение текущей программы и заставить его отреагировать на эти события. Обычно эти события нормальны и предсказуемы, как, например, запрос на ввод с клавиатуры. Процессор прерывает текущую операцию, вызывает BIOS- подпрограмму для обработки запроса с клавиатуры и возвращается к выполнению прерванной программы. Другие прерывания могут быть критическими, например, останов по делению на ноль, вынуждающий систему прервать работу. Другой тип прерываний - программные прерывания. Их вызывает программа, например, для вывода информации на экран. Так же, как и в предыдущем случае, система получает управление, обрабатывает прерывание и возвращается к выполнению прерванной программы. Глава 1. Основные свойства аппаратуры PC 41
Ключевые моменты • Процессор воспринимает только данные в двоичной форме (в виде битов, каждый из которых может находиться только в одном состоянии из двух: включено (1) или выключено (0)) и производит вычисления только с двоичными числами. • Значение двоичного числа определяется значениями его отдельных битов. Например, число 1101 равно (справа налево) 1*2° + 0 * 21 + 1*22 + 1*23 = 1 + 4 + 8 = 13. • Отрицательное двоичное число представляется в виде двоичного дополнения к такому же по модулю положительному числу. Для получения двоичного дополнения к числу необходимо инвертировать его биты и добавить единицу к результату. • Элементарная ячейка памяти компьютера - байт, состоящий из восьми битов данных и одного для проверки четности. Два последовательных байта составляют слово, четыре - двойное слово. • Шестнадцатеричный формат - сокращенная форма записи, представляющая отдельными цифрами группы из четырех битов. Шестнадцатеричные цифры 0—9 и А—F соответствуют двоичным числам от 0000 до 1111. • Символьный формат использует ASCII для представления данных. • Мозг PC - процессор - хранит данные в памяти в виде слов и двойных слов, записанных в обратном порядке. • В компьютере ест;ь два типа внутренней памяти - постоянная (ПЗУ) и оперативная (ОЗУ). • Программа на языке ассемблера состоит из одного или более сегментов: сегмента стека - для хранения адресов возвращения, сегмента данных - для хранения рабочих данных и промежуточных результатов, и сегмента кода - для хранения исполняемых инструкций программы. Ячейки в сегменте выбираются по сумме адреса начала сегмента и смещения. • Регистры CS, DS и SS управляют адресацией сегментов кода, данных и стека, соответственно. • Регистры CS:IP содержат адрес следующей исполняемой инструкции в виде сегментхмещение. • Регистры указателей SP и ВР связаны с регистром SS и позволяют системе работать с данными в стеке. • Регистры АХ, ВХ, СХ и DX формируют рабочее ядро системы. Они состоят из "левых" (старших) байтов и "правых" (младших). АХ (основной аккумулятор) 42 Часть I. Основы аппаратного и программного обеспечения PC
используется для выполнения ввода/вывода и большей части арифметических операций. ВХ может использоваться как регистр индекса для расширенной адресации. СХ используется при вычислениях и в качестве специального счетчика. DX также используется для расчетов. ЕАХ, ЕВХ, ЕСХ, EDX - расширенные 32-разрядные версии регистров общего назначения АХ, ВХ, СХ, DX. • Регистры индекса SI и DI доступны для расширения адресации и для использования в сложении и вычитании. Эти регистры также необходимы в некоторых строковых операциях. • Регистр флагов Flags указывает текущее состояние процессора и результаты выполнения инструкций. Вопросы и упражнения 1. (а) Какова самая малая единица памяти компьютера? (б) Каковы ее два состояния? 2. (а) Как называется группа из девяти единиц, упомянутых в вопросе 1-а? (б) Для каких целей используются 8 из них? (в) Каково назначение девятого элемента? 3. Укажите длину следующих элементов данных: (а) параграфа, (б) слова, (в) двойного слова, (г) байта, (д) килобайта. 4. Преобразуйте следующие десятичные числа в двоичную систему счисления: (а) 7, (б) 15, (в) 25, (г) 28, (д) 33. 5. Сложите следующие двоичные числа: (а) 00101101 (б) 00110110 (в) 00101111 (г) 01111111 00000101 00111001 00000001 01010101 6. Преобразуйте каждое двоичное число из предыдущего вопроса в десятичную форму и убедитесь, что результаты сложений правильны. 7. Найдите двоичные дополнения к следующим числам: (а) 00110110, (б) 00111101, (в) 01111100, (г) 00000000. 8. Преобразуйте следующие отрицательные двоичные числа в положительные числа, равные им по модулю: (а) 11001100, (б) 00111101, (в) 10101010, (г) 11111111. 9. (а) сложите двоичные числа 11001011 и 01110111. (б) каково десятичное значение восьмиразрядной двоичной суммы? (в) преобразуйте двоичные значения в десятичные и проверьте правильность вычисления суммы. Глава 1. Основные свойства аппаратуры PC 43
10. Преобразуйте в шестнадцатеричную форму следующие значения: (а) ASCII- символ R, (б) ASCII-символ 7 (не число!), (в) двоичное число 01110101, двоичное число 01110110. 11. Сложите следующие шестнадцатеричные числа: (а) 13В4 (б) 53CD (в) 897 8 (г) DCBE , (д) FDAC +0033 +0004 +0777 +35В5 +0BAF 12. Определите шестнадцатеричное представление следующих десятичных чисел. Обратитесь к приложению А за методом преобразования. Вы можете проверить результат, преобразуя шестнадцатеричное значение в двоичное и суммируя веса единичных битов: (а) 23, (б) 37, (в) 75, (г) 255, (д) 4095, (е) 56217. 13. Используя приложение В в качестве руководства, укажите двоичное представление следующих символов в ASCII: (a) G, (б) s, (в) #, (г) 6, (д) *, (е) : (символ двоеточия). 14. Каковы основные функции процессора? 15. Укажите два основных типа памяти PC и важнейшие их применения. 16. Покажите, как процессор хранит в памяти такие значения: (а) 2АВ5, (б) 0AD4C2. 17. Объясните следующие термины: (а) сегмент, (б) смещение, (в) граница адреса сегмента. 18. Каковы (а) три типа сегментов, (б) их максимальный размер, и (в) адресная граница, на которой они могут начинаться? 19. Каково назначение регистров CS, DS, SS и ES? 20. Покажите абсолютные адреса, формируемые (а) значением SS 2АВ4[0]Н и значением SP 24H, (б) значением CS 2ВСЗ[0]Н и значением IP 3AH. 21. Какие регистры используются для следующих целей: (а) счет повторений цикла, (б) умножение и деление, (в) адресация сегментов, (г) индикация нулевого результата, (д) адрес смещения инструкции, подлежащей выполнению, (е) сложение и вычитание? 22. Покажите структуру регистра ЕВХ, размер и положение ВН, BL и ВХ в нем. 23. Закодируйте инструкции языка ассемблера для помещения значения 50 в следующий регистр: (а) СХ, (б) СН, (в) CL, (г) ЕСХ. 24. Закодируйте инструкции языка ассемблера для добавления (ADD) числа 28 к следующему регистру: (a) CL, (б) СН, (в) СХ, (г) ЕСХ. 25. Определите, какие флаги затрагиваются следующим событием: (а) арифметическая сумма равна 0, (б) арифметическая сумма отрицательна, (в) строковые данные сдвигаются слева направо. 44 Часть I. Основы аппаратного и программного обеспечения PC
Глава 2 Адресация и выполнение инструкций Цель: разъяснить общие требования к загрузке и выполнению программ на PC. Введение В этой главе описывается программная среда PC, функции операционной системы и ее основные компоненты. Мы рассмотрим процесс загрузки компьютера (как система запускается при включении питания) и то, как система загружает программы, подлежащие исполнению. Также будет описано использование стека и способ получения инструкциями, размещенными в сегменте кода, данных из сегмента данных. Эта глава завершает основы описания аппаратного и программного обеспечения PC и позволяет приступить к гл. 3, в которой рассматривается ввод простых программ в память и их пошаговое исполнение. Операционная система Операционная система предлагает унифицированный, не зависящий от типа устройства, доступ к ресурсам компьютера для таких устройств, как клавиатура, дисплей и дисковые накопители. Аппаратная независимость {device independence) означает отсутствие необходимости непосредственного обращения к устройству, поскольку операционная система может сама выполнять операции ввода/вывода (I/O) на уровне устройств, независимо от того, какая программа затребовала операцию. В этой книге рассматриваются следующие интересующие нас функции. • Управление файлами. Операционная система поддерживает структуру файлов и папок на дисках компьютера. Программы создают и обновляют файлы, но система управляет их размещением на дисках. • Ввод/вывод. Программы запрашивают исходные данные у системы и предоставляют выходные данные системе с помощью прерываний. Программисту не нужно кодировать низкоуровневые операции ввода/вывода. Глава 2. Адресация и выполнение инструкций 45
• Загрузка программ. Когда пользователь или программа запрашивают выполнение другой программы, загрузчик выполняет последовательность шагов, требуемых для получения доступа к этой программе на диске, для ее размещения в памяти и начала выполнения. • Управление памятью. Когда загрузчик заносит программу с диска в память для выполнения, он выделяет достаточный объем памяти для кода программы и ее данных. Программы могут обрабатывать данные в своей области памяти, могут освобождать ненужную память и запрашивать дополнительный ее объем. • Обработка прерываний. Система позволяет программам получать доступ к внешним устройствам посредством прерываний. Загрузка посредством BIOS Включение питания компьютера приводит к переходу процессора в состояние запуска (reset state), очистке памяти (записи нулей), проведению проверки четности памяти, загрузке в регистр CS адреса FFFF[0]H и смещения 0 в регистр IP. Поэтому первая инструкция, подлежащая выполнению, находится по адресу FFFF0H, сформированному парой CS:IP. Это адрес точки входа в BIOS, записанного в ПЗУ. BIOS содержит набор процедур, позволяющих поддерживать устройства. Процедура, начинающаяся с адреса FFFF0H, проверяет различные части компьютера, чтобы опознать и проинициализировать устройства, присоединенные к нему (она также содержит функции чтения и записи для этих устройств). После этого BIOS создает две области данных. 1. Область, содержащую таблицу векторов прерывании (Interrupt Vector Table), начинающуюся с адреса 0 и содержащую 256 4-байтных адресов в форме сегментхмещение. BIOS и операционная система используют эти адреса для обработки возникающих в процессе работы прерываний. 2. Область данных BIOS (BIOS Data Areas), которая начинается с адреса 40[0]Н и связана, в основном, с состоянием присоединенных устройств. Эта область детально описана в гл. 24. Затем BIOS определяет, доступен ли диск, на котором содержится операционная система, и, если доступен, вызывает начальный загрузчик с диска. Эта программа загружает системные файлы с диска в память и передает им управление. Системные файлы содержат драйвера устройств и другой код для работы с аппаратурой. Эти модули инициализируют внутренние системные таблицы и системную часть таблицы векторов прерываний. 46 Часть I. Основы аппаратного и программного обеспечения PC
Одна из задач операционной системы - взаимодействие с BIOS в случае необходимости. Когда прикладная программа запрашивает функцию ввода/вывода (I/O) у операционной системы, последняя передает запрос BIOS, a BIOS работает непосредственно с устройством. Однако в некоторых случаях, например, при работе с клавиатурой и дисплеем, программа обращается к BIOS напрямую. Иногда - хотя такая практика встречается редко и не рекомендуема - программа напрямую обращается к устройству. На рисунке показаны все возможные пути доступа к устройствам. Программа Операционная с ИС] сема BI Оборудс пользователя OS )Bai ыие /Ус :тройства Рис. 2.1. Интерфейс ввода-вывода Загрузчик системных программ Как только BIOS передает управление операционной системе (ОС), вы можете запросить выполнение программы. Есть два типа исполняемых файлов - .СОМ и .ЕХЕ. Программы типа .СОМ состоят из единственного сегмента, содержащего код, данные и стек. Этот тип программ полезен для создания небольших утилит или резидентных программ (это программы, остающиеся в памяти при выполнении других программ). В реальном режиме программы типа .ЕХЕ состоят из раздельных сегментов кода, данных, стека и применяются для создания более серьезных приложений. В этой книге используются программы обоих типов. Когда вы запрашиваете у системы выполнение программы типа .ЕХЕ, загрузчик выполняет следующие шаги. 1. Находит программу на диске. 2. Создает 256-байтный (100Н) префикс сегмента программы (program segment prefix, PSP) на границе параграфа в доступной внутренней памяти. 3. Загружает программу в память, непосредственно следующую за PSP. Глава 2. Адресация и выполнение инструкций 47
4. Загружает адрес PSP в регистры DS и ES. 5. Загружает адрес сегмента кода в регистр CS и устанавливает регистр IP в значение, соответствующее смещению первой инструкции (обычно 0) в сегменте кода. 6. Загружает адрес стека в регистр SS и устанавливает регистр SP в значение, равное размеру стека. 7. Передает управление исполняемой программе, начиная (обычно) с первой инструкции в сегменте кода. Как указано выше, загрузчик инициализирует CS:IP и SS:SP. Но заметьте, что он сохраняет адрес PSP как в DS, так и в ES, хотя программа требует наличия в этих регистрах адреса сегмента данных. Поэтому программы типа .ЕХЕ должны инициализировать регистр DS адресом сегмента данных (мы обсудим этот вопрос в главе 4). Теперь рассмотрим стек и сегменты кода и данных. Стек И .ЕХЕ, и .СОМ-файлы требуют резервирования места в программе под стек. У стека три основных назначения. 1. Программа вызывает подпрограмму для специальных целей; делая это, она сохраняет в стеке адрес, используемый подпрограммой для возвращения в основную программу. 2. Программа, вызывающая подпрограмму, может передавать ей данные, помещая их в стек, откуда подпрограмма может их получить. 3. Программа должна выполнять вычисления с использованием регистров; она может сохранять в стеке текущие их значения, производить вычисления и восстанавливать значения регистров из стека. Загрузчик программ автоматически создает стек для программ .СОМ, но для программ типа .ЕХЕ стек должен объявляться явно. В реальном режиме базовый элемент стека - слово (2 байта). Регистр SS, инициализированный загрузчиком, содержит адрес начала стека. Регистр SP изначально содержит размер стека, т.е. значение, указывающее на байт, следующий за концом стека. Стек отличается от других сегментов способом хранения данных: он начинает сохранять данные в ячейке с наибольшим доступным адресом в сегменте, а следующие данные сохраняет в ячейках в сторону уменьшения адресов памяти. Для изменения значения регистра SP используются инструкции PUSH и POP. Эти команды используются для записи данных в стек и чтения их из стека. PUSH уменьшает значение регистра SP на 2 и помещает значение в 48 Часть I. Основы аппаратного и программного обеспечения PC
слово, адресуемое парой SS:SP. POP считывает значение из адресуемого слова и увеличивает значение регистра SP на 2, перемещая указатель на следующее по адресу слово. Часть стека, содержащая все данные, помещенные в него для отдельной процедуры, называется кадром стека {stack frame). Следующий пример иллюстрирует помещение данных из регистров АХ и ВХ в стек и последующее извлечение данных обратно из стека в регистры. Предположим, что регистр АХ содержит 026В, ВХ содержит 04ЕЗ, и SP содержит 36. Адрес сегмента стека, хранящийся в регистре SS, здесь не важен. 1. Изначально стек пуст и имеет следующий вид. Смещение 34 32 30 2Е Кадр стека 0000 0000 0000 0000 SP 36 PUSH АХ: уменьшает значение регистра SP на 2 (до 34) и сохраняет содер-' жимое регистра АХ, 026В, в стеке. Заметим, что операция меняет местами байты в слове, так что 026В превращается в 6В02. Смещение Кадр стека 34 32 30 2Е 6В02 0000 0000 0000 SP = 34 PUSH ВХ: уменьшает SP до 32 и сохраняет содержимое ВХ, т.е. 04ЕЗ, в стеке в виде Е304. Смещение 34 32 30 2D Кадр стека 6В02 Е304 0000 0000 <-- SP = 32 POP ВХ: помещает слово из ячеек, на которые указывает SP, в регистр ВХ и увеличивает SP на 2 (до 34). Регистр ВХ теперь содержит 04ЕЗ с правильно переставленными байтами. Глава 2. Адресация и выполнение инструкций 4 — 5-741 49
Смещение 34 32 30 2Е Кадр стека 6В02 Е304 0000 0000 <-- SP 34 5. POP AX: помещает слово из ячеек, на которые указывает SP, в регистр АХ и увеличивает SP на 2 (до 36). Регистр АХ теперь содержит 026В с правильно переставленными байтами. Смещение 34 32 30 2Е Кадр стека 6В02 Е304 0000 0000 SP = 36 Заметьте, что инструкции POP вызывались в порядке, обратном инструкциям PUSH, т.е. в примере содержимое АХ записывалось первым, но читалось вторым, а содержимое ВХ записывалось вторым, но читалось первым. Также обратите внимание, что значения, записанные в стек, не теряются после чтения, хотя SP уже не указывает на них. Последующие операции PUSH заменят старые значения слов новыми. Следует помнить, что написанная программа должна сочетать помещение элементов в стек и извлечение их из стека. Хотя это требование кажется простым и ясным, его нарушение может привести к трудно устранимым ошибкам. Кроме того, для программы .ЕХЕ стек должен быть достаточно большим для хранения всех значений, которые могут быть в него помещены. Заметьте: когда SP = 0, стек полон. Другие инструкции, работающие со стеком: • PUSHF и POPF: сохраняют и восстанавливают состояние флагов; • PUSHA и РОРА (80286+): сохраняют и восстанавливают содержимое всех регистров общего назначения (АХ, DX, СХ, ВХ, SP, BP, SI и DI) и увеличивают/уменьшают SP на 16; • PUSHAD и POPAD (80386+): сохраняют и восстанавливают содержимое всех 32-хразрядных регистров общего назначения (ЕАХ, EDX, ECX, EBX, ESP, EBP, ESI и EDI) и увеличивают/уменьшают SP на 32. 50 Часть I. Основы аппаратного и программного обеспечения PC
Выполнение инструкций и адресация Программист на языке ассемблера пишет программу в виде символического кода и использует компилятор для преобразования ее в машинный код в виде программы типа .СОМ или .ЕХЕ. Для выполнения программы система загружает машинный код в память. Каждая инструкция состоит, как минимум, из одной операции, например, перемещения, сложения или возврата. В зависимости от операций, инструкция может также иметь один или более операндов, ссылающихся на данные, подлежащие обработке. Основные шаги, предпринимаемые процессором для выполнения инструкции, приведены ниже. 1. Выбрать следующую подлежащую исполнению инструкцию и поместить ее из памяти в очередь исполнения. 2. Декодировать инструкцию: вычислить адреса, по которым надо обратиться к памяти, доставить данные в арифметико-логическое устройство и увеличить регистр IP на 1. 3. Выполнить инструкцию: совершить требуемые действия, сохранить результаты в памяти или в регистрах и установить соответствующие флаги (например, ZF или CF). Процессоры Pentium и более новые могут одновременно получать, декодировать и исполнять инструкции, как показано на упрощенном примере ниже. Выборка Декодирование Выборка Выполнение Декодирование Выборка Выполнение Декодирование Выполнение Как уже говорилось, в программе .ЕХЕ регистр CS указывает на адрес начала сегмента кода, a DS - сегмента данных. Сегмент кода содержит инструкции, которые необходимо выполнить, а сегмент данных - данные, на которые ссылаются инструкции из сегмента кода. Регистр ГР указывает адрес смещения инструкции, подлежащей выполнению, в сегменте кода. Операнд инструкции содержит адрес смещения в сегменте данных, на который необходимо сослаться. Рассмотрим пример, в котором загрузчик счел, что должен загрузить программу в память, начиная с адреса 05ВЕ[0]Н. Соответственно загрузчик устанавливает CS в 05ВЕ[0]Н и IP в 0. CS:IP определяют аДрес первой подлежащей исполнению инструкции: 5ВЕ0Н + 0000Н = 5ВЕ0Н. Поэтому исполнение программы начинается с первой инструкции в сегменте кода. Если длина первой Глава 2. Адресация и выполнение инструкций 51 4*
инструкции 2 байта, процессор увеличивает значение IP на 2, и адрес следующей инструкции будет равен 5ВЕ2Н. . Предположим, что программа продолжает выполняться ц IP в какой-то момент содержит 0023Н. CS:IP теперь определяет адрес следующей подлежащей исполнению инструкции в виде Адрес сегмента CS 5BE0H Смещение (регистр IP) + 0023Н Адрес инструкции 5С03Н Пусть, например, инструкция MOV, начинающаяся с адреса 5С03Н, копирует содержимое байта из памяти в регистр AL; байт находится в ячейке сегмента данных со смещением 0016Н. Вот запись данной инструкции в символьном и машинном кодах: А01600 MOV AL, [0016] I Адрес 05С03Н Адрес 05С03Н содержит первый байт (АО) машинного кода инструкции, которую должен обработать процессор. Второй и третий байты содержат значение смещения с переставленными байтами (0016 хранится в виде 1600). В символьном коде операнд [0016] заключен в квадратные скобки, обозначающие оператор индекса, чтобы указать, что это - значение смещения, а не просто адрес ячейки памяти. Пусть программа проинициализировала регистр DS адресом сегмента данных 05D1[0]H. Чтобы получить элемент данных, процессор определяет его адрес, суммируя адрес из регистра DS со значением смещения (0016Н) в операнде инструкции. Поскольку DS содержит 05D1[0]H, получим адрес требуемой ячейки памяти Адрес сегмента DS 5D10H Смещение (операнд инструкции) + 0016Н Адрес элемента данных , 5D2 6H Предположим, что ячейка с адресом 05D26H содержит значение 4АН. Процессор копирует это значение в регистр AL. На следующем рисунке показаны регистр CS, указывающий на начало сегмента кода, регистр DS, указывающий на начало сегмента данных, смещения в сегментах и копирование значения 4А в регистр AL. Обрабатывая каждый байт инструкции, процессор увеличивает значение IP на 1, а поскольку IP изначально содержал значение 0023Н и длина инструкции равна 3 байта, то ГР будет содержать 0026Н - адрес начала следующей 52 Часть I. Основы аппаратного и программного обеспечения PC
инструкции. Процессор будет готов к ее исполнению и определит ее адрес исходя из значений в регистрах CS (05BE0H) и IP (0026H), получив 05С06Н. CS - 05ВЕ DS - 05D1 ► ^ i : Смещение 0023 | Сегмент кода ! J t I Смещение 0016 Сегмент данных АХ | I 4А ' А Рис. 2.2. Сегменты и смещения Инструкция также может обращаться более чем к одному байту одновременно. Например, предположим, что инструкция должна сохранить значение регистра АХ (0248Н) в двух последовательных байтах в памяти, начиная со смещения 0016Н в сегменте данных. Символьный кбд такой инструкции - MOV [0016], АХ. При сохранении значения регистра в памяти процессор переставляет байты: Содержимое байтов: 48 02 Смещение в сегменте данных: 0016 0017 Другая инструкция, MOV АХ, [0016], соответственно, поместит эти байты из памяти обратно в регистр АХ, опять переставив байты местами. Операнды инструкций У инструкции может быть один, два или три операнда. Одна из особенностей операндов, которую следует уяснить, касается возможности использования имен, имен в квадратных скобках и чисел. В следующем примере инструкция DW объявляет WORDX как слово (2 байта): WORDX DW 0 /Определяем WORDX как слово MOV CX, WORDX MOV CX, 25 MOV CX, EX MOV CX, [BX] /Заносим значение WORDX в CX /Заносим число 25 в CX /Заносим значение BX в CX /Заносим в CX значение из ячейки /памяти, адресуемой регистром BX Глава 2. Адресация и выполнение инструкций 53
• Первая инструкция MOV переносит данные (WORDX) из памяти в регистр СХ. • Вторая инструкция MOV заносит число 25 в регистр СХ. • Третья инструкция MOV перемещает данные между регистрами (из ВХ в СХ). • Квадратные скобки в четвертой инструкции MOV определяют индексный оператор (index operator), означающий необходимость использования значения регистра ВХ в качестве смещения (вместе с регистром DS, в виде DS:BX), и слово, расположенное по полученному адресу, поместить в регистр СХ. Сравните действие этой инструкции и предыдущей, в которой содержимое регистра ВХ просто копировалось в СХ. Глава 6 подробно описывает такую непрямую адресацию. Защищенный режим В защищенном режиме, например, в Windows, процессор может переключаться между несколькими задачами. Каждая программа занимает свой отдельный участок памяти, и процессор должен защищать этот участок и сохранять состояние каждой программы. В реальном режиме сегментные регистры содержат действительные адреса сегментов, но могут адресовать только 1 мегабайт. Защищенный режим требует гораздо более мощных средств адресации. Для этого он использует различные таблицы, включая приведенные ниже. • Таблицы локальных дескрипторов (Local Descriptor Table, LDT). В соответствии с требованиями системы для каждой задачи наличествует одна такая таблица. 16-разрядный регистр LDT содержит адрес этой таблицы для выполняемой в данный момент задачи. • Таблица дескрипторов прерываний (Interrupt Descriptor Table, IDT). Эта таблица используется при обработке операций прерывания, а ее адрес содержится в регистре ШТ. • Таблица глобальных дескрипторов (Global Descriptor Table, GDT). Эта таблица содержит адреса всех LDT, а ее адрес хранится в 32-разрядном регистре GDT. Действительные адреса сегментов хранятся в таблицах дескрипторов; сегментные регистры (или селекторы) содержат указатели на текущую таблицу LDT. Таблицы предоставляют 32 разряда для адресации до 4 гигабайт памяти. Обычно таблица векторов прерываний расположена по адресу 0000:0000, но каждая задача может иметь свою собственную ее копию. 54 Часть I. Основы аппаратного и программного обеспечения PC
Ключевые моменты • Включение компьютера вводит процессор в состояние запуска, устанавливает все ячейки памяти в 0, проводит проверку четности памяти и устанавливает регистры CS и IP в значения, соответствующие начальному адресу BIOS в ПЗУ. • Два вида (типа) исполняемых программ - .СОМ и .ЕХЕ. • Для того чтобы загрузить на выполнение программу типа .ЕХЕ, загрузчик создает 256-байтный (100Н) заголовок PSP на границе параграфа в памяти и сохраняет программу сразу после PSP. Затем он загружает адрес PSP в регистры DS и ES, загружает адрес сегмента кода в CS, устанавливает IP на первую подлежащую исполнению инструкцию, загружает адрес стека в SS и указывает в SP размер стека. После этого загрузчик передает управление исполняемой программе. • Стек предназначен для временного хранения адресов и элементов данных. • Загрузчик программ определяет стек для программы типа .СОМ, но вы должны явно определять стек для программы типа .ЕХЕ. • Обрабатывая каждый байт инструкции, процессор увеличивает на 1 значение в регистре ГР так, что пара CS:IP всегда содержит адрес (сегментхмещение) следующей подлежащей выполнению инструкции.. Вопросы и упражнения 1. Определите 5 основных функций операционной системы. 2. Укажите шаги, производимые системой при начальной загрузке. 3. Загрузчик создает и заполняет область данных перед исполняемым модулем, загружая этот модуль для исполнения, (а) Как называется эта область данных? (б) Каков ее размер? 4. Загрузчик программ выполняет некоторые действия при загрузке программы типа .ЕХЕ. Какие значения он записывает в (a) SS и SP? (б) CS и IP? (в) DS и ES? 5. Объясните назначение стека. 6. Объясните, каким образом определяется стек для (а) программы типа .ЕХЕ, (б) программы типа .СОМ. 7. (а) Где изначально находится вершина стека и как она адресуется? (б) Каков размер каждой записи в стеке? 8. Если стек определен как DW 32H, каково содержимое регистра SP, если (а) стек пуст? (б) стек полон? Глава 2. Адресация и выполнение инструкций 55
9. Стек определен в размере 64Н байт, (а) Каково начальное значение регистра SP? (б) СХ содержит 25А4Н. После инструкции PUSH CX покажите содержимое стека и SP. (в) DX содержит ЗВ2АН. После PUSH DX укажите содержимое стека и SP. (г) Покажите содержимое SP после POP DX и POP CX. 10. При выполнении программы CS содержит 4АВ6[0]Н, SS содержит 4А82[0]Н, IP содержит 36Н, и SP содержит 28Н (значения указаны в обычном виде, т.е. с не переставленными байтами). Найдите адрес (а) вершины (текущей ячейки) стека, (б) следующей подлежащей исполнению инструкции. 11. При выполнении программы CS содержит 5С9В[0]Н, SS содержит 5С84[0]Н, IP содержит 48Н, и SP содержит 1 АН (значения указаны в обычном виде, т.е. с не переставленными байтами). Найдите адрес (а) вершины (текущей ячейки) стека, (б) следующей подлежащей исполнению инструкции. 12. Вычислите адрес данных, на которые ссылаются следующие инструкции: (а) MOV AX, [24H], если DS содержит значение 7Е9В[0]; (б) MOV BX, [48H], если DS содержит 43С6[0]Н. 56 Часть I. Основы аппаратного и программного обеспечения PC
Глава 3 Память компьютера и отслеживание исполнения инструкций Цель: ознакомиться с вводом программ в память и отслеживанием (трассировкой) их выполнения. Введение Эта глава использует DOS-программу DEBUG, позволяющую просматривать память, записывать в память программы и отслеживать их выполнение. Мы рассмотрим, как записывать программы в сегмент кода, и разберем каждый шаг выполнения. Хотя существуют более мощные средства отладки, например, CodeView и TurboDebugger, мы будем использовать DEBUG, поскольку она проста в использовании и доступна всем. Начальные упражнения демонстрируют, как просматривать содержимое отдельных участков памяти. Первая программа использует "непосредственные" данные, определенные прямо в инструкциях, для загрузки в регистры и выполнения арифметических операций. Второй пример использует данные, определенные отдельно от инструкций программы. Наблюдение за исполнением этих инструкций дает возможность понять принципы работы процессора и роль регистров. Начинать изучение изложенного здесь материала можно, не зная языка Ассемблера и программирования вообще. Все, что нужно, - это PC с процессором Intel или совместимым и копия операционной системы DOS (как отдельный продукт или под управлением Windows). Предполагается, что вы знакомы с вводом системных команд и работой с дисками и каталогами. Использование программы DEBUG Программа DEBUG используется для тестирования и отладки исполняемых программ. Программа DEBUG показывает код и данные программы в шестнадца- теричном формате, и любые данные, которые вы вводите в память, также должны быть в этом формате. DEBUG также реализует пошаговый режим исполнения, позволяющий выполнять инструкции программы по отдельности одну за другой и наблюдать результат выполнения каждой инструкции в памяти и регистрах. Глава 3. Память компьютера и отслеживание исполнения инструкций 57
Команды DEBUG Набор команд программы DEBUG позволяет выполнять несколько полезных операций. Команды, интересующие нас в данный момент, перечислены ниже: А преобразовать инструкции символьного кода в машинный код D показать содержимое области памяти в шестнадцатеричном формате Е ввести данные в память, начиная с определенного адреса G начать выполнение программы в памяти Н выполнить шестнадцатеричные арифметические операции N назвать программу Р продолжить или выполнить группу связанных инструкций Q завершить сеанс работы с DEBUG R показать содержимое одного или более регистров в шестнадцатеричном формате Т отследить выполнение одной инструкции U преобразовать (дизассемблировать) машинный код в символьный Правила команд DEBUG Вот основные правила использования программы DEBUG. • DEBUG не различает строчные и заглавные буквы. Вы можете использовать любые из них при вводе команд. • DEBUG полагает, что все вводимые числа заданы в шестнадцатеричной форме. • Пробелы используются в командах только для разделения параметров. • Сегмент и смещение указываются с использованием символа двоеточия, т.е. в форме сегментхмещение. Приложение С содержит полное описание команд DEBUG, включая инструкции по его запуску. Сейчас мы используем DEBUG для просмотра памяти. Команда D (Display - показать) в программе DEBUG Эта команда выводит содержимое указанной области памяти на экран. Следующие три примера используют команду D для просмотра одного и того же участка памяти, начинающегося со смещения ЗС1Н в сегменте кода (CS): D CS:3C1 (команда набрана заглавными буквами с пробелом); DCS:3C1 (команда набрана заглавными буквами без пробела); dcs:3cl (команда набрана строчными буквами без пробела); 58 Часть I. Основы аппаратного и программного обеспечения PC
В результате выполнения команды мы получим восемь строк. В левой части каждой из них будет указан адрес первого слева показанного байта в форме сегментхмещение. Основную часть строки, в центре, занимает шестнадцате- ричное представление параграфа, начинающегося с указанного в начале строки байта. Справа на экран выведены в ASCII-форме символы этого параграфа, которые можно вывести на экран, для облегчения интерпретации шестнадцате- ричной записи. Команда D выводит 8 строк данных, в каждой из которых по 16 байт (32 шест- ' надцатеричных разряда), всего - 128 байт, начиная с указанного вами адреса. Адрес хххх:хх10 хххх:хх20 хххх:хх30 хххх:хх40 <-- XX XX XX XX Шестнадцатеричное представление хх-хх —> XX XX XX XX <- ASCII -> X X X X X Адрес слева относится только к первому байту в строке, адреса последующих байтов могут быть легко найдены простым счетом: например, если адрес первого байта - 0159:0240Н, то одиннадцатый байт в строке имеет адрес 0159:024АН. Шестнадцатеричное представление содержит два знака для каждого байта, байты разделяются пробелами для улучшения читаемости. Кроме того, восьмой и девятый байты разделяет дефис. Поэтому, например, если вам требуется найти байт со смещением хх13Н, начните с байта ххЮН и найдите третий после него байт. Команда D также показывает содержимое регистров и состояние флагов в регистре Flags. Просмотр областей памяти В первых двух упражнениях команда D используется для просмотра областей памяти. Упражнение I: просмотр области данных BIOS Первое упражнение показывает содержимое области данных BIOS в памяти, начиная с адреса 400Н или, более точно, с адреса сегмента 40[0]Н. BIOS инициализирует значения в этой области памяти при включении компьютера и изменяет их в ходе выполнения программ. Вы можете просматривать эти значения при помощи адреса из двух частей: 40 в качестве адреса сегмента (то есть 400, с отброшенным младшим разрядом), Глава 3. Память компьютера и отслеживание исполнения инструкций 59
и пп в качестве смещения от начала сегмента. Воспринимайте адрес 40:пп как сегмент 40[0]Н плюс смещение ппН. В гл. 24 в параграфе "Область данных BIOS" содержимое этой области описано подробно. Проверка параллельных и последовательных портов Первые 16 байт области данных BIOS содержат адреса параллельных и последовательных портов. Введите следующую команду: D 40:00 (и нажмите <Enter>) Первые четыре выведенных слова указывают на адреса портов от СОМ1 до COM4. Если на вашем компьютере два последовательных порта, первые два слова, вероятно, содержат F803 и F802 в обращенной (с переставленными байтами) последовательности. Последовательные порты имеют адреса 03F8 и 02F8. Следующие 4 слова указывают на параллельные порты от LPT1 до LPT4. Для системы с одним параллельным портом первое слово, вероятно, содержит 7803, т.е. адрес порта - 0378. Проверка оборудования системы Слово состояния оборудования в области данных BIOS предлагает базовую информацию о присутствующих в системе устройствах. Это слово, расположенное по адресу 410Н - 411Н, можно просмотреть командой D 40:10 (и нажмите <Enter>) Выведенные строки должны начинаться так: 0040:0010 хх хх ... Допустим, что слово содержит 23 44 в шестнадцатеричной форме. Чтобы интерпретировать его, переставим байты (44 23) и преобразуем в двоичную форму: Двоичное значение: 0 1 0 0 0 10000100011 Позиция бита: 15 14 13 12 11 10 9 8 7 б 5 4 3 2 1 0 Вот что означают эти биты: Биты Устройство 15,14 Число параллельных (принтерных) портов = 1 (двоичное 01) 11—9 Число последовательных портов = 2 (двоичное 010) 7,6 Число дисководов = 1 (00 = 1, 01 = 2, 10 = 3, 11 = 4) 5,4 Начальный видеорежим = 10 (01 = 40x25 цветной, 10 = 80x25 цветной, 11 = 80x25 монохромный) 1 1 = присутствует математический сопроцессор 0 1 = присутствует привод для дискет 60 Часть I. Основы аппаратного и программного обеспечения PC
Неописанные биты не используются. Проверка состояния регистра клавиатуры (клавиша Shift) В области данных BIOS по адресу 417Н хранится первый байт состояния регистра клавиатуры. Убедитесь, что Num Lock и Caps Lock выключены, и просмотрите содержимое байта по этому адресу командой D 40:17. Результат будет похож на 0040:0017 00 00 ... Теперь включите Num Lock и Caps Lock и введите вновь команду D 40:17. Результат должен начинаться с 60 00. Проверка состояния видеоустройства В области данных BIOS по адресу 449Н находится первая область видеоданных (Video Data Area). Введите команду D 40:49. Первый показанный байт указывает на текущий видеорежим (например, 03 - цветной), а второй - число столбцов на экране (например, 50 означает режим с 80 столбцами). Число строк хранится по адресу 40:84Н. Упражнение II: Просмотр ROM BIOS В следующем упражнении просматриваются данные в ROM BIOS в верхней памяти. Проверка авторского права (copyright) и серийного номера Сведения об авторском праве BIOS системы встроены в ROM BIOS no адресу FE00:0. В зависимости от производителя компьютера будут выведены различные строки, после которых будет указан семизначный серийный номер. Строка, указывающая на авторство BIOS, легко читается в виде ASCII-последовательности, а серийный номер - в виде шестнадцатеричного числа. Строка с указанием авторских прав может быть длиннее, чем показанный участок памяти; в этом случае для просмотра не показанной части снова введите D и нажмите <Enter>. Проверка даты производства BIOS Эта дата, записанная в виде мм/дд/гг, начинается с адреса FFFF5H. Для ее просмотра введите команду D FFFF:5. Знание этой даты полезно при определении возраста и модели компьютера. Теперь, когда вы знаете, как использовать команду D, вы можете просмотреть содержимое любой области памяти. Можно также последовательно просматривать память, просто повторно вводя D - DEBUG будет выводить 128 байт, следующих за последними просмотренными. Глава 3. Память компьютера и отслеживание исполнения инструкций 61
Для завершения работы с DEBUG введите Q. Пример на машинном языке I: использование непосредственных (immediate) данных Воспользуемся DEBUG для ввода первой из двух программ непосредственно в память и наблюдения за ее выполнением. Обе программы включают простые инструкции машинного языка в виде, в котором они находятся в памяти, и позволяют продемонстрировать эффект их выполнения. Мы начнем выполнение упражнения с команды Е (Enter, ввести). Будьте внимательны при ее использовании, поскольку ввод неправильных данных или их ввод по неправильному адресу может привести к непредсказуемым результатам. Вряд ли вы нанесете системе существенный ущерб своими действиями, но вас может привести в замешательство результат ввода и вы можете потерять данные, введенные в сеансе работы с DEBUG. Первая программа использует непосредственные данные - данные, определенные в теле инструкций. Далее показан как машинный код, так, для улучшения восприятия, и соответствующий символьный код с комментариями. Первой инструкции, В82301, соответствует символьный код MOV АХ,0123, заносящий (копирующий) значение 0123Н в регистр АХ (непосредственные данные записываются в прямой, с непереставленными байтами, форме). MOV - инструкция, АХ - ее первый операнд, непосредственное значение 0123Н - второй. Машинная инструкция Символьный код Объяснение В82301 052500 8BD8 03D8 8ВСВ 2ВС8 2ВС0 ЕВЕЕ MOV ADD MOV ADD MOV SUB SUB JMP AX, AX, EX, BX, CX, CX, AX, 100 0123 0025 AX AX BX AX AX Занести значение 0123Н в АХ Прибавить к значению АХ 0025Н Занести значение АХ в ВХ Прибавить значение из АХ к ВХ Занести значение из ВХ в СХ Отнять значение АХ от СХ Отнять АХ от АХ (очистить АХ) Перейти к началу программы Заметьте, что машинные инструкции имеют длину 1, 2 или 3 байта. Первый байт указывает, собственно, операцию, а все последующие - ее операнды (непосредственные значения, ссылки на регистры или адреса в памяти). Исполнение программы начинается с первой машинной инструкции и последовательно 62 Часть I. Основы аппаратного и программного обеспечения PC
проходит через все инструкции одну за другой. Не пытайтесь сейчас разобраться в машинном коде: например, в одном случае машинный код (первый байт) инструкции MOV - В8, а в другом - 8В. Ввод инструкций программы Начните это упражнение точно так же, как и предыдущее: введите в командной строке DEBUG и нажмите <Enter>. После загрузки DEBUG он выводит свое приглашение (символ "-"). Дня того чтобы ввести свою программу непосредственно в память, вводите только машинный код, символьный код и комментарии вводить не нужно. Введите команду Е и следующие символы, как показано ниже. Е CS:100 B8 23 01 05 25 00 (и нажмите <Enter>) CS:100 указывает начальный адрес области, в которой будет храниться введенный код (обычный начальный адрес для машинного кода в программе DEBUG). Команда Е сохранит следующие за ней байты в адресах от CS:100 до CS:105. Следующая команда Е сохранит еще 6 байт в адресах от CS:106 до CS:10B. Е CS:106 8В D8 03 D8 8В СВ (нажмите <Enter>) ' Последняя команда Е сохраните 6 байт в адресах otCS:10C до CS:111. Е CS:10C 2В С8 2В СО ЕВ ЕЕ (нажмите <Enter>) Если вы введете неправильную строку, просто повторите ввод правильно. Выполнение инструкций программы Теперь несложно выполнить введенные инструкции по очереди. На рис. 3.1 показаны все шаги, включая команды Е для ввода машинного кода. Ваш дисплей должен отображать такие же результаты после ввода каждой команды DEBUG. Вы можете также просматривать содержимое регистров после выполнения каждой инструкции. Интересующие нас сейчас команды - R (registers - регистры) и Т (trace - отследить). Для просмотра начального состояния регистров и флагов введите команду R и нажмите <Enter>, как показано в строке 4 рисунка. DEBUG показывает содержимое регистров в шестнадцатеричной форме в виде АХ=0000 ВХ=0000 ... Из-за различий в конфигурациях компьютеров содержимое регистров на вашей машины может отличаться от приведенного на рисунке. DEBUG проини- циализировал DS, CS, SS и ES одним и тем же адресом, хххх[0]. ЕР должен показывать 0100, указывая, что выполнение инструкций начинается со смещения 100Н относительно начала сегмента кода (поскольку мы ввели Е CS:100 в первой команде). Глава 3. Память компьютера и отслеживание исполнения инструкций 63
-С CS:100 B8 23 01 05 25 00 -С CS:106 8В D8 03 D8 8В СВ -С CS:10C 2В С8 2В СО ЕВ ЕЕ "К АХ=0000 ВХ=0000 DS=181B ES=181B 181В:0100 В82301 -т АХ=0123 ВХ=0000 DS=181B ES=181B 181В:0103 052500 -Т АХ=0148 ВХ=0000 DS=181B ES=181B 181В:0106 8BD8 -т АХ=0148 ВХ=0148 DS=181B ES=181B 181В:0108 03D8 -Т АХ=0148 ВХ=0290 DS=181B ES=181B 181В:010А 8ВСВ -Т АХ=0148 ВХ=0290 DS=181B ES=181B 181В:010С 2ВС8 -Т АХ=0148 ВХ=0290 DS=181B ES=181B 181В:010Е 2ВС0 -Т АХ=0000 ВХ=0290 DS=181B ES=181B 181В: ОНО ЕВЕЕ CX=0000 DX=0000 SP=FFEE SS=181B CS=181B IP=0100 MOV AX,0123 CX=0000 DX=0000 SP=FFEE SS=181B CS=181B IP=0103 ADD AX,0025 CX=0000 DX=0000 SP=FFEE SS=181B CS=181B IP=0106 MOV BX,AX CX=0000 DX=0000 SP=FFEE SS=181B CS=181B IP=0108 ADD BX,AX CX=0000 DX=0000 SP=FFEE SS=181B CS=181B IP=010A MOV CX,BX CX=0290 DX=0000 SP=FFEE SS=181B CS=181B IP=010C SUB CX,AX CX=0148 DX=0000 SP=FFEE SS=181B CS=181B IP=010E SUB AX,AX CX=0148 DX=0000 SP=FFEE SS=181B CS=181B IP=0110 JMP 0100 BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PO NC BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PO NC , BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PE NC BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PE NC BP=0000 SI=0000 DI=0000 NV UP EI PL NZ AC PE NC BP=0000 SI=0000 DI=0000 NV UP EI PL NZ AC PE NC BP=0000 SI=0000 DI=0000 ' NV UP EI PL NZ AC PE NC BP=0000 SI=0000 DI=0000 NV UP EI PL ZR NA PE NC Рис. З.1. Трассировка машинных команд Регистр флагов на рисунке показывает следующие значения флагов переполнения, направления, прерывания, знака, нуля, дополнительного переноса, четности и переноса: NV UP EI PL NZ NA PO NC 64 Часть I. Основы аппаратного и программного обеспечения PC
Эти значения соответствуют слева направо: отсутствию переполнения, направлению вверх (или вправо), разрешению прерываний, положительному знаку, ненулевому значению, отсутствию дополнительного переноса, нечетному слову, и отсутствию переноса. В данный момент эти значения для нас не важны. Сразу после регистров командой R показана первая подлежащая выполнению инструкция. Заметьте, что на рисунке регистр CS содержит 181В. Поскольку ваш сегмент кода может быть расположен по другому адресу в памяти, он показан как хххх для инструкций: хххх:0100 В82301 MOV*AX,0123 • хххх означает начало сегмента кода по адресу хххх[0]Н. Значение хххх:0100 означает смещение 100Н от начала сегмента кода, начинающегося по адресу хххх[0]Н. • В82301 - машинный код, введенный вами по адресу CS:100. • MOV AX,0123 - символьная инструкция на языке Ассемблера, определенная DEBUG из машинного кода. Эта инструкция перемещает (копирует) непосредственное значение 0123Н в регистр АХ. DEBUG "дизассемблировал" машинный код для лучшего его понимания. После этой главы вы будете вводить программы исключительно в виде инструкций на языке Ассемблера. Предупреждение Перед тем как попытаться выполнить инструкцию, убедитесь, что она корректна. Даже если вы вв!едете неправильный машинный код, DEBUG попытается его выполнить, и, если код неправилен, процессор прервет обработку, после чего необходимо перезагрузить компьютер. Для выполнения инструкции MOV введите команду Т и нажмите <Enter>. Машинный код команды - В8 (поместить в АХ), операнд - 2301. Операция помещает 23 в младшую половину регистра (AL) и 01 - в старшую (АН). АН AL АХ: 01 23 DEBUG выводит результаты выполнения - эффект, оказанный операцией на содержимое регистров. Регистр ГР содержит теперь 0103Н (01 ООН плюс 3 байта - длина выполненной инструкции). Это значение указывает на адрес, по которому расположена следующая подлежащая выполнению инструкция, то есть: хххх:0103 052500 ADD AX,0025 Для выполнения этой инструкции вновь введите Т. Инструкция добавит 25Н к младшему байту (AL) регистра АХ и ООН - к старшему (АН), то есть добавит 0025Н к содержимому АХ. Теперь АХ содержит 0148Н, a IP - 0106H, указывая на следующую инструкцию, которую необходимо выполнить: Глава 3. Память компьютера и отслеживание исполнения инструкций 65 5^5-741
xxxx:0106 8BD8 MOV АХ,ВХ Вновь введите команду Т. Инструкция MOV заносит содержимое регистра АХ в регистр ВХ. Заметьте, что после выполнения инструкции ВХ содержит 0148Н. АХ все еще хранит 0148Н, поскольку инструкция MOV копирует данные, не удаляя их из источника. Теперь последовательно вводите команды Т для выполнения оставшихся инструкций. Инструкция ADD суммирует содержимое регистров АХ и ВХ, записывая значение 0290Н в регистр ВХ. Затем программа копирует содержимое ВХ в СХ, вычитает АХ из СХ и вычитает АХ из самого себя. Последняя инструкция очищает АХ, устанавливая его в 0, и устанавливает флаг ZF из NZ (nonzero - не нуль) в ZR (zero - нуль), отображая результат операции. Инструкция JMP устанавливает ЕР в 100Н, и обработка возвращается обратно к началу программы. Эта инструкция введена из соображений предосторожности, поскольку за последней введенной инструкцией следует "мусор", который при попытке выполнения мог бы вызвать останов процессора или другие нежелательные результаты. Заметьте, что программа представляет собой бесконечную последовательность (цикл). У нее нет механизмов нормального завершения, хотя DEBUG позволяет вам нормально с ней работать. Такого рода программы используются в особых целях, например, для непрерывного мониторинга. Кроме того, на рис. 3.1 показано, что регистры DS, ES, SS и CS содержат один и тот же адрес сегмента. Это потому, что DEBUG рассматривает весь введенный код как программу .СОМ, хранящую данные, стек и код в одном сегменте, хотя вы и храните их по отдельности внутри сегмента. При написании программы .ЕХЕ вы храните стек, данные и код в отдельных сегментах с разными адресами. Для повторного выполнения программы просто введите еще раз команду Т, программа DEBUG выполнит инструкцию JMP и перейдет к началу введенной программы. Просмотр содержимого памяти Для просмотра программы на машинном языке в сегменте кода запросите вывод информации командой D CS:100. На рис. 3.2 представлены результаты выполнения этой команды с 16 байтами (32 шестнадцатеричными разрядами) в каждой строке. Справа - ASCII представление (если соответствующий символ можно вывести) каждого байта. В случае машинного кода ASCII представление бессмысленно и может игнорироваться. В следующих далее параграфах правая часть рисунка рассмотрена более детально. 66 Часть I. Основы аппаратного и программного обеспечения PC
-D CS:100 181B:0100 181B:0110 181B:0120 181B:0130 181B:0140 181B:0150 181B:0160 181B:0170 B8 23 01 05 25 00 8B D8-03 D8 8B CB. 2B C8 2B CO ЕВ ЕЕ 8D 46 14 50 51 52-FF 76 28 E8 74 00 8B E5 B8 01 00 50 FF 76 32 FF-76 30 FF 76 2E FF 76 28 E8 88 15 8B E5 FF 36 18-12 FF 36 16 12 8B 76 28 FF 74 ЗА 89 46 06 E8 22-CE 8B E5 30 E4 3D OA 00 75 32 Al 16 12 2D 01 00-8B IE 18 12 83 DB 00 53 50 8B 76 28 FF 74 ЗА АЗ-16 12 89 IE 18 12 E8 FA CD 8B E5 30 E4 3D OD 00-74 OA 83 06 16 12 01 83 .#..% + . + . ...F.PQR.v(.t... .. .P.v2.v0.v. .v( 6...6...v( .t:.F.."...0.=.. u2. ..- S P.v(.t: ...0.=..t Рис. З.2. Просмотр содержимого памяти Первая строка рисунка начинается со смещения 100Н в сегменте кода и содержит байты с адресами от CS:100H до CS:10FH. Вторая строка выводит байты с адресами от CS:110H до CS:11FH. Хотя, собственно, введенная программа заканчивается байтом с адресом CS:111H, DEBUG автоматически выводит восемь строк - от CS:100H до CS:170H. В этом примере все данные, следующие за CS:111Н -"мусор". Байты с CS:100H до CS:111Н рисунка должны совпадать с соответствующими байтами выведенного программой DEBUG содержимого памяти и в вашем случае; остальные байты могут быть любыми. Введите Q для завершения работы с DEBUG или начните выполнение следующего упражнения. Пример на машинном языке II: использование ранее определенных данных Предыдущий пример использовал непосредственные данные, определенные прямо в директивах MOV и ADD. В этом примере данные (числовые константы 0123Н и 0025Н) определены в виде отдельных элементов данных в программе. Инструкции программы должны работать с ячейками памяти, содержащими эти значения. Этот пример покажет вам, как компьютер получает доступ к данным с помощью адреса в регистре DS и смещений. В примере, начиная с адреса DS:0200H, определены следующие элементы данных. Смещение в сегменте DS Шестнадцатеричный код содержимого 0200Н 2301Н 0202Н 2500Н 0204Н 0000Н 0206Н 2А2А2АН Глава 3. Память компьютера и отслеживание исполнения инструкций 67
Помните, что шестнадцатеричная цифра занимает полбайта, поэтому, например, 23Н хранится в байте с адресом 200Н, а 01Н - в следующем (со смещением 201Н). Ниже приведены машинные инструкции, обрабатывающие эти данные, со значениями, введенными в обратной (с переставленными байтами) форме. Инструкция Объяснение А10002 Поместить слово (2 байта), начинающееся с адреса DS:0200H, в регистр АХ 03060202 Добавить содержимое слова (2 байта), начинающегося с адреса DS:0202H, к содержимому регистра АХ A3 0402 Поместить содержимое АХ в слово, начинающееся с адреса DS:0204H EBF4 Перейти к началу программы Вы могли заметить, что две инструкции по перемещению данных имеют разные машинные коды: А1 и A3. Действительный машинный код инструкции MOV зависит от регистров, на которые ссылается инструкция, размера элемента данных (байт или слово), направления передачи дайных (в регистр или из регистра) и того, ссылается инструкция на непосредственные данные, адрес в памяти или регистр. Ввод инструкций и данных программы Мы используем программу DEBUG для ввода и выполнения программы этого примера. Сначала с помощью команды Е введем инструкции, начиная с адреса CS:0100: Е CS:100 A1 00 02 03 Об 02 02 (и нажмите <Enter>) Е CS:107A3 04 02 ЕВ F4 (и нажмите <Enter>) Теперь (также при помощи команды Е) введите соответствующие значения в ячейки сегмента данных: Е DS:0200 23 01 25 00 00 00 (и нажмите <Enter>) Е DS:0206 2А 2А 2А (и нажмите <Enter>) Первая команда Е сохраняет три слова (6 байт) в начале области данных - начиная со смещения 0200Н. Все эти слова должны быть введены в форме с переставленными байтами, то есть 0123 как 2301 и 0025 как 2500. Когда в дальнейшем инструкция MOV запросит эти слова из памяти, байты будут переставлены опять, образуя исходные значения. Вторая команда Е вводит в память три символа звездочки (***) в виде кода 2А2А2А. Вы сможете их увидеть в виде символов с помощью команды D. 68 Часть I. Основы аппаратного и программного обеспечения PC
На рис. 3.3 показано исполнение всех шагов программы, включая команды Е. На вашем дисплее должны отобразиться такие же результаты, хотя, вероятно, адреса сегментов CS и DS будут другими. Для просмотра данных в сегменте DS (со смещениями от 0200Н до 0208Н) и инструкций в сегменте CS (от 01 ООН до 010АН) введите следующие команды D: Для просмотра кода: D CS:100,10A <Enter> Для просмотра данных: D 08:200,208 <Enter> Убедитесь, что содержимое обеих показанных областей идентично приведенному на рис. 3.3. -Е CS:100 Al 00 -Е CS:107 A3 04 -Е DS:200 23 01 -Е DS:206 2А 2А -D CS:100,10A 181В:0100 Al 00 -D 03:200,208 181В:0200 23 01 _D —К АХ=0000 BX=0000 DS=181B ES=18lB 181B:0100 A10002 -т - АХ=0123 ВХ=0000 DS=181B ES=181B 02 03 06 02 ЕВ F4 25 00 00 2А 02 03 06 25 00 00 СХ=0000 SS=181B 02 02 00 02 02 АЗ-04 02 ЕВ 00 2А 2А-2А DX=0000 SP=FFEE CS=181B IP=0100 MOV AX,[0200] сх=оооо SS=181B DX=0000 SP=FFEE CS=181B IP=0103 ЩВ:0103 03060202 ADD AX,[0202] -Т АХ=0148 ВХ=0000 DS=181B ES=181B 181B:0107 A30402 -Т АХ=0148 ВХ=0000 DS=181B ES=181B 181B:010A EB F4 -D 03:0200,0208 181В:0000 23 01 -Q сх=оооо SS=181B DX=0000 SP=FFEE CS=181B IP=0107 MOV [0204],АХ СХ=0000 SS=18lB DX=0000 SP=FFEE CS=181B IP=010A JMP 0100 25 00 48 01 2A 2A-2A F4 # %...*** BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA Ю NC DS:0200=0123 BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PO NC DS:0202=0025 BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PE NC DS:0204=0000 BP=0000 SI=0000 DI=0000 NV UP EI PL NZ NA PE NC #.%.H.*** Рис. 3.3. Трассировка машинных команд Выполнение инструкций программы Введя инструкции, вы можете выполнять их тем же способом, что и в предыдущем примере. Сначала убедитесь, что ЕР содержит 01 ООН. После этого командой R просмотрите содержимое регистров, флагов и код первой инструкции. Хотя АХ может все еще содержать значение, присвоенное ему в предыдущем примере, оно скоро будет заменено новым. Первая показанная инструкция хххх:0100 А10002 MOV АХ,[0200] Глава 3. Память компьютера и отслеживание исполнения инструкций 69
CS:0100 указывает на первую инструкцию, А10002. DEBUG интерпретирует эту инструкцию как MOV и определяет, что она ссылается на ячейку со смещением [0200Н] в сегменте DS. Квадратные скобки указывают, что это не непосредственное значение, а адрес в памяти. (Непосредственное значение для записи в АХ выглядело бы как MOV АХ,0200.) Теперь введите команду Т. Инструкция выполняется и помещает значение слова со смещения 0200Н в регистр АХ. Содержимое этого слова - 2301Н, а инструкция переставляет байты и помещает его в регистр АХ в виде 0123Н, стирая любое предыдущее значение. Введите вторую команду Т. Будет выполнена следующая инструкция, т.е. ADD. Операция добавляет значение из слова по адресу DS.0202H к значению регистра АХ. Результат - сумма 0123Н и 0025Н, то есть 0148Н. Следующая инструкция - MOV [0204],АХ. Введите Т для ее исполнения. Инструкция копирует значение из регистра АХ в слово в памяти, занимающее ячейки с адресами 0204Н и 0205Н. При этом байты будут переставлены, и слово будет содержать значение 4801Н. Для просмотра изменившегося содержимого ячеек памяти введите команду D 03:200,208 <Enter> Выведенные значения должны быть такими: Значение в ячейке: 23 01 25 оо 48 01 2а 2а 2а III I I I I I I Смещение: 200 201 202 203 204 205 206 207 208 Левая сторона дисплея показывает действительный машинный код так, как он хранится в памяти. Правая сторона просто помогает вам легко находить символьные данные. Заметьте, что эти шестнадцатеричные значения представлены в правой части экрана соответствующими им символами ASCII. Коды 23Н и 25Н выводятся как # (символ номера) и % (символ процента) соответственно. Три байта 2АН выводятся как три звездочки (*). Можно завершить работу с DEBUG командой Q или перейти к выполнению следующего примера. Повторное выполнение инструкций Иногда необходимо вручную установить значение в регистре ЕР. Фактически, вы довольно часто будете это делать, и это делается так. 1. Введите команду R IP для вывода содержимого регистра ГР. 2. Введите значение 100 (или адрес другой инструкции), а затем нажмите <Enter>. 70 Часть I. Основы аппаратного и программного обеспечения PC
Эта процедура возвращает вас к началу программы (или к инструкции внутри программы), и вы можете повторно выполнить уже пройденные шаги. Введите команду R (без IP). DEBUG выведет на экран содержимое регистров, флаги и следующую подлежащую выполнению инструкцию. Теперь вы можете использовать команду Т для повторного выполнения инструкций. Если ваша программа накапливает значения, используйте команду Е для очистки ячеек памяти и команду R для очистки регистров. Но не пытайтесь очищать регистры CS, DS, SP, SS - их значения используются системой. Сохранение программы в DEBUG Можно использовать DEBUG для сохранения программы на диске в двух случаях. 1. Для получения с диска существующей программы, внесения в нее изменений и последующего сохранения. 2. Дня создания при помощи DEBUG маленькой программы в машинных кодах, которую вы хотите сохранить. За детальным объяснением обращайтесь к описанию команды W (write - записать) в приложении С. На данном этапе вам также может оказаться полезной команда Н, складывающая и вычитающая шестнадцатеричные числа. Максимальная длина чисел - 4 шестнадцатеричных разряда. Например, введите команду Н 3443 2А2В. Команда выведет сначала сумму (5Е6Е), а затем - разность (0А18). Программа на языке Ассемблера Хотя до этого все примеры вводились в виде команд машинного языка, DEBUG также можно использовать для ввода программ на языке Ассемблера. Каждый из способов ввода полезен в определенных случаях. Рассмотрим команды DEBUG А и U, используемые для ввода выражений на языке Ассемблера в компьютер. Команда A (Assemble) Команда А приказывает DEBUG начать воспринимать ввод символьных инструкций и преобразовывать их в машинный код. Инициализируйте начальный адрес для инструкций в сегменте кода смещением 100Н с помощью команды А 100 <Enter> Глава 3. Память компьютера и отслеживание исполнения инструкций 71
DEBUG выведет адрес сегмента кода и смещения (01 ООН) в виде хххх:0100. Введите следующие инструкции: MOV CL, 42 <Enter> MOV DL, 2A <Enter> ADD CL, DL <Enter> JMP 100 <Enter> После ввода программы нажмите <Enter> еще раз для выхода из команды А. Это нажатие <Enter> указывает DEBUG, что вы больше не собираетесь вводить символьные инструкции. По завершении ввода DEBUG показывает адрес каждой введенной инструкции: xxxx:0100 MOV CL,42 хххх:0102 MOV DL,2A хххх:0104 ADD CL,DL хххх:010б JMP 100 хххх:0108 Перед выполнением программы воспользуемся командой U (Unassemble - дизассемблировать) для просмотра созданного DEBUG машинного кода. КомандаU Команда U выводит на дисплей машинный код, соответствующий введенным вами символьным инструкциям. Можно использовать эту команду для указания первой и последней инструкции, которые вы хотите просмотреть, т.е. в данном случае это 01 ООН и 0107Н. Введите команду U 100,107 <Enter> На экран будут выведены колонки адресов, машинных и символьных кодов, выглядящие приблизительно следующим образом: хххх:0100 В142 MOV CL,42 хххх:0102 В22А MDV DL,2A xxxx:0104 00D1 ADD CL,DL xxxx:0106 EBF8 JMP 100 xxxx:0108 Теперь отследите выполнение машинного кода введенной программы. Начните с ввода команды R для просмотра содержимого регистров (ЕР должен содержать значение 01 ООН) и следующей выполняемой инструкции (MOV CL,42). Затем последовательно вводите команды Т для выполнения инструкций. Когда вы доберетесь до JMP 100, ГР должен содержать 0106Н, a CL - 6СН. Теперь переходите к следующему примеру или введите Q для завершения сеанса работы с DEBUG. 72 Часть I. Основы аппаратного и программного обеспечения PC
Использование инструкции INT Следующие четыре примера показывают, как запрашивать информацию о системе. Для этого вы будете использовать инструкцию INT (interrupt - прерывание), которая осуществляет выход из программы, вызов подпрограммы DOS или BIOS, выполнение заданной, функции и возвращение в вашу программу. Есть разные типы операций INT, некоторые из них требуют наличия кода функции (function code) в регистре АН для выполнения определенного действия. Вместо использования команды Т для пошагового исполнения программ, мы используем команду Р (Proceed - продолжить) для выполнения всей программы обработки прерывания сразу. Убедитесь, что IP установлен в 01 ООН. Получение текущих значений даты и времени Инструкция для получения текущих значений даты и времени - INT 21H, с кодом функции 2АН. Снова введите в DEBUG, команду А 100, а затем - следующие инструкции: MOV АН, 2А <Enter> INT 21 <Enter> JMP 100 <Enter>, <Enter> Введите R для просмотра регистров и Т для выполнения инструкции MOV. После этого введите Р для выполнения подпрограммы обработчика прерываний; выполнение остановится на инструкции JMP. Регистры будут содержать в шест- надцатеричной форме следующую информацию: AL: день недели, где 0 = воскресенье СХ: год (например, 07D4H = 2004) DH: месяц (от 01Н до 0CH) DL: день месяца (от 01Н до 1FH) Операция для получения текущего времени - ЕЧТ 21Н с кодом функции 2СН. Сначала используйте команду R IP для установки IP в 01 ООН, а затем введите следующие команды: А 100 <Enter> MOV АН, 2С <Enter> INT 21 <Enter>' JMP 100 <Enter> Выполните программу так же, как и предыдущую. Операция помещает число часов в СН (в 24-часовом формате, где 00 - полночь), минуты - в CL, секунды - в DH и сотые доли секунды - в DL. Глава 3. Память компьютера и отслеживание исполнения инструкций 73
Введите Q для выхода или переходите к следующему примеру (установив IP в 01 ООН). Определение установленного оборудования В одном из упражнений в начале этой главы мы просматривали сожер- жимое байтов с адресами 410Н и 411Н, для определения оборудования, присутствующего в системе. BIOS содержит прерывание, INT 11H, помещающего соответствующую информацию в АХ. Введите команду А 100 в DEBUG и следующие инструкции: INT 11 <Enter> JMP 100 <Enter>/ <Enter> Введите R для просмотра регистров и первой инструкции. Эта инструкция, INT 11H, передает управление подпрограмме BIOS, помещающей данные об установленном оборудовании в АХ. Используйте команды Т для наблюдения за последовательным выполнением инструкций BIOS (да, эта процедура нарушает правило, запрещающее отслеживать выполнение прерывания, но она корректно выполняется). (Выполнять отладку встроенных в BIOS обработчиков прерываний на машинах с перепрограммируемым (FLASH) BIOS нежелательно - изменение их кода может привести к выходу из строя самого BIOS или других компонентов компьютера. - Прим. ред.) Инструкции, содержащиеся в BIOS вашего компьютера, могут отличаться от приведенных ниже - в зависимости от установленной марки и версии BIOS (комментарии справа - авторские). JMP EE53 PUSH DS ; Сохранить адрес из DS в стеке MOV АХ, 0040 ; Получить адрес сегмента, MOV DS, АХ ; поместить его в DS MOV АХ, [0010] ; Поместить данные из 40:10 в АХ POP DS ; Восстановить адрес в DS из стека IRET ; Вернуться в исходную программу Последняя команда Т производит выход из BIOS и возвращает управление DEBUG. Если все выполнено правильно, АХ теперь содержит данные об установленном оборудовании. Следующая команда, введенная вами, - JMP 100. Введите Q для выхода или переходите к следующему примеру (установив ЕР в 01 ООН). Использование INT для вывода на дисплей Этот пример, выводящий данные на экран, вводит в употребление несколько новых возможностей. Введите в DEBUG команду А 100, а затем - следующие инструкции: 74 Часть I. Основы аппаратного и программного обеспечения PC
100 MOV АН,09 <Enter> 102 MOV DX,109 <Enter> 105 INT 21 <Enter> 107 JMP 100 <Enter> 109 DB 'your name', '$' <Enter> <Enter> Две инструкции MOV указывают INT 21H организовать вывод на экран (АН = 09Н) и с какого начального адреса (DX = 109). Заметьте, что по смещению 109Н начинается строка, где DB означает "define byte" - "определить байт". Символы, подлежащие выводу, заключены в апострофы ('). За строкой, которую необходимо вывести, следует знак доллара ($), также заключенный в апострофы, указывающий INT на конец вывода. Введите R для выполнения просмотра регистров и первой инструкции, две команды Т - для выполнения двух инструкций MOV, и Р - для выполнения INT 21Н, и на экране вы увидите строку, заключенную в апострофы. Введите Q для выхода или переходите к следующему примеру (установив IP в 01 ООН). Использование INT для ввода с клавиатуры Этот пример, воспринимающий символы с клавиатуры, также демонстрирует новые возможности. Введите команду DEBUG A 100 и следующие инструкции на Ассемблере: 100 MOV АН ДО <Enter> 102 INT 16 <Enter> 104 JMP '100 <Enter>/ <Enter> Первая инструкция, MOV, указывает код функции, ЮН, задающий использование INT 16H для ввода с клавиатуры. При выполнении инструкций обработчиком с клавиатуры считывается символ и помещается в регистр AL. Введите R для просмотра регистров и следующей инструкции, и Т - для выполнения инструкции MOV. Затем введите Р для выполнения INT 16H, после чего система будет ожидать нажатия клавиши. Если вы нажмете клавишу 1, вы увидите, что в AL помещено число 31Н (это ASCII-код цифры 1). Введите Т для выполнения инструкции JMP 100, затем еще раз Т - для выполнения MOV АН, 10. Когда вы введете Р для выполнения INT 16H, система вновь будет ожидать нажатия клавиши. Если вы нажмете 2, то увидите, что в AL занесено значение 32Н. Можно продолжать ввод до бесконечности, следуя только что описанной процедуре. Вы найдете список кодов ASCII в приложении F. Введите Q для выхода или переходите к следующему примеру (установив IP в 01 ООН). Использование оператора PTR Следующий пример программы демонстрирует несколько новых возможностей. В этом примере вы перемещаете и складываете данные в регистрах и ячейках Глава 3. Память компьютера и отслеживание исполнения инструкций 75*
памяти. В предыдущих примерах, помещавших данные в регистр, DEBUG мог определить, исходя из типа регистра (AL или АХ, например), сколько байтов было необходимо переместить. Приведенная ниже программа помещает непосредственные данные в память. Поскольку инструкция вроде MOV [120],25 не указывает количества байт, для этой цели можно использовать оператор PTR. Вот инструкции программы: 100 103 107 10А 10D 113 118 НА НС НЕ 120 MOV АХ, [НА] ADD AX, [НС] ADD AX,25 MOV [НЕ] ,АХ MOV WORD PTR MOV BYTE PTR JMP 100 DB 14 23 DB 05 00 DB 00 00 DB 00 00 00 [120],25 [122],30 Ниже приведено описание инструкций. 100: Поместить содержимое ячеек памяти ПАН и 11ВН в АХ. Квадратные скобки указывают на то, что операнд - адрес в памяти, а не непосредственное значение. 103: . Добавить содержимое ячеек памяти 11СН и 11ВН к АХ. 107: Добавить непосредственное значение 25Н к АХ. 10А: Поместить содержимое регистра АХ в ячейки памяти с адресами HEHhUFH. 10D: Поместить непосредственное значение 25Н в ячейки памяти 120Н и 121Н. Обратите внимание на использование оператора WORD PTR, указывающего DEBUG, что значение 25Н должно быть помещено в слово в памяти. Если бы вы просто записали данную инструкцию в виде MOV [120] ,25, DEBUG не смогла бы определить, какая длина выделенного участка памяти подразумевалась, и вывела бы сообщение об ошибке. Хотя вы редко будете нуждаться в операторе PTR, полезно знать, когда он необходим. 113: Поместить непосредственное значение ЗОН в ячейку памяти по адресу 122Н. На этот раз используется оператор BYTE PTR для указания длины в 1 байт. ПА: Определить значения байт 14Н и 23Н. DB определяет байты для элементов данных, на которые инструкции (например, первая) ссылаются. 11С, 1 IE, и 120: Определить другие значения, используемые в программе. .76 Часть I. Основы аппаратного и программного обеспечения PC
Для ввода этой программы сначала введите команду DEBUG A 100, а затем последовательно введите все инструкции (но не адреса, с которых начинаются строки). В конце еще раз нажмите клавишу <Enter> для завершения ввода. Дня выполнения программы сначала воспользуйтесь командой R для просмотра состояния регистров и первой инструкции, а затем используйте последовательность команд Т. Остановите выполнение, когда вы доберетесь до команды JMP 100 по адресу 118Н. Командой D просмотрите содержимое области памяти программы и убедитесь, что изменилось содержимое АХ (233Н) и ячеек памяти с адресами 11EH-11FH (ЗЕ23), 120Н-121Н (2500) и 122Н (30). Введите Т для повторения программы или Q - для выхода. Ключевые моменты • Программа DEBUG может применяться для проверки и отладки программ в машинных кодах и на языке Ассемблера. Среди ее команд есть такие полезные операции, как просмотр, ввод в память и пошаговое выполнение программы. • Поскольку DEBUG не различает строчные и заглавные буквы, можно вводить команды любыми из них. • В DEBUG все числа представляются в шестнадцатеричном формате. • В случае ввода неправильного значения в сегмент данных или кода повторите последнюю команду Е для ввода правильного значения вместо неправильного. • Используйте команду Т для выполнения отдельной инструкции программы, или команду Р для выполнения всей подпрограммы INT. • Для выполнения программы, начиная с первой инструкции, установите ЕР (указатель инструкций) в 01 ООН. Для этого воспользуйтесь командой R IP. DEBUG выведет содержимое регистра ГР и будет ожидать ввода нового содержимого. Введите 0100 и нажмите <Enter>. Вопросы и упражнения 1. Объясните назначение следующих команд DEBUG: (а) А, (б) U, (в) Р, (г) Т, (д) Q, (e) D, (ж) R, (з) Е. 2. Укажите команды DEBUG, с помощью которых можно сделать следующее. (а) Просмотреть содержимое всех регистров. (б) Просмотреть содержимое регистра IP и установить его в 01 ООН. Глава 3. Память компьютера и отслеживание исполнения инструкций 77
(в) Просмотреть данные, хранящиеся в памяти, начиная со смещения 2ВСН в сегменте данных. (г) Просмотреть данные, находящиеся в памяти, начиная с ячейки с адресом 3AFH. (Замечание: разделите этот адрес на части, соответствующие сегменту и смещению.) (д) Дизассемблировать символьный код по адресам от 100Н до 12ВН. (е) Ввести 24А63ВН в сегмент данных, начиная с адреса 18АН. 3. Укажите машинный код, выполняющий следующие действия: (а) прибавление непосредственного значения 03 А8Н к АХ, (б) помещение значения 2СА4Н в регистр АХ. 4. Предположим, вы ввели такую команду DEBUG: Е CS:100 B8 45 01 05 25 00 Значение 45 на самом деле должно было быть 54. Укажите команду, изменяющую только этот байт, которая исправляет сделанную ошибку. 5. Область видеопамяти (Video Display Area) для цветного монитора в текстовом режиме начинается с адреса В800[0]. (а) Используйте команду Debug D для просмотра этой области памяти. Заметьте, что за каждым символом в этой области следует байт атрибутов (07Н для черно-белого режима). Используйте дополнительные команды D для просмотра всей области видеопамяти. (Ь) Используйте команду DEBUG F (Fill - заполнить) для вывода на экран 50 звездочек (символов * - код 2АН). Например, следующая команда заполняет всю видеопамять 4000 (FA0H) сердечками (03) и атрибутами (где 16H соответствует коричневым символам на синем фоне): F B800:0 LFA0 03 16. 6. Предположим, что в DEBUG введена следующая команда: Е CS:100 B8 05 1В 05 00 2С ЕВ F8 (а) Какие три символьных инструкции здесь закодированы? (Первая программа в главе содержит подсказку.) (б) При исполнении этой программы вы обнаруживаете, что в регистр АХ помещается значение 4705 вместо ожидаемого 0547. В чем состоит ошибка и как ее исправить? (в) Исправив ошибку, вы хотите повторно выполнить программу, начиная с первой инструкции. Какие команды DEBUG необходимо использовать? 7. Рассмотрим следующие инструкции машинного языка: ВО 1С DO E0 ВЗ 12 F6 ЕЗ ЕВ F6 78 Часть I. Основы аппаратного и программного обеспечения PC
Эти инструкции выполняют следующие действия. (а) помещают 1СН в регистр AL, (б) смещают регистр AL на один бит влево (эквивалент умножения на 2), (в) помещают 12Н в регистр BL, (г) умножают AL на BL. Используйте команду DEBUG E для ввода этой программы, начиная с адреса CS:100 (Помните, что все значения - шестнадцатеричные). Затем введите D CS:100 для просмотра введенного кода. После этого введите R и последовательными командами Т выполните программу до достижения инструкции JMP. Каков конечный результат в регистре АХ? 8. Используйте DEBUG для ввода следующей программы в машинных кодах: Код (с адреса 100Н): АО 00 02 DO E0 F6 26 01 02 A3 02 02 90 Данные (с адреса 200Н): IE 16 00 00 Эта программа выполняет следующие действия: (а) помещает значение байта DS:0200H (IE) в регистр AL; (б) сдвигает содержимое AL на один бит влево; (в) умножает AL на байт, содержащийся в ячейке с адресом DS:0201H (16), (г) помещает произведение из АХ в слово, начинающееся с адреса DS:0202H. Введите команды D для просмотра введенных кода и данных. После этого введите команду R и последовательными командами Т выполните программу до достижения инструкции JMP. Каков конечный результат в регистре АХ? Введите еще одну команду D DS:0200 и посмотрите, в каком виде произведение хранится в памяти. 9. Для вопроса 7, введите команды, записывающие программу на диск под именем HEXMULT.COM (см. приложение С). 10. Используйте команду DEBUG А для ввода следующих инструкций: MOV DX,2E ADD DX,1F SHL DX,01 SUB DX,BA JMP 100 Дизассемблируйте эти инструкции и прследите их выполнение вплоть до JMP, и просмотрите значение в регистре DX после выполнения программы. 11. Каково назначение инструкции INT? 12. Используйте DEBUG для создания и выполнения программы, выводящей на экран фразу "Coffee Break". Начните с А 100 для ввода инструкций и с А 120 для ввода фразы (не забудьте про ограничитель $). Подсказка: обратитесь к параграфу "Использование INT для вывода на дисплей ". Глава 3. Память компьютера и отслеживание исполнения инструкций 79
13. Используйте DEBUG для создания и запуска программы, запоминающей три введенных с клавиатуры символа и выводящей их на экран. (а) Начните с А 100. (б) Используйте INT 16 для помещения символа в AL и скопируйте его из AL в ячейку с адресом [200]. (в) Опять используйте INT 16 для помещения символа в AL и скопируйте его из AL в ячейку с адресом [201]. (г) В третий раз используйте INT 16 для помещения символа в AL и скопируйте его из AL в ячейку с адресом [202]. (д) Используйте команду Е 123 '$' для установки ограничителя $ после трех введенных символов. (е) Наконец, используйте INT 21 для вывода символов. Подсказка: воспользуйтесь параграфом данной главы "Использование INT для ввода с клавиатуры ". 80 Часть I. Основы аппаратного и программного обеспечения PC
Часть II Основы программирования на языке Ассемблер Глава 4. Требования к написанию программ на языке Ассемблер Глава 5. Трансляция, компоновка и исполнение программ Глава 6. Символьные инструкции и адресация Глава 7. Логические операции и управляющие структуры
Глава 4 Требования к написанию программ на языке Ассемблер Цель: описать базовые требования к написанию программ на Ассемблере и определению элементов данных. Введение В гл. 3 мы рассматривали использование DEBUG для ввода и исполнения программ в машинных кодах. Несомненно, вы убедились в том, что машинный код труден для восприятия даже при небольшом объеме программы. Вы также использовали команду А программы DEBUG для ввода небольших программ на языке Ассемблер. Как вы, наверное, заметили, такие программы намного легче для понимания, чем написанные в машинных кодах. Использование команды А в DEBUG -только временная мера, поскольку, начиная с этой главы, вы начнете разработку куда больших программ и вам потребуются куда более эффективные средства их документирования и редактирования. Программа на Ассемблере пишется согласно набору строгих правил, при этом используется редактор или текстовый процессор для ввода ее в компьютер в виде файла и программа транслятора с языка Ассемблер для чтения этого файла и преобразования его в машинный код. В этой главе мы объясним основные требования к разработке ассемблерной программы: использование комментариев, общий формат записи программ, директивы управления листингом и требования к определению сегментов и процедур. Мы также рассмотрим общую организацию программы, включая её инициализацию и завершение выполнения. Наконец, рассмотрим требования к определению элементов данных. Два основных класса языков программирования - высокоуровневые и низкоуровневые. Программисты, пишущие на языках высокого уровня, таких как С или BASIC, используют сложные команды, каждая из которых может соответствовать множеству инструкций машинного кода. Программисты, использующие языки низкого уровня, напротив, используют простые символьные инструкции, каждая из которых соответствует одной инструкции машинного кода. Несмотря на то, что написание программ на языке высокого уровня проходит более быстро, у программ на языках низкого уровня есть свои преимущества: 82 Часть II. Основы программирования на языке Ассемблер
• они дают больше возможностей в управлении аппаратурой компьютера; • создают более компактный исполняемый код; • исходный код, созданный на основе программ на языках низкого уровня, исполняется быстрее. Весьма часто преимущества обоих типов языков пытаются сочетать в одной программе: пишут основную часть проекта на языке высокого уровня, а части, критичные по скорости исполнения или работающие непосредственно с аппаратурой - на ассемблере. Независимо от используемого вами языка программирования, это символьный язык, требующий преобразования в машинный код, который может быть исполнен компьютером. Языки высокого уровня используют компилятор, преобразующий исходный код в машинные инструкции (иногда говорят - в объектный код). Низкоуровневый язык использует программу-транслятор {ассемблер) для преобразования исходного листинга в объектный код. Затем программа- компоновщик преобразует объектный код, созданный из исходного кода на любом языке, в исполняемый модуль. Особенности языка Ассемблер Сначала мы рассмотрим некоторые элементы этого языка: комментарии к программам, зарезервированные слова, идентификаторы, предложения и директивы. Эти элементы составляют основу языка. Комментарии к программам Использование комментариев в тексте программы повышает ее читабельность, особенно для программ на Ассемблере, где назначение наборов инструкций часто неясно. Например, понятно, что инструкция MOV АН,ЮН помещает ЮН в АН, но зачем это делается - неясно. Комментарий начинается с символа точки с запятой (;). Где бы вы не поставили этот символ, все символы, стоящие в строке после него, воспринимаются ассемблером как комментарий. Комментарий может состоять из любых печатаемых символов или быть пустым. Комментарий может занимать целую строку, например, ;Вычисление коэффициента производительности или следовать за инструкцией, например, ADD АХ,ВХ* ;добавление к общей сумме Поскольку комментарий не включается в состав генерируемого кода, в состав исходного листинга можно включать любое количество комментариев, не Глава 4. Требования к написанию программ на языке Ассемблер 83 б*
оказывая влияния на размер результирующего кода. В этой книге все инструкции Ассемблера набраны заглавными буквами, а комментарии - строчными. Это сделано для улучшения читабельности программ. Технически можно использовать любые буквы для комментариев и инструкций. Еще один способ включения комментариев в листинг - использование директивы COMMENT, описанной в гл. 25. Зарезервированные слова Определенные имена в языке Ассемблер зарезервированы для определенных целей и используются только в определенных случаях. Эти слова делятся на следующие категории. • Инструкции, например, MOV или ADD, указывающие на операции, которые может выполнять компьютер. • Директивы, такие как END или SEGMENT, используемые для указаний ассемблеру. • Операторы, например, FAR или SIZE, используемые в составе выражений. • Предопределенные символы, например, @Data и @Model, возвращающие информацию вашей программе при трансляции. Использование зарезервированного слова не по назначению приведет к тому, что ассемблер выдаст сообщение об ошибке. Идентификаторы Идентификатор (или символ) - это имя, назначаемое вами элементу программы, на который вы хотите ссылаться. Есть два типа идентификаторов - имя и метка. 1. Имя ссылается на адрес или элемент данных, например, COUNTER в следующей директиве COUNTER DB О 2. Метка ссылается на адрес инструкции, процедуры или сегмента, например, MAIN и ВЗО в следующих предложениях: MAIN PROC FAR ВЗО: ADD BL,25 Имена и метки подчиняются одинаковым правилам. Идентификаторы могут содержать следующие символы: 84 Часть II. Основы программирования на языке Ассемблер
Категория Допустимые символы Буквы латинского алфавита: А—Z, а—z Цифры: 0—9 (но не в качестве первого символа) Специальные символы: знак вопроса (?), знак подчеркивания (_), знак доллара ($), знак @, точка (.) (но не в качестве первого символа) Первый символ идентификатора должен быть буквой или специальным символом, кроме точки. Поскольку Ассемблер использует зарезервированные слова, начинающиеся со знака @, вы должны избегать использования их в качестве собственных идентификаторов. По умолчанию ассемблер не различает строчные и заглавные буквы (при его запуске можно задать в командной строке директиву, которая заставит его различать заглавные и строчные буквы). Максимальная длина идентификатора - до 31 символа в MASM 6.0 и до 247 в более поздних версиях. Примеры идентификаторов - TOTAL, QTY250 и $Р50. Рекомендуется использовать выразительные, осмысленные идентификаторы. Имена регистров, такие как АН, ВХ, CS, зарезервированы для обращений к этим регистрам. Соответственно, встретив в листинге инструкцию ADD СХ,ВХ, ассемблер поймет, что она обращается к регистрам. Однако в инструкции MOV REGSAVE,CX ассемблер узнает имя REGSAVE только тогда, если оно было заранее объявлено. Предложения Программа на Ассемблере состоит из последовательности предложений. Есть два типа предложений. 1. Инструкции, например, MOV и ADD, которые ассемблер преобразует в машинный код. 2. Директивы, требующие от ассемблера выполнения определенных действий, например, определения элемента данных. Вот формат предложения с квадратными скобками, указывающими на необязательную часть: [идентификатор ] операция [ операнд (ы) ] [; комментарий] Идентификатор (если он есть), операция и операнды (если они есть) разделяются хотя бы одним пробелом или символом табуляции. В MASM 6.0 допускаются строки длиной до 132 символов, в более поздних версиях - до 512, хотя большинство программистов используют строки длиной не более 80 символов - это максимальное количество, помещающееся в строке на экране. Глава 4. Требования к написанию программ на языке Ассемблер 85
волов - это максимальное количество, помещающееся в строке на экране. Вот два примера предложений: Идентификатор Операция Операнд Комментарий Директива: COUNT DB 1 ;Имя, операция, операнд Инструкция: L30; №V AX, О ;Метка, операция, 2 операнда Идентификатор, операция и операнд могут начинаться с любого столбца. Однако постоянное использование одного и того же столбца в качестве начального во всей программе делает ее более читабельной. Кроме того, многие программы редакторов позволяют использовать табуляцию с шагом в 8 символов для разделения столбцов. Описанный ранее в параграфе "Идентификаторы " термин имя обозначает имя определенной директивы или элемента данных, а термин метка относится к имени инструкции. В дальнейшем мы будем использовать эти термины именно в таком смысле. Операция, которая должна быть преобразована в код, чаще всего используется для определения областей данных и записи инструкций. Для элемента данных используются операции, например, DB и DW, которые определяют поля, рабочие области или константы. Для инструкций, например, MOV или ADD, операции указывают на действие, подлежащее выполнению. Операнд (если он есть) содержит информацию, обрабатываемую операцией. Для элемента данных операнд определяет начальное значение. Например, в следующем примере элемент данных COUNTER операция DB определяет как байт, а операнд указывает на присвоение ему значения 0: Имя Операция Операнд Комментарии COUNTER DB 0 ; Определяем байт с начальным значением 0 В случае инструкции операнд указывает, где должно быть предпринято указанное действие. Операнд инструкции может состоять из 1, 2, 3 элементов или не иметь элементов вообще. Вот три примера: Операция Операнд Комментарий RET ;Вернуться из процедуры INC ВХ ;Увеличить значение регистра ВХ на 1 ADD СХ,25 ;Добавить 25 к регистру СХ Директивы В языке Ассемблер предусмотрен ряд предложений, позволяющих вам управлять способом трансляции и вывода программы. Эти предложения, называемые 86 Часть II. Основы программирования на языке Ассемблер
директивами, действуют только при трансляции программы и не создают исполняемого кода. Наиболее часто используемые директивы рассмотрены в нескольких следующих параграфах. Все директивы подробно описаны в гл. 25, которую можно использовать в качестве справочника. Директивы PAGE и TITLE Эти директивы позволяют управлять форматом вывода на экран программы на Ассемблере. Это их единственное назначение, и они не оказывают воздействия на ход исполнения программы. В начале программы директива PAGE указывает максимальное количество строк, выводимых на одной странице, и максимальное количество символов в строке. Ее формат таков: PAGE [длина] [, ширина] Например, директива PAGE 60,132 указывает разместить 60 строк на странице (длина), а в строке - 132 символа {ширина). В типичном ассемблере число строк на странице может меняться от 10 до 255, а число символов в строке - от 60 до 132. Отсутствие директивы PAGE приводит к установке режима с 50 строками на странице и 80 символами в строке. Предположим, что программа с помощью директивы PAGE устанавливает максимальное число строк равным 60. При печати ассемблерной программы по достижении 60-й строки происходит автоматический переход к началу следующей страницы и увеличение значения счетчика страниц. Вы также можете захотеть, чтобы страница завершилась на определенной строке, например, на конце сегмента. В таком случае добавьте в эту строку директиву PAGE без операндов. Когда ассемблер достигнет этой директивы, он перейдет на следующую страницу и продолжит вывод. Директива TTTLE может использоваться для вывода заголовка программы на второй строке каждой страницы листинга. Директиву ТПЪЕ можно использовать в начале программы и только один раз. Ее формат таков: TITLE текст [комментарий] В качестве текста обычно указывается имя файла программы на диске. Например, если программа называется ASMSORT, укажите это имя и необязательный описательный комментарий (символ начала комментария (;) не требуется), например, так: TITLE ASMSORT Ассемблерная программа для сортировки названий CD Глава 4. Требования к написанию программ на языке Ассемблер 87
Директива SEGMENT В гл. 2 мы упоминали, что ассемблерная программа в формате .ЕХЕ состоит из одного или более сегментов. В реальном режиме сегмент стека содержит специальным образом организованное хранилище - стек, сегмент данных содержит элементы данных, а сегмент кода - исполняемый код. Директивы для определения сегментов, SEGMENT и ENDS, имеют следующий формат: Имя имя сегмента имя сегмента Операция SEGMENT ENDS Операнд [выравнивание], [совмещение] [•класс•] Предложение SEGMENT определяет начало сегмента. Имя сегмента должно присутствовать, быть уникальным и соответствовать соглашениям об именах, принятым в Ассемблере. Предложение ENDS указывает на конец сегмента и содержит то же имя, что и предложение SEGMENT. Максимальный размер сегмента в реальном режиме - 64 килобайта. Операнд предложения SEGMENT может содержать опции трех разновидностей. • Опция выравнивание указывает границу, с которой начинается сегмент. Типичное требование - PARA, указывающее на начало сегмента на границе параграфа, так что адрес начала нацело делится на 16 (ЮН). Пропуск операнда выравнивания вызывает установку выравнивания по умолчанию в PARA. • Опция совмещение указывает, должны ли совмещаться этот сегмент и другие, когда объектный код компонуется после трансляции (этот вопрос рассмотрен в параграфе "Компоновка объектного файла" гл. 5). В качестве опции совмещения можно задать STACK, COMMON, PUBLIC или выражение (expression) AT. Например, сегмент стека обычно определяется так: имя сегмента SEGMENT PARA STACK Вы можете использовать значения опции совмещения PUBLIC и COMMON, если хотите скомпоновать отдельно оттранслированные программы в один модуль. В противном случае пропустите эту опцию или задайте значение NONE. • Опция класс, заключаемая в апострофы, используется для группирования родственных сегментов при компоновке. В этой книге класс 'code1 используется для сегментов кода (согласно рекомендации Microsoft), 'data1 - для сегментов данных, и 'stack' - для сегментов стека. В примере ниже (рис. 4.1) показаны предложения SEGMENT с различными опциями. Заметьте, что программа определяет сегмент стека с выравниванием (PARA), совмещением (STACK) и классом ('Stack'). 88 Часть II. Основы программирования на языке Ассемблер
TITLE STACK STACK 7 DATASEG DATASEG CODESEG MAIN MAIN CODESEG page A04ASM1 SEGMENT ENDS SEGMENT ENDS SEGMENT PROC FAR ENDP ENDS END MAIN 60,132 Сегменты в PARA STACK PARA 'Data PARA 'Code программе 'Stack' • ;Конец ;Конец ;Конец .EXE процедуры сегмента программы « Рис. 4.1. Определение сегментов в программе типа .ЕХЕ Директива PROC Сегмент кода содержит исполняемый код программы, состоящий из одной или более процедур, начинающихся директивой PROC и заканчивающихся директивой ENDP. Рассмотрим формат процедуры. Имя имя процедуры имя процедуры Операция PROC ENDP Операнд FAR Комментарий ;Начало процедуры /Конец процедуры Имя процедуры должно присутствовать обязательно, должно быть уникальным и должно соответствовать соглашениям об именах, принятых в Ассемблере. Операнд (в данном случае FAR) связан с выполнением программы. Когда вы запрашиваете выполнение программы, загрузчик использует эту процедуру, как начальную точку для определения первой подлежащей исполнению инструкции. Директива ENDP указывает на конец процедуры и содержит то же имя, что и предложение PROC, чтобы позволить ассемблеру соотнести конец процедуры и ее начало. Поскольку процедура должна полностью содержаться в одном сегменте, ENDP завершает процедуру перед тем, как ENDS определяет конец сегмента, как показано на рис. 4.1. Сегмент кода может содержать любое количество процедур, используемых в качестве подпрограмм, и каждая из процедур при этом должна выделяться парой предложений PROC и ENDP. Каждая дополнительная директива PROC обычно задается с операндом NEAR (явно или по умолчанию), как показано в гл. 7. Глава 4. Требования к написанию программ на языке Ассемблер 39
Директива END Как уже упоминалось, директива ENDS завершает сегмент, а директива ENDP - процедуру. Директива END завершает всю программу и является последним ее предложением, как показано на рис. 4.1. Ее формат (операция и операнд) таков: END [имя процедуры] Операнд может отсутствовать, если программа не предназначена для непосредственного исполнения; например, вы хотите только указать определения данных или связать программу с другим модулем. В большинстве программ операнд содержит имя единственной (или первой) процедуры, определенной как FAR, с которой начинается исполнение программы. Директива ASSUME Программа типа .ЕХЕ использует регистр SS для обращений к стеку, DS - для обращений к сегменту данных, и CS - для обращений к сегменту стека. Вы должны указать ассемблеру назначение каждого сегмента в программе. Для этого применяется директива ASSUME, записываемая в сегменте кода в таком виде: ASSUME SS: имя_стека, DS: имя_сетмента_даниых, CS: имя_сегмента_кода, . . . SS:uMH_cmeKa указывает ассемблеру связать с регистром SS сегмент с указанным именем и поступить таким же образом с другими заданными операндами. Операнды могут располагаться в любом порядке. ASSUME может также содержать запись для регистра ES. Если ваша программа не использует сегмент ES, вы можете не указывать этого или указать на неиспользование этого сегмента при помощи ES:NOTHING. Начиная с MASM 6.0, ассемблер создает директиву ASSUME для сегмента кода. Как и остальные директивы, ASSUME - просто сообщение, помогающее ассемблеру преобразовать символьный код в машинный. Вам все равно может понадобиться написать инструкции, загружающие адреса сегментов в соответствующие регистры, когда программа начинает выполняться. Директивы процессора Большинство ассемблеров полагают, что программа будет выполняться на простейшей машине с процессором 8086. Как следствие, если вы хотите использовать более сложные инструкции или свойства, появившиеся в последующих процессорах, вы должны сообщить ассемблеру об этом при помощи директивы процессора, например, .286, .386, .486, .586. Директива может даваться непосредственно перед инструкцией, перед сегментом кода или даже в начале программы для защищенного режима. Кроме того, использование расширенных регистров, например, ЕАХ, требует наличия директивы .386. Вот некоторые инструкции, требующие определенных директив процессора: 90 Часть II. Основы программирования на языке Ассемблер
.286 .386 .486 POPA MOVSX/MOVZX CMPXCHG PUSHA SHLD / SHRD XADD Директивы определения сегментов Два основных типа исполняемых программ - .СОМ и .ЕХЕ. Сейчас мы рассмотрим требования к программам типа .ЕХЕ, а программы .СОМ рассмотрены в гл. 5. На рис. 4.2 показана структура программы типа .ЕХЕ, включая определение сегментов кода, данных и стека. 1 2 4 с 6 7 8 Q 10 1 1 12 13 14 15 16 17 18 19 20 21 22 TITLE STACK STACK DATASEG DATASEG CODESEG MAIN MAIN CODESEG page 60, A04A8M1 SEGMENT ENDS SEGMENT ENDS SEGMENT PROC ASSUME MOV MOV MOV INT ENDP ENDS END AX DS AX 132 Скелет программы .ЕХЕ PARA STACK 'Stack' PARA 'Data' PARA 'Code' FAR SS:STACK,DS:DATASEG,CS:CODESEG DATASEG ;Поместить адрес сегмента AX /данных в регистр DS 4C00H ;Завершение работы 21H MAIN /Конец процедуры ;Конец сегмента ;Конец программы Рис. 4.2. Скелет программы типа .ЕХЕ Далее программа описывается построчно. Строка Пояснение 1 Используется директива PAGE для определения 60 строк и 132 столбцов на странице 2 Используется директива TITLE для определения имени программы A04ASM1 Глава 4. Требования к написанию программ на языке Ассемблер 91
3 Комментарии в строках 3, 7 и 11 разделяют три отдельных сегмента программы 4—6 Определяется сегмент стека STACK (но не его содержимое в этом примере) 8—10 Определяется сегмент данных DATASEG (но не его содержимое) 12 Определяется сегмент кода CODESEG 13—20 Определяется единственная процедура сегмента, названная в этом примере MAIN. Эта процедура выполняет обычные действия, необходимые для инициализации и завершения программы типа .ЕХЕ. Для инициализации необходимо: (1) сообщить ассемблеру, какие сегменты связать с сегментными регистрами, и (2) загрузить в DS адрес сегмента данных 14 Используется директива ASSUME для указания ассемблеру пар сегментов и сегментных регистров, в данном примере - STACK и SS, DATASEG и DS, CODESEG и CS: ASSUME SS:STACK,DS:DATASEG,CS:CODESEG Связывая имена сегментов с сегментными регистрами, ассемблер может определить адреса смещения элементов в стеке, элементов данных в сегменте данных и инструкций в сегменте кода. Например, каждая машинная инструкция в сегменте кода имеет определенную длину. Первая инструкция размещается в ячейке со смещением 0 и, если она имеет длину 2 байта, то следующая инструкция имеет смещение 2, и так далее 15,16 Инициализация адреса сегмента данных в DS: MOV AX,DATASEG ;Получить адрес сегмента данных MOV DS,AX ;Сохранить адрес в DS Первая инструкция MOV загружает адрес сегмента данных в регистр АХ. Вторая - загружает его из АХ в DS. Две инструкции MOV необходимы потому, что процессор позволяет вам заносить в сегментный регистр только данные из регистра общего назначения. Поэтому предложение MOV DS,DATASEG неверно - оно пытается занести в адресный регистр значение из памяти. В гл. 5 инициализация сегментных регистров рассмотрена более подробно 18,19 ' Запрос завершения выполнения программы и возвращения в операционную систему. Далее эта процедура рассмотрена более подробно 92 Часть II. Основы программирования на языке Ассемблер
22 Используется директива END, указывающая транслятору на окончание исходной программы. Операнд MAIN означает, что процедура MAIN будет использоваться в качестве точки входа для дальнейшего исполнения программы. Вместо MAIN можно использовать любое другое имя, корректное по стандартам Ассемблера, но в PROC, ENDP и END должно использоваться одно и то же имя Последовательность определения сегментов обычно несущественна. На рис. 4.2 они определены в следующем порядке: STACK SEGMENT PARA STACK 'Stack' DATASEG SEGMENT PARA 'Data' CODESEG SEGMENT PARA 'Code' Заметьте, что программа на рисунке записана на символьном языке. Чтобы выполнить ее, вы должны использовать ассемблер и компоновщик для преобразования ее в исполняемый машинный код в виде программы типа .ЕХЕ. Как указано в гл. 2, когда загрузчик считывает программу типа .ЕХЕ с диска в память для исполнения, он создает 256-байтный (100Н) PSP (префикс сегмента программы) на границе параграфа в доступной внутренней памяти и сохраняет программу сразу следом за ним. После этого загрузчик • инициализирует адрес сегмента кода в CS; • инициализирует адрес стека в SS; • инициализирует адрес PSP в DS и ES. Загрузчик инициализирует CS:IP и SS:SP, чтобы процессор мог получить доступ к сегментам стека и кода. Однако ваша программа обычно будет требовать доступ и к сегменту данных DS (а так же и к ES), кроме адреса PSP. Как следствие, вы должны инициализировать DS адресом сегмента данных, как это сделано в примере (рис. 4.2) инструкциями MOV. Теперь, даже если суть инициализации пока недостаточно ясна, не волнуйтесь: все программы типа .ЕХЕ инициализируются почти одинаково, так что вы можете просто повторять приведенные здесь шаги при написании своих программ. Завершение выполнения программы INT 21H - обычное прерывание DOS, выполняющее различные действия в зависимости от значения в регистре АН. Среди функций INT 21Н - работа с клавиатурой и экраном, обращения к диску, передача данных на принтер. Функция, интересующая нас в данный момент, выполняется при коде в регистре АН 4СН - это завершение программы. Эту операцию также можно использовать для Глава 4. Требования к написанию программ на языке Ассемблер 93
передачи кода возврата в AL с целью последующего тестирования в пакетном файле (через предложение IF ERRORLEVEL), например, так: ; Запрос на завершение обработки MOV АН,4СН ;Передача кода операции MOV AL,retcode необязательный код возврата INT 21H ;Вызов обработчика прерывания Код возврата нормального завершения программы обычно равен 0. Вы можете заменить две инструкции MOV, приведенные выше, одной инструкцией (как это сделано в примере на рис. 4.2): MOV АХ,4С00Н /Запрос на нормальное завершение Пример исходной программы На рис. 4.3 собрана ранее изложенная информация в едином простом, но полном листинге ассемблерной программы, суммирующем два элемента данных в регистре АХ. Необходимые сегменты определяются так. • STACK содержит одну запись, DW (define word - определить слово), определяющую 32 слова, инициализированных нулем - адекватный размер для маленьких программ. • DATASEG определяет три слова, названные FLDD (инициализированное значением 215), FLDE (инициализированное значением 125), и FLDF (неинициализированное). • CODESEG содержит исполняемые инструкции для программы, хотя первые два предложения - PROC и ASSUME - не генерируют исполняемого кода. Директива ASSUME указывает ассемблеру выполнить следующие действия: • назначить STACK регистру SS, в результате процессор использует адрес в SS для обращения к сегменту STACK; • назначить DATASEG регистру DS, в результате процессор использует адрес в DS для обращения к сегменту DATASEG; • назначить CODESEG регистру CS, в результате процессор использует адрес в CS для обращения к сегменту CODESEG. При загрузке программы с диска в память для исполнения загрузчик устанавливает правильные адреса сегментов в SS и CS, но, как показано первыми двумя инструкциями MOV, программа должна инициализировать DS (и, обычно, ES). Трансляцию, компоновку и исполнение этой программы мы рассмотрим в гл. 5, 94 Часть II. Основы программирования на языке Ассемблер
TITLE STACK STACK DATASEG FLDD FLDE FLDF DATASEG CODESEG MAIN MAIN CODESEG page A04ASM1 SEGMENT DW ENDS SEGMENT DW DW DW ENDS SEGMENT PROC ASSUME MOV MOV MOV ADD MOV MOV INT ENDP ENDS END 60,132 (EXE) Операции перемещения и сложения PARA STACK 32 DUP(O) PARA 'Data' 215 125 ? PARA 'Code' FAR SS:STACK,DS AX,DATASEG DS,AX AX,FLDD AX,FLDE FLDF,AX AX,4C00H 21H MAIN Stack' :DATASEG,CS:CODESEG ;Поместить адрес ;сегмента данных в DS /Поместить 0215 в АХ ;Прибавить 0125 к АХ ;Сохранить сумму в FLDF ;Завершить работу ;Конец процедуры ;Конец сегмента ;Конец программы Рис. 4.3. ЕХЕ-программа с обычными сегментами Упрощенные директивы сегментов Ассемблер предлагает несколько способов упрощения определения сегментов. Для их использования перед определением сегментов вы должны определить модель памяти. Модель памяти указывает ассемблеру, как использовать сегменты, резервировать достаточное пространство для объектного кода и обеспечивать оптимальную скорость выполнения. Формат (включая точку в начале) таков: .MODEL модель памяти Модель памяти может быть одной из следующих: Tiny, Small, Medium, Compact, Large, Huge, Flat. В MASM 6.0 и TASM 4.0 модель Tiny предназначена для использования в программах типа .СОМ, размещающих свои данные, стек и исполняемый код в одном сегменте размером до 64 Кбайт. Модель Flat определяет одну область для кода и данных размером до 4 Гбайт, использует 32-разрядные Глава 4. Требования к написанию программ на языке Ассемблер 95
адреса и работает под управлением Windows в защищенном режиме. Характеристики остальных моделей приведены ниже. Модель Количество сегментов кода и Количество сегментов данных и их размер их размер Small 1 <= 64 К 1 <= 64 К Medium Любое количество, любой размер 1 <= 64 К Compact 1 <= 64 К Любое количество, любой размер Large Любое количество, любой размер Любое количество, любой размер Huge Любое количество, любой размер Любое количество, любой размер Любую из этих моделей можно использовать для самостоятельной (не связанной с другими) программы. Модель Small подходит для большинства примеров в этой книге; ассемблер полагает, что все адреса - ближние (near), т.е. в пределах 64 Кбайт, и генерирует 16-разрядные адреса смещения. В модели Compact ассемблер, напротив, генерирует 32-разрядные адреса смещения, а результирующий код потребует больше времени для исполнения. Модель Huge отличается от Large тем, что может содержать переменные, например, массивы, размером более 64 К. Директива .MODEL автоматически генерирует соответствующее предложение ASSUME для всех моделей. Формат директив (включая точку в начале), определяющих сегменты кода, данных и стека, следующий: .STACK [размер] .DATA .CODE [имя сегмента] Каждая из этих директив вызывает генерацию соответствующего предложения SEGMENT и связанного с ним ENDS. Имена сегментов по умолчанию (их не требуется определять) - STACK, _DATA, и _ТЕХТ (сегмент кода для моделей Tiny, Small, Compact и Flat). Знак подчеркивания в начале _DATA и TEXT поставлен намеренно. Размер стека по умолчанию равен 1024 байтам, но вы можете его изменить. Как указано выше, можно также изменить имя сегмента кода. Эти директивы используются для определения того, где в программе должны быть расположены эти сегменты. Заметьте, что инструкции, используемые теперь для инициализации адреса сегмента данных в регистре DS, таковы: MOV AX,@data инициализируем DS MOV DS,AX /адресом сегмента данных На рис. 4.3 приведен пример программы, использующий стандартные директивы для определения сегментов. Рис. 4.4 приводит тот же пример, но использующий директиву .MODEL для определения модели памяти и определения сегментов директивами .STACK, .DATA, и .CODE. Как видите, в четвертой 96 Часть II. Основы программирования на языке Ассемблер
строке выбрана модель памяти .SMALL. Размер стека определен в 32 слова (64 байта). Заметьте, что ассемблер не генерирует предложений SEGMENT и ENDS, и вам не нужно использовать предложение ASSUME. TITLE # FLDD FLDE FLDF # MAIN MAIN page A04ASM2 .MODEL .STACK .DATA DW DW DW .CODE PROC MOV MOV MOV ADD MOV MOV INT ENDP END 60,132 (EXE) Операции перемещения и сложения SMALL 64 215 125 ? FAR AX,@data DS,AX AX,FLDD AX,FLDE FLDF,AX AX,4C00H #21H MAIN ;Определение стека Определение сегмента данных ;Определение сегмента кода ;Поместить адрес ;сегмента данных в регистр DS ;Прибавить 0215 к АХ ;Прибавить 0125 к АХ ;Поместить сумму в FLDF ;Завершить работу ;Конец процедуры ;Конец программы Рис. 4.4. ЕХЕ-программа с упрощенными сегментными директивами Как вы увидите в следующей главе, обработка ассемблером программ, использующих стандартное объявление сегментов, немного отличается от обработки программ, использующих упрощенные директивы и модели памяти. Директивы .STARTUP и .EXIT В MASM 6.0 появились директивы .STARTUP и .EXIT, упрощающие инициализацию и завершение программы. Директива .STARTUP генерирует инструкции для инициализации сегментного регистра, а .ЕХГГ - вызывает обработчик прерывания INT 21H с кодом функции 4СН для завершения программы. Эти директивы требуют использования директивы .MODEL и работают со всеми моделями памяти, кроме Flat. С целью изучения языка Ассемблер в этой программе используются не эти директивы, а соответствующий им набор инструкций языка. Глава 4. Требования к написанию программ на языке Ассемблер 97 7 — 5-741
Инициализация в защищенном режиме Под управлением Windows все программы работают в защищенном режиме и используют модель памяти Flat. Адресуя сегменты 32-разрядными регистрами, можно использовать до 4 Гбайт памяти. Общая структура программы такого типа имеет следующий вид: .386 или .486 ;Сначала - директива процессора .MODEL FLAT, STDCALL .STACK .DATA ;Bce данные объявляются после этой директивы .CODE ;Далее следует исполняемый код END Использование директивы процессора перед предложением .MODEL заставляет ассемблер применить 32-разрядную адресацию. Запись STDCALL указывает ассемблеру применять стандартные соглашения об именах и вызовах процедур. Процессор работает эффективнее, поскольку не требуется преобразования адресов из формата сегментхмещение в действительные. В то время как в реальном режиме смещения для данных и инструкций 16-разрядные, в защищенном режиме они 32-разрядные. Использование DWORD для выравнивания сегментов по 32-разрядным (соответствующим двойному слову) адресам ускоряет достуц к памяти по 32- разрядной шине данных. Тип USE32 указывает ассемблеру генерировать код, соответствующий 32-разрядному защищенному режиму: имя_сегмента SEGMENT DWORD USE32 Поскольку в этих процессорах регистр DS все еще 16-разрядный, его инициализация может выглядеть так: MOV EAX,DATASEG /Получить адрес сегмента данных MOV DS,AX /Загрузка 16-разрядной части Инструкции STI, CLI, EST, OUT, разрешенные в реальном режиме, запрещены в защищенном. Определение типов данных Как уже говорилось, сегмент данных в программе типа .ЕХЕ содержит кон- станты^ рабочие области и области ввода/вывода. Ассемблер предоставляет набор директив, позволяющих определять элементы данных различных типов и размеров: например, DB определяет байт, a DW - слово. Элемент данных может содержать неопределенное (не проинициализированное) значение или инициализированную константу, определенную как строку символов или числовое значение. Вот формат определения данных: 98 Часть II. Основы программирования на языке Ассемблер
[имя] Dn выражение Имя Программа, ссылающаяся на элемент данных, использует для ссылки имя этого элемента данных. Имя, как указывают квадратные скобки, задавать не обязательно. Ранее в параграфе "Идентификаторы " приведены правила составления имен. Директива (Dn) Директивы, используемые для определения элементов данных: DB (Define Byte - определить байт), DW (Define Word - определить слово), DD (Define. Doubleword - определить двойное слово), DF (Define Farword - определить дальнее слово), DQ (Define Quadword - определить учетверенное слово) и DT (Define Tenbytes - определить удесятеренное слово), каждая из которых явно определяет размер выделяемой области. В MASM 6.0 для этих директив появились синонимы BYTE, WORD, DWORD, FWORD, QWORD и TWORD, соответственно. Однако исходные обозначения все еще широко применяются во многих версиях Ассемблера. Выражение Выражение - операнд, который может указывать неинициализированное значение или константу. Для указания неинициализированного значения укажите в качестве операнда знак вопроса (?), например: DATAX DB ? /Неинициализированный элемент В этом случае, когда программа начнет выполняться, начальное значение DATAX будет вам неизвестно. Обычно в элемент данных при определении записывается какое-то значение, но оно должно соответствовать размеру элемента. Вы можете использовать операнд для определения константы, например: DATAY DB 25 инициализированный элемент Вы можете свободно использовать инициализированное значение 25 в вашей программе или изменять его. Выражение может содержать несколько констант, разделенных запятыми и ограниченных только длиной строки: DATAZ DB 21, 22, 23, 24, 25, 26, ... Ассемблер определяет эти константы в соседних байтах, слева направо. Ссылка на DATAZ возвращает первый байт - 21 (это байт со смещением DATAZ+0), а ссылка на DATAZ+1 вернет вторую константу - 22, и т.д. Например, инструкция MOV AL,DATAZ+3 Глава 4. Требования к написанию программ на языке Ассемблер gg 7*
загружает значение 24 (18Н) в регистр AL. Выражение также допускает повторение констант в выражении в виде [имя] Dn число повторений DUP(выражение) ... Следующие ниже примеры иллюстрируют повторение. DW 10 DUP(?) * ;Десять неинициализированных слов DB 5 ШР(12) ;Пять байтов, содержащих 0C0C0C0C0CH DB 3 DUP(5 DUP(4)) ;Пятнадцать четверок Третья строка генерирует пять копий цифры 4 (44444) и повторяет их 3 раза, давая в результате 15 четверок. Выражение может определять символьную строку или числовую константу. Символьные строки Символьные строки используются для описательных текстовых данных, например, имен людей или названий предметов. Строка выделяется одинарными кавычками, например, 'PC, или двойными кавычками, например, "PC". Ассемблер хранит содержимое кавычек как объектный код в формате ASCII и без кавычек. Странно, но в MASM формат DB (или BYTE) - единственный формат, определяющий символьную строку длиной более двух символов с символами, хранящимися с выравниванием налево и в нормальном порядке слева направо (как в европейских языках). Соответственно, DB - единственный формат для определения символьных данных любой длины. Например: • DB 'Computer City' Если строка должна содержать одинарную или двойную кавычку, вы можете определить ее одним из двух способов: DB "Crazy Sam's CD Emporium" ;Двойные кавычки для определения ;строки, а одинарная - в качестве ;апострофа DB 'Crazy Sam"s CD Emporium' ;Одинарные кавычки для определения /строки, а двойная - в качестве ;апострофа Числовые константы Числовые константы используются для определения арифметических значений и адресов памяти. Константы определяются без кавычек, но за ними следует необязательный указатель основания системы счисления, например, Н в шестнадцатеричном числе 1С2Н. Для большинства директив определения данных ассемблер хранит сгенерированные байты в обратной последовательности, т.е. справа налево. Далее описываются различные форматы чисел. 100 Часть II. Основы программирования на языке Ассемблер
Двоичный Двоичный формат использует двоичные цифры 0 и 1, а числа в этом формате обозначаются указанием основания системы счисления В. Обычно двоичный формат используется для значений побитовых операций AND, OR, XOR и TEST. Десятичный Десятичный формат использует цифры от 0 до 9 и не требует обязательного использования указателя основания системы счисления D, так что 125D = 125. Хотя ассемблер для упрощения ввода позволяет вам записывать данные в десятичном формате, он преобразует их в двоичную форму и в объектном коде представляет их в шестнадцатеричном формате. Например, 125D преобразуется в 7DH. Шестнадцатеричный Шестнадцатеричный формат использует соответствующие цифры от 0 до F и указатель основания системы счисления Н. Поскольку ассемблер ожидает, что ссылка, начинающаяся с буквы, представляет собой символьное имя, первый разряд шестнадцатеричного числа должен быть в пределах от 0 до 9. Например, 3DH и 0DE8H, которые ассемблер сохранит в виде 3D и (из-за перестановки байт) E80D, соответственно. Поскольку ассемблер преобразует все числовые значения в двоичные (и представляет их в шестнадцатеричном формате), определения десятичного числа 12, шестнадцатеричного С и двоичного 1100 генерируют одно и то же значение: двоичное 00001100 или шестнадцатеричное ОС, в зависимости от того, как вы просматриваете содержимое байта. Поскольку буквы D и В имеют смысл и как указатели основания системы счисления, и как шестнадцатеричные цифры, они могут вызвать сложности. Как следствие, в MASM 6.0 указатель Т (Теп) используется в качестве указателя для обозначения десятичной системы счисления, a Y (binarY) - двоичной. Действительный Ассемблер преобразует указанное действительное число (десятичную или шестнадцатеричную константу с указателем основания системы счисления R) в число в формате с плавающей запятой для использования математическим сопроцессором. Уясните себе разницу в использовании символов и числовых констант. Например, символьная константа, определенная как DB '24', сгенерирует 2 символа ASCII и будет представлена в памяти как 3234Н. Числовая константа DB 24 генерирует двоичное число и представляется в памяти в виде 18Н. Глава 4. Требования к написанию программ на языке Ассемблер 101
Байт Слово Двойное слово Дальнее слово Учетверенное слово Десять байт DB DW DD DF DQ DT Директивы для определения данных В следующей таблице приведены директивы определения элементов данных и соответствующие им директивы, введенные в MASM 6.0. Определение Обычная директива Директива MA.SM 6.0 BYTE WORD* DWORD FWORD QWORD TBYTE В этом тексте используются обычные директивы из-за их широкого распространения. В ассемблерной программе, приведенной на рис. 4.5, показаны примеры использования директив DB, DW, DD и DQ для определения символьных строк и числовых констант. Сгенерированный объектный код, который рекомендуется разобрать, приведен в левой части рисунка. Заметьте, что шестнадцате- ричный код, соответствующий неинициализированным элементам данных, состоит из нулей. Поскольку эта программа состоит только из сегмента данных и не содержит исполняемых инструкций, она не пригодна для выполнения. DB или BYTE Может определять одно или более численных значений величиной в 1 байт или, соответственно, 2 шестнадцатеричных разряда. Для положительных чисел диапазон допустимых значений - от 0 до 255, для чисел со знаком - от -128 до 127. Ассемблер преобразует числовые константы в двоичный объектный код (представленный в шестнадцатеричном формате). На рис. 4.5 первое определение BYTE1 использует "?" для указания неопределенного значения. Числовые DB константы BYTE2, BYTE3, BYTE4 и BYTE5 имеют различные значения. На их примере продемонстрировано применение различных методов присваивания начальных значений. Например, ассемблер преобразовал заданное десятичное значение 48 в шестнадцатеричное ЗОН. Символьное выражение DB может содержать последовательность символов любого размера вплоть до предельной длины строки. Пример - символьные строки в объявлении элементов BYTE6 и BYTE7. Шестнадцатеричный объектный код показывает символ ASCII в каждом байте в обычном порядке слева направо, а 20Н соответствует пустому символу (пробелу). 102 Часть II. Основы программирования на языке Ассемблер
0000 00 0001 30 0002 30 0003 7А 0004 000А[ 000Е 50 43 6F 72 0019 31 32 001Е 01 4А 65 62 002А FFFO 002С 007А 002Е 001Е 0030 0002 0007 003А0008[ 00 20 69 33 61 03 R ] 45 75 34 6Е 4D 6D 6D 35 02 61 0004 0006 0009 0000 ] 004А 00000000 004Е 0000А25А 0052 00000018 005А 00000001 70 46 72 00000030 005Е 0000000000000000 0066 395Е000000000000 006Е 5АА2000000000000 TITLE / BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE 6 BYTE7 BYTE 8 ; / WORDl WORD2 WORD3 WORD4 WORD5 '' / DWORD1 DWORD2 DWORDS DWORD4 ; QWORDl QWORD2 QWORD3 Page 60,132 A04DEFIN (EXE) .MODEL SMALL .DATA DB DB DB DB DB DB DB DB DB DW DW DW DW DW DW DD DD DD DD DD DQ DQ DQ DQ Директивы данных - определение байтов: ? 48 ЗОН 01111010В 10 DUP(0) ;Без указания значения /Десятичная константа /Шестнадцатеричная ;константа ;Двоичная константа ;Десять нулей 'PC Emporium' •12345' 01,'Jan',02 ;Строка символов ;Цифры как символы ,'Feb',03,'Mar' ;Таблица месяцев - определение слов: 0FFF0H 01111010B BYTES 2,4,6,7,9 8 DUP(0) ; Шестнадцатеричная ;константа ;Двоичная константа ;Константа адреса /Таблица из ;5 констант ;Восемь нулей - определение двойных слов: ? 41562 24, 48 ;Без значения /Десятичное значение ;Две константы BYTE3 - BYTE2 - определе 0 05Е39Н 41562 END ;Разность между адресами ние счетверенных слов: ; Нуль ; Шестнадцатеричная ;константа ;Десятичная константа Рис. 4.5. Определение символьных и числовых констант Глава 4. Требования к написанию программ на языке Ассемблер \ 03
DW или WORD Эта директива определяет элемент данных длиной в одно слово (2 байта). Числовое выражение - операнд директивы DW - может определять одну или более констант длиной в слово каждая. Для положительных чисел допускаются значения от 0 до 65535; для чисел со знаком - от -32768 до +32767. Ассемблер преобразует числовые константы DW в двоичный объектный код и сохраняет в последовательности с переставленными байтами. Соответственно, десятичное число 12345 преобразуется в 3039Н и будет сохранено в виде 3930Н. На рис. 4.5 WORD1 и WORD2 определяют числовые константы, WORD3 определяет операнд как адрес - в этом случае адрес смещения BYTE8 (R справа означает relocatable - перемещаемый), а посмотрев на рисунок, можно убедиться, что адрес BYTE8 (первый слева столбец) действительно 00IE. WORD4 определяет таблицу из 5 числовых констант. Заметьте, что длина каждой константы равна слову (2 байтам). WORD5 определяет таблицу, проини- циализированную восемью нулевыми значениями. Поскольку символьное выражение DW в MASM ограничено 2 символами, оно редко используется для определения символьных строк. DD или DWORD Эта директива определяет элементы данных длиной в два слова (4 байта). Числовое выражение DD может определять одну или более констант, максимальный размер каждой из которых - 4 байта (8 шестнадцатеричных разрядов). Для положительных чисел это соответствует диапазону от 0 до 4294967295; для чисел со знаком - от -2147483648 до +2147483647. Ассемблер преобразует константы DD в двоичный объектный код (представляемый в шестнадцатеричном формате) и хранит байты в обратной последовательности. Это значит, что десятичное число 12345678 будет преобразовано в 00ВС614ЕН и записано в виде 4Е61ВС00Н. На рис. 4.5, значение DWORD2 определяет числовую константу, a DWORD3 - две числовые константы. Значение DWORD4 генерирует численную разность между двумя определенными ранее адресами, в данном случае, результат будет равен длине BYTE2, то есть 1. Символьное выражение для DD в MASM ограничено двумя символами и имеет те же свойства, что и DW. Ассемблер осуществляет правое выравнивание символов в 4-байтном двойном слове. DQ или QWORD Эта директива определяет элементы длиной в 4 слова (8 байт). Числовое выражение DQ может определять одну или более констант, максимальная длина каждой из которых равна 8 байтам или 16 шестнадцатеричным разрядам. Максимальное число, представимое в этом формате - 7FFFFFFFFFFFFFFFH. 104 Часть II. Основы программирования на языке Ассемблер
В качестве примера величины этого числа укажем, что шестнадцатеричное число, состоящее из 1 с последующими 15 нулями эквивалентно десятичному числу 1152921504606846976. Ассемблер обрабатывает числовые значения и символьные строки формата DQ точно так же, как DD и DW. На рис. 4.5 числовые значения QWORD1, QWORD2 и QWORD3 иллюстрируют этот формат. Просмотр сегмента данных Преобразование исходной программы в машинный код выполняется за два шага: необходимы ассемблирование (трансляция) и компоновка. Хотя ассемблер не выдаст сообщений об ошибке при трансляции программы, приведенной на рис. 4.5, компоновщик выдаст сообщение "Warning: no STACK Segment" и "There were 1 errors detected." Несмотря на это, вы можете использовать DEBUG для просмотра полученного объектного кода, показанного на рис. 4.6. Если вы заглянете вперед, в гл. 5, вы сможете оттранслировать и скомпоновать программу. После этого используйте DEBUG, чтобы загрузить результирующий файл типа .ЕХЕ на выполнение и введите команду D DS:100, чтобы просмотреть данные. Правая часть дисплея содержит ASCII-представление данных, например, строки "PC Emporium", в то время как в средней части дисплея показано шестнадцатеричное представление, в котором видно все содержимое просматриваемой области. Данные, выведенные на ваш дисплей, должны быть идентичны приведенным на рисунке для смещений от 0100 до 0175D. Адрес сегмента данных (на рисунке - 14AD) и данные, расположенные в ячейках с большим смещением, вероятно, будут отличаться. -d ds:100 14AD:0100 14AD:0110 14AD:0120 14AD:0130 14AD:0140 14AD:0150 14AD:0160 14AD:0170 00 20 61 02 00 00 00 00 < 30 30 7A 00 00 00 00-00 00 00 00 00 00 45 6D 70 6F 72 69 75-6D 31 32 33 34 35 6E 02 46 65 62 03 4D-61 72 F0 FF 7A 00 00 04 00 06 00 07 00-09 00 00 00 00 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 18 00 00 00 30 00-00 00 01 00 00 00 00 00 00 00 00 39 5E-00 00 00 00 00 00 00 00 00 00 00 22 DE-42 D9 21 DE 42 D9 50 43 0Г4А IE 00 00 00 5A A2 00 00 5A A2 21 DE > .ooz :pc . Rrporiuml23 45. J an.Feb.Mar. .z... Z. 0 9Л Z. ..'...." .B. ! .B. ! . <r UCPTT *> Рис. 4.6. Просмотр содержимого сегмента данных Поскольку загрузчик устанавливает в регистре DS адрес префикса сегмента программы, а данные начинаются со смещения в 100 байт от этого адреса, требуется ввести именно команду D DS:100 для просмотра данных. Позднее, при использовании DEBUG для просмотра программ, инициализирующих DS Глава 4. Требования к написанию программ на языке Ассемблер \ 05
адресом сегмента данных, для просмотра данных нужно будет использовать команду DS:0. Директивы присваивания Ассемблер содержит директивы присваивания: = (знак равенства), EQU и TEXTEQU для переопределения символьных имен другими именами и константных числовых значений именами. Эти директивы не генерируют каких- либо элементов данных сами по себе; т.е. программа не может при исполнении, например, добавить число к элементу, определенному директивой EQU. Вместо этого ассемблер использует определенное значение в качестве подстановки (псевдонима) в других предложениях. Преимущество директив присваивания проявляется, например, в ситуации, когда многие предложения используют одну и ту же числовую константу. Если для этой константы указано имя с помощью директивы присваивания и значение константы необходимо изменить, то достаточно будет изменить только предложение присваивания. В противном случае придется искать в программе все предложения, использующие данную числовую константу, чтобы изменить их. При использовании директив присваивания программа становится проще для понимания и удобнее при сопровождении. Директива = (знак равенства) Эта директива позволяет вам присваивать значение выражения определенному имени и может делать это множество раз в тексте программы. Например, так: VALUE_OF_PI = 3.1416 RIGHT_COL =79 SCREEN_POSITIONS = 80 * 25 Примеры использования результатов этих директив: IMUL AX,VALUE_OF_PI ;Умножить АХ на 3.1416 CMP BL,RIGHT_COL /Сравнить BL с 79 MOV CX/SCREEN_POSITIONS /Поместить 2000 в СХ При использовании этой директивы для определения значения двойного слова сначала необходимо использовать директиву процессора .386 для указания ассемблеру: .386 DBLWORD1 = 42АЗВ05СН Директива EQU Рассмотрим следующее предложение с использованием директивы EQU, содержащееся в сегменте данных: FACTOR EQU 12 106 Часть II. Основы программирования на языке Ассемблер
Имя, в этом случае FACTOR может быть любым корректным, с точки зрения ассемблера, именем. Теперь каждый раз при встрече имени FACTOR в программе ассемблер заменит его значением 12. Например, ассемблер преобразует директиву TABLEX DB FACTOR DUP(?) в ее эквивалент TABLEX DB 12 DUP(?) TABLEX может быть определено с помощью EQU только однажды, и не может быть переопределено другим предложением, содержащим EQU. Инструкция может содержать операнд, заданный директивой EQU, как в следующем примере: RIGHT_COL EQU 79 MOV CX,RIGHT_COL /Занести 79 в СХ Можно также создавать псевдонимы для символьных имен, как в следующем примере: ANNL_TEMP DW О AT EQU ANNL_TEMP MPY EQU MUL Первая EQU приравнивает AT определенному элементу ANNL_TEMP (создает псевдоним AT для имени ANNL_TEMP). Любая инструкция, содержащая операнд AT, заменяется ассемблером на аналогичную, содержащую ANNL TEMP. Вторая директива EQU позволяет программе использовать слово MPY вместо обычной символьной инструкции MUL. Директива TEXTEQU MASM 6.0 ввел в употребление директиву TEXTEQU для переопределения текстовых данных в формате имя TEXTEQU <текст> Строка текст определяется в угловых скобках, например, так: PROMPT_MSSGE TEXTEQU <,Add/ Change or Delete?■> и используется так: USER_PROMPT DB PROMPT_MSSGE Ассемблер преобразует такое предложение в USER_PROMPT DB 'Add, Change or Delete?' Глава 4. Требования к написанию программ на языке Ассемблер \ 07
Ключевые моменты • Комментарий в строке начинается с символа точки с запятой (;). • Зарезервированные слова в Ассемблере используются для специальных целей и только в определенных случаях. • Идентификатор - это имя, которое вы назначаете элементам в вашей программе. Есть два типа идентификаторов: имя, ссылающееся на адрес элемента данных, и метка, ссылающаяся на адрес инструкции. • Операция обычно используется для определения областей данных и кодирования инструкций. Операнд предоставляет информацию, над которой производится операция. • Программа состоит из одного или более сегментов, каждый из которых начинается на границе параграфа. • Директива ENDS заканчивает сегмент, ENDP - процедуру, END - программу. • Директива ASSUME связывает сегментные регистры CS, DS и SS с соответствующими именами сегментов. • Для программы типа .ЕХЕ регистр DS обычно инициализируется адресом сегмента данных. • Для упрощенных директив сегментов сначала выбирается модель памяти, а затем определяются сегменты. • Функция 4СН прерывания INT 21H - стандартная функция завершения программы. • Имена элементов данных должны быть уникальными в пределах программы и осмысленными. • DB (или BYTE) - предпочтительный формат для хранения символьных строк, поскольку он позволяет хранить строки длиной более двух символов и преобразует их в нормальную последовательность слева направо. • Десятичные и двоичные (шестнадцатеричные) константы генерируют различные значения. Сравните результаты добавления десятичного числа 25 и шестнадцатеричного 25: ADD СХ,25 ;Добавляем 25 ADD СХ,25Н ;Добавляем 37 • Для DW, DD и DQ ассемблер хранит байты в объектном коде в обратной последовательности. 108 Часть II. Основы программирования на языке Ассемблер
• Элементы DB обрабатываются половиной регистров (например, AL, BL), DW - полными регистрами (например, ВХ, СХ), DD - расширенными регистрами (ЕАХ, ЕВХ и т. д.) Более длинные числовые элементы требуют специальных методов обработки. Вопросы и упражнения 1. Укажите различия между компилятором и ассемблером. 2. Что такое зарезервированное слово в Ассемблере? Приведите два примера. 3. Какие два типа идентификаторов есть в Ассемблере? 4. Из приведенных элементов, определенных в сегменте данных, определите правильные: (а) $50, (б) AT&T, (в) @$_А, (г) 23АС, (д) ЕВХ. Если имя неправильное, объясните, почему. 5. Объясните разницу между директивой и инструкцией, w приведите два примера каждой. 6. Приведите директивы, которые укажут ассемблеру (а) выводить заголовок программы в начале каждой страницы, (б) перейти на новую страницу. 7. Есть ли способ, и если есть, то какой, заставить ассемблер различать строчные и заглавные буквы? 8. Формат директивы SEGMENT таков: имя SEGMENT выравнивание совмещение •класс' Объясните назначение параметра (а) выравнивание, (б) совмещение, (в) 'класс'. 9. (а) Объясните назначение процедуры, (б) Когда процедура определяется как NEAR? (в) Когда процедура определяется как FAR? (г) Как определяются начало и конец процедуры? 10. Какие предложения относятся к концу (а) процедуры, (б) сегмента, (в) программы? 11. Укажите предложение, заканчивающее ассемблирование, и предложение, заканчивающее исполнение программы. 12. Укажите требуемое предложение ASSUME, если имена сегментов стека, данных и кода, соответственно - STKSEG, DATSEG и CODSEG. 13. Рассмотрите применение инструкции MOV АХ,4С00Н с инструкцией INT 21Н. (а) объясните, что выполняет данная инструкция, (б) Объясните назначение 4С00Н. Глава 4. Требования к написанию программ на языке Ассемблер 109
14. Для упрощенных сегментных директив директива .MODEL указывает модель памяти как Tiny, Small, Medium, Compact, Large, Huge или Flat. При каких обстоятельствах нужно использовать каждую из этих моделей? 15. Укажите длины в байтах элементов данных, создаваемых следующими директивами: (a) DW, (б) DQ, (в) DB, (г) DD. 16. Определите символьную строку с именем CO_NAME, содержащую константу "Internet Services". 17. Определите следующие числовые значения в элементах данных с именами от ITEM1 до ITEM5, соответственно: (а) 1-байтовый элемент, содержащий шестнадцатеричное значение, эквивалентное десятичному числу 71; (б) 2-байтовый элемент, содержащий неопределенное значение; (в) 4-байтовый элемент, содержащий шестнадцатеричное значение, эквивалентное десятичному числу 7524; (г) 1-байтовый элемент, содержащий двоичное значение, эквивалентное десятичному числу 47. 18. Покажите, какой шестнадцатеричный объектный код соответствует (a) DB 82, (б) DB '82', (в) DB 4 DUP('5'). 19. Определит^ шестнадцатеричный объектный код, сохраняемый ассемблером для (a) DB 72, (б) DW 2АВЕН, (в) DD 1EB6C3H, (г) DQ 24C3E29H. 20. (а) Используйте директиву EQU, чтобы переопределить значение 16Н как ATTRIBUTE, (б) Используйте инструкцию MOV для занесения ATTRIBUTE в регистр BL. 110 Часть II. Основы программирования на языке Ассемблер
Глава 5 Трансляция, компоновка и исполнение программ Цель: рассмотреть механизмы сборки, компоновки и исполнения программ на языке Ассемблера. Введение Эта глава объясняет способы ввода программы на языке ассемблера, а также методы преобразования ее в объектный код, компоновки и выполнения. Символьные инструкции, из которых вы составляете программу, называют исходным кодом. Вы используете ассемблер для преобразования (трансляции) программы в исходном коде в объектный код - последовательность машинных инструкций. Наконец, вы используете программу компоновки для формирования внутренних связей и адресации программы, создавая исполняемый модуль. Разделы, посвященные созданию объектного кода, описывают, как выполнить трансляцию программы в символьных кодах для получения диагностических данных (в том числе сообщений об ошибках) и создания, собственно, объектного кода. Разъясняются также детали листингов на языке Ассемблера, а также кратко демонстрируется процесс обработки исходного кода. Разделы, посвященные процессу компоновки, описывают, как получить из объектного кода исполняемый модуль. Описаны также детали получаемой карты связей и диагностики, выдаваемой компоновщиком. Наконец, эти разделы описывают процесс запуска исполняемого модуля. В последнем разделе описывается создание, преобразование и выполнение программ типа .СОМ. Подготовка программы к преобразованию и исполнению В гл. 4 (рис. 4.3) показан только исходный код программы, не преобразованной в исполняемый формат. Для ввода этой программы вы можете использовать любой текстовый редактор, который может сохранять текст в виде простого ASCII-файла без форматирования. Можно получить значительный прирост производительности, расположив рабочие файлы данных и программы Глава 5. Трансляция, компоновка и исполнение программ 111
на виртуальном диске в оперативной памяти ПК. (В последнее время данный совет вряд ли актуален - производительность компьютеров высока, а в новых операционных системах явные средства принудительного создания виртуальных дисков могут отсутствовать, хотя, например, при работе в сети и загрузке машины с сервера эмулируется виртуальный диск. - Прим.ред.) Запустите программу текстового редактора, введите предложения программы, приведенной на рис. 4.3, и назовите файл с листингом A05ASM1.ASM. Хотя выравнивание текста не существенно для ассемблера, программа станет более доступной для понимания, если вы будете записывать имя, операцию, операнд и комментарии в четко выровненной по столбцам форме. Многие редакторы позволяют использовать табуляцию для создания столбцов шириной 8 символов. После ввода всех предложений программы проверьте их правильность. В том виде, в каком она есть, программа является просто текстовым файлом, который не может прямо выполняться - его необходимо сначала оттранслировать и скомпоновать. На рис. 5.1 показаны шаги, необходимые для компиляции, компоновки и выполнения программы. Редактор /Клавиатура у~ Ассемблер Прогр. LST Прогр. CRF (при необходимости) : I Компоновщик Создание прогр. ASM Прогр. ASM Ассемблирование Прогр. OBJ Компоновка Прогр. ЕХЕ Создание и редактирование исходного кода на Ассемблере (.ASM) Трансляция исходного кода, создание объектного кода (.OBJ) Компоновка объектного кода, создание исполняемого кода (.ЕХЕ) Выполнение Загрузка и выполнение исполняемого кода (.ЕХЕ) Рис. 5.1. Последовательность шагов трансляции, компоновки и выполнения 112 Часть II. Основы программирования на языке Ассемблер
1. Шаг ассемблирования включает в себя трансляцию машинного кода в объектный код и создание промежуточного файла (модуля) типа .OBJ (объектного). (Вы уже видели примеры исходного и машинного кодов в предыдущих главах.) Одна из задач ассемблера - вычисление смещений для каждого элемента данных в сегменте данных и для каждой инструкции в сегменте кода. Ассемблер также создает заголовок, располагающийся в самом начале генерируемого файла типа .OBJ. Часть заголовка содержит информацию о неполных адресах. Файл типа .OBJ - еще не исполняемый модуль. 2. Шаг компоновки включает в себя преобразование объектного файла в исполняемый (типа .ЕХЕ) файл в машинных кодах. Задачи компоновщика включают в себя формирование полных значений любых адресов, оставленных неполными ассемблером, и сборку раздельно ассемблированных программ в единый модуль. 3. Последний шаг - загрузка программы для исполнения. Поскольку загрузчик знает, где программа должна располагаться в памяти, он может выбрать соответствующие значения для адресов, все еще остающихся неполными в заголовке. Загрузчик отбрасывает заголовок и создает префикс сегмента программы непосредственно перед загрузкой программы в память. Ассемблирование исходной программы Ассемблер преобразует исходные предложения программы в объектный код и выводит на экран сообщения об обнаруженных ошибках. Типичные ошибки - это, например, имена, не соответствующие соглашениям о допустимых в программах на ассемблере именах, неправильно написанные имена инструкций (например, MOVE вместо MOV) и операнды, содержащие неопределенные имена. Поскольку разновидностей ошибок достаточно много (100 или более) и различаются они у различных версий ассемблеров, то за полным списком (и описанием) ошибок лучше обратиться к руководству по конкретной версии ассемблера. Ассемблер может попытаться исправить некоторые ошибки, но в любом случае лучше вновь загрузить исходный файл в редактор, исправить ошибки и повторно оттранслировать программу. Результатом работы ассемблера являются файлы типа .OBJ (объектные), .LST (листинги), .CRF (файл перекрестных ссылок). Обычно требуется файл типа .OBJ, который можно скомпоновать в исполняемый модуль. Могут также потребоваться листинги (файлы типа .LST), особенно если они содержат диагностическую информацию об ошибках или вы хотите изучить сгенерированный машинный код. Файлы типа .CRF (или .SBR, или .XRF - в зависимости от версии Ассемблера) полезны в больших программах, при работе над которыми вам может понадобиться узнать, какие инструкции обращаются к каким элементам данных. Кроме того, требование создать файл типа .CRF заставляет ассемблер Глава 5. Трансляция, компоновка и исполнение программ 113 8 —5-741
сгенерировать номера для элементов (инструкций) файла .LST, на который ссылается файл .CRF. Далее эти файлы будут рассмотрены более подробно. За подробной информацией о трансляции и компоновке программ обратитесь к приложению Е. Использование стандартных директив определения сегментов На рис. 5.2 приведен листинг, созданный ассемблером под именем A05ASM1.LST. Ширина строки, указанная директивой PAGE, равна 132 символам. В начале листинга можно видеть, как ассемблер отреагировал на директивы PAGE и TITLE. Ни одна директива, включая SEGMENT, PROC, ASSUME или END, не генерируют непосредственно какой-либо машинный код, поскольку просто содержат информацию, которая необходима ассемблеру. Строки листинга содержат следующие компоненты. • В начале строки слева расположен номер данной строки листинга. • После номера указаны шестнадцатеричные величины смещений элементов данных и инструкций. • После смещения указан сгенерированный машинный код в шестнадцате- ричном формате. • Завершает строку исходный символьный код. Сама программа разделена по вертикали на три сегмента, каждый - со своими собственными величинами смещений для данных или инструкций. Каждый сегмент содержит директиву SEGMENT, указывающую ассемблеру выравнивать сегмент по адресу, нацело делящемуся на ЮН - само по себе предложение SEGMENT не генерирует машинный код. Загрузчик программы сохраняет содержимое каждого сегмента в памяти и инициализирует этими адресами сегментные регистры: STACK в SS, DATASEG в DS и CODESEG в CS. Начало сегмента соответствует нулевому смещению от этого адреса. Сегмент стека Сегмент стека содержит директиву DW, определяющую 32 слова, каждое из которых содержит значение 0. Это определение создает стек размером, достаточным в большинстве реальных ситуаций, поскольку большая программа может потребовать вызова многих прерываний для ввода/вывода и вызова множества подпрограмм, а все эти операции требуют использования стека. Сегмент стека завершается смещением 0040Н, эквивалентным десятичному 64 (32 слова по 2 байта каждое). Ассемблер показывает сгенерированную константу слева как 0020[0000], то есть 20Н (32) слова. *| ^4 Часть II. Основы программирования на языке Ассемблер
A05ASM1 1 2 т. 4 0000 5 0000 6 7 8 9 0040 10 11 0000 12 0000 13 0002 14 0004 15 0006 16 17 0000 18 0000 19 20 0000 21 0003 22 23 0005 24 0008 25 000С 26 000F 27 0012 28 0014 29 0014 30 Segments CODESEG DATASEG STACK Symbols: MAIN FLDD . . FLDE . . FLDF . . (EXE) Перемещение и 0020 [ 0000 ] 00D7 007D 0000 B8 R 8ED6 Al 0000 R 03 06 0002 R A3 0004 R B8 4C00 CD 21 and Groups: Name Name 0 Warning Errors 0 Severe Errors TITLE STACK STACK сложение page A05ASM1 SEGMENT DW ENDS / DATASEG SEGMENT FLDD FLDE FLDF DW DW DW DATASEG ENDS CODESEG SEGMENI MAIN MAIN PROC ASSUME MOV MOV MOV ADD MOV MOV INT ENDP CODESEG ENDS END Length 0014 0006 0040 Type F PROC L WORD L WORD L WORD 60,132 Page 1-1 (EXE) Перемещения и сложения 1 PARA STACK 'Stack' 32 DUP(0) 1 PARA 'Data' 215 125 ? 1 PARA 'Code1 FAR SS:STACK ,DS:DATASEG,CS:CODESEG AX,DATASEG /Поместить адрес DS,AX AX,FLDD AX,FLDE FLDF,AX AX,4C00H 21H MAIN * Align PARA PARA PARA Value 0000 0000 0002 0004 ;сегмента данных в DS ;Поместить 0215 в АХ ;Прибавить 0125 к АХ ;Сохранить сумму в FLDF ;Завершить работу ;Конец процедуры ;Конец сегмента ;Конец программы Combine Class NONE 'CODE' NONE 'DATA' STACK 'STAC&' Attr CODESEG Length =0014 DATASEG DATASEG DATASEG Рис. 5.2. Оттранслированная программа с обычной структурой сегментов Глава 5. Трансляция, компоновка и исполнение программ 115
Сегмент данных Сегмент данных программы, DATASEG, содержит три определенных значения, все в формате DW (слово). 1. FLDD определяет слово (два байта) и инициализирует его десятичным значением 215, которое ассемблер преобразует в 00D7H (показано слева). 2. FLDE определяет слово, инициализированное десятичным значением 125 и преобразованное ассемблером в 007DH. Действительные сохраненные значения этих двух констант - соответственно, D700 и 7D00, в чем вы можете убедиться, воспользовавшись DEBUG. 3. FLDF определяется с помощью DW с операндом ?, то есть не инициализируется определенным значением. Листинг показывает его содержимое в виде 0000. Адреса смещений FLDD, FLDE и FLDF - соответственно, 0000, 0002 и 0004. что соответствует их размерам. Сегмент кода Сегмент кода программы, CODESEG, содержит исполняемый код программы, весь в одной процедуре (PROC). Три предложения позволяют адресовать сегмент данных: ASSUME SS:STACK,DS:DATASEG,CS:CODESEG 0000 B8 R MOV AX,DATASEG 0003 8E D8 MOV DS,AX • Директива ASSUME связывает каждый сегмент с соответствующим ему сегментным регистром. ASSUME просто предоставляет информацию ассемблеру и не вызывает генерации какого-либо машинного кода. • Первая инструкция MOV "сохраняет" DATASEG в регистре АХ. Заметим, что инструкция не может действительно сохранить сегмент в регистре - ассемблер понимает, что это ссылка на сегмент и только при выполнении запишет в регистр его адрес. Посмотрите на машинный код слева: В8—R. Четыре дефиса значат, что на данном этапе ассемблер не может определить адрес DATASEG; система определяет этот адрес только тогда, когда программа уже скомпонована и загружена для выполнения. Поскольку загрузчик может поместить программу где угодно в памяти, ассемблер оставляет адрес открытым и указывает на это символом R - relocatable, т.е. перемещаемый. Загрузчик должен заменить незаконченный адрес действительным. • Вторая инструкция MOV заносит содержимое регистра АХ в DS. Поскольку нет инструкции, корректно перемещающей данные напрямую из памяти в регистр DS, для его инициализации требуются две инструкции. Заметьте, что программа не использует регистр ES, хотя многие программисты инициализируют его в обычном порядке. 116 Часть II. Основы программирования на языке Ассемблер
Хотя загрузчик автоматически инициализирует регистры SS и CS, когда загружает программу на выполнение, вы должны проинициализировать регистр DS, и, если необходимо, ES. Несмотря на то, что все операции кажутся довольно сложными, вам сейчас не обязательно понимать их. Все программы в этой книге используют стандартное определение и инициализацию, и вы можете просто воспроизводить этот код в каждой вашей программе. Сейчас просто создайте заготовку программы на диске, и для каждой программы, которую вы хотите создать, скопируйте заготовку в файл с подходящим именем и используйте редактор для ввода дополнительных инструкций. Первая инструкция после инициализации регистра DS - MOV AX,FLDD, начинающаяся с ячейки со смещением 0005 и генерирующая машинный код А1 0000. Промежуток в листинге между А1 (операцией) и 0000 (операндом) предназначен только для удобства восприятия. Следующая инструкция, ADD AX,FLDE, начинается со смещения 0008 и генерирует четыре байта машинного кода. Инструкция MOV FLDF,AX копирует сумму из регистра АХ в переменную FLDF со смещением 0004 в сегменте данных. В этом примере машинные инструкции имеют длину два, три или четыре байта. Последнее предложение в программе, END, содержит операнд MAIN, связанный с именем PROC со смещением 0000. Это - ячейка в сегменте кода, на которую передает управление загрузчик программы для начала ее выполнения. За листингом программы следуют таблица сегментов и групп, а также таблица символов. - Таблица сегментов и групп Эта таблица показывает любые определенные сегменты и группы. Заметьте, что сегменты показаны не в Vofi последовательности, в которой они указаны в программе; ассемблер, использованный в этом примере, перечисляет их поименно в алфавитном порядке. (Эта программа не содержит групп, которые рассматриваются далее.) В таблице приводятся длина каждого сегмента в байтах, выравнивание (все - по границам параграфов), тип совмещения и класс. Таблица символов Эта таблица содержит имена полей данных в сегменте данных (FLDD, FLDE и FLDF) и метки, относящиеся к инструкциям в сегменте кода. Для MAIN (единственной записи в сегменте) величина F PROC в столбце Туре означает процедуру типа Far (far - удаленный - в свою очередь, означает, что MAIN, как начальная точка для исполнения, должна быть известна за пределами программы). Столбец Value указывает смещение от начала сегмента для имен, меток и процедур. Столбец, озаглавленный "Attr" (attribute - атрибут) указывает сегмент, в котором определен каждый элемент. Глава 5. Трансляция, компоновка и исполнение программ 117
MASM 6.n также приводит списки процедур, параметров и таблицу групп. Приложение Е описывает эти таблицы более подробно. Использование упрощенных директив сегментов В гл. 4 на рис. 4.4 приведен пример программы, использующей упрощенные директивы определения сегментов. На рис. 5.3 приведен ассемблированный листинг этой программы. Для упрощенных сегментных директив инициализация. DS выполняется следующим образом: MOV AX,(2data MOV DS,AX Первая часть таблицы сегментов под заголовком "Sgments and groups" показывает три сегмента, переименованных ассемблером и перечисленных в алфавитном порядке: • _DATA - сегмент данных длиной 6 байт; • STACK - сегмент стека длиной 40Н (64) байта; • . _ТЕХТ - сегмент кода длиной 14Н (20) байт. Перечисленные под заголовком "Symbols" имена определены в программе или являются именами по умолчанию. Упрощенные директивы сегментов задают несколько предопределенных имен, начинающихся с символа @, на которые вы можете свободно ссылаться в программе. Кроме @data, предусмотрены следующие имена: (2CODE Приравнивается к имени сегмента кода, _ТЕХТ ©FILENAME Имя программы Можно использовать @code и @data в ASSUME и исполняемых предложениях, например, MOV AX,@data. Двухпроходный ассемблер Ассемблеры обычно делают два или более прохода по тексту программы при трансляции для обработки ссылок вперед на адреса, определения которых еще не встречались в программе. При первом проходе ассемблер просматривает всю исходную программу и составляет таблицу символов, содержащую имена и метки, встреченные в программе, т.е. имена полей данных, метки программы и их относительные местоположения (смещения) в сегменте. Вы можете увидеть эту таблицу символов непосредственно под оттранслированной программой на рис. 5.3, где смещения FLDD, FLDE и FLDF - соответственно, 0000, 0002 и 0004 байта. Хотя программа не содержит определений меток инструкций, они появились бы в *| *| 8 Часть II. Основы программирования на языке Ассемблер
сегменте кода со своими смещениями. Первый проход определяет размер кода, который сгенерирует каждая инструкция. A05ASM2 1 2 з 4 5 6 7 0000 8 0002 9 0004 1 Г) 11 12 0000 13 0000 та 14 0003 15 16 0005 17 0008 18 000С 19 20 000F 21 0012 22 0014 23 (EXE) Операции перемещения > 00D7 007D 0000 B8 8E Al 03 A3 B8 CD ---*- R D8 0000 R 06 0002 0004 R 4C00 21 page 60 TITLEA05ASM2 FLDD FLDE FLDF MAIN R Segments and Groups: DGROUP _DATA STACK JTEXT Symbols MAIN FLDD . FLDE . FLDF . (2CODE ©FILENAME Name Name 0 Warning Errors | 0 > Severe* Errors .MODEL .STACK .DATA DW DW DW PROC MOV MOV MOV ADD MOV MOV INT MAIN ,132 (EXE) SMALL 64 215 125 ? .CODE FAR AX, DS, AX, AX, FLDF, AX, 21H ENDP END и сложения Page 1-1 ] Операции перемещения и сложения /Определение стека /Определение сегмента данных /Определение сегмента кода @data /Поместить адрес сегмен- | АХ /данных.в регистр DS FLDD ; Поместить 0215 в АХ FLDE ;Прибавить 0125 к АХ АХ /Сохранить сумму в FLDF 4С00Н ;Завершить работу /Конец процедуры MAIN ;Конец программы Length GROUP 0006 0040 0014 ' Type F PROC L WORD L WORD L WORD TEXT TEXT Align Combine Class WORD PUBLIC 'DATA' PARA STACK 'STACK' WORD PUBLIC 'CODE' Value Attr 0000 _TEXT Length = 0014 0000 _DATA 0002 _DATA 0004 _DATA _TEXT a05asm2 Рис. 5.З. Оттранслированная программа с упрощенными сегментными директивами Глава 5. Трансляция, компоновка и исполнение программ 119
При втором проходе ассемблер использует таблицу символов, созданную им при первом проходе. Теперь, когда он знает длину каждой инструкции и ее относительное расположение, он может целиком сформировать объектный код для каждой инструкции. После этого он, в зависимости от заданных при вызове параметров, создает различные объектные (.OBJ) файлы, файлы листинга (.LST) и файлы перекрестных ссылок (.CRF). Потенциальная проблема при первом проходе - обращения вперед: инструкции определенных типов в сегменте кода могут ссылаться на метку инструкции, до определения которой ассемблер еще не добрался. MASM создает объектный код на основе предполагаемой длины каждой инструкции машинного языка. Если есть какие-то различия между длинами инструкций, вычисленных на первом и втором проходах, MASM выдает сообщение об ошибке, гласящее "Phase error between passes". Такие ошибки относительно редки, но если они появляются, вы должны будете найти их источник и устранить его. Начиная с версии 6.0, MASM более эффективно учитывает длины инструкций, делая столько проходов, сколько необходимо. TASM может ассемблировать программу в один проход, но вы можете приказать ему сделать более одного прохода, если возникли сложности с обращениями вперед. Компоновка объектного файла Когда вы добьетесь безошибочной трансляции исходного текста в объектный код, вы можете скомпоновать получившийся объектный файл, A05ASM1.OBJ, созданный ассемблером и содержащий только машинный код (MASM 6.1 производит компоновку после трансляции по команде ML). Компоновщик выполняет следующие действия. • Соединяет, если требуется, несколько раздельно оттранслированных программ в один исполняемый модуль, например, две или более программы на ассемблере или ассемблерную программу и программу на С. • Создает файл типа .ЕХЕ и инициализирует его специальными инструкциями, выполняющими его дальнейшую загрузку для исполнения. После того как вы скомпоновали один или более объектных файлов в исполняемый модуль, вы можете запускать этот модуль на исполнение произвольное количество раз. Но если вам потребуется внести изменения в программу, вы должны будете изменить исходный листинг, повторно оттранслировать его в объектный код и скомпоновать полученный файл типа .OBJ в исполняемый файл. Даже если сейчас эти шаги не совсем ясны, вы обнаружите, что через некоторое время (после написания и отладки дюжины программ) они будут заучены до автоматизма. 120 Часть II. Основы программирования на языке Ассемблер
Результатами компоновки являются исполняемые файлы (.ЕХЕ), файлы карт (.MAP) и файлы библиотек (.LIB). За детальным описанием компоновки программ обращайтесь к приложению Е. Карта связей для первой программы Для программы A05ASM1 компоновщик выдаст такую карту: START ОООООН 00040Н 00050Н Program STOP 0003FH 00045Н 00063Н entry point at LENGTH 0040H 0006H 0014H 0005:0000 NAME STACK DATASEG CODESEG CLASS STACK DATA CODE • Первым сегментом является стек, начинающийся со смещения 0 байтов от начала программы. Поскольку он определен как 32 слова, его длина равна 64 байтам, как показывает строка параметров (0040Н). • CerMeHt данных начинается со следующей доступной границы параграфа, смещение - 40Н. • Сегмент кода начинается со следующей границы параграфа, смещение - 50Н (некоторые ассемблеры переставляют сегменты в алфавитном порядке). • Точка входа программы, 0005:0000, представленная в форме сегмент: смещение, ссылается на относительный адрес первой исполняемой инструкции. В результате относительный стартовый адрес - байт в сегменте 5[0], смещение 0 байт, что соответствует границе сегмента кода со смещением 50Н. Загрузчик программы использует это значение при загрузке программы на выполнение. Единственная ошибка, с которой вы, вероятно, можете столкнуться сейчас - неправильное указание имени файла: В этом случае просто повторите команду компоновки, указав правильное имя. Карта связей для второй программы Карта связей для второй программы, A05ASM2, использующей упрощенные сегментные директивы, демонстрирует несколько другую структуру исполняемого модуля. Во-первых, ассемблер физически переставил сегменты в алфавитном порядке; во-вторых, следующие один за другим сегменты выравниваются по границам слов, а не параграфов, как видно на карте: Глава 5. Трансляция, компоновка и исполнение программ 121
START ОООООЫ 00014H 00020H Program entry STOP 00013H 00019H 0005FH point at 0000 LENGTH 0014H 0006H 0040H :0000 NAME _TEXT _DATA STACK CLASS CODE DATA STACK • Теперь первый сегмент - сегмент кода, начинающийся со смещения 0 байтов от начала программы. • Сегмент данных начинается с первой доступной границы слова со смещением 00014Н. • Стек начинается со следующей доступной границы слова со смещением 00020Н. • Адрес точки входа программы - 0000:0000. Это значит, что относительное размещение сегмента кода начинается'с сегмента 0 и со смещением 0. Выполнение программы Оттранслировав и скомпоновав программу, вы можете выполнить ее. Если исполняемый файл расположен в текущей папке, вы можете приказать загрузчику считать его в память для выполнения, введя в командной строке A05ASM1.EXE ИЛИ A05ASM1 (без расширения .ЕХЕ) Если вы пропустите расширение имени файла, загрузчик предположит, что запрошенный файл является исполняемым (типа .ЕХЕ или .СОМ). Однако поскольку созданная программа не отображает каких-либо результатов работы на экране, лучше запустить ее в программе DEBUG и использовать команды Т для наблюдения за ее выполнением. Введите следующую строку, включая расширение .ЕХЕ: DEBUG n:АО5ASM1.EXE DEBUG загружает исполняемый программный модуль и выводит приглашение (дефис). Для просмотра сегмента стека введите D SS:0. Стек, в соответствии с результатом инициализации, содержит сплошные нули. Для просмотра сегмента кода введите D CS:0. Сравните выведенный на экран машинный код с кодом, приведенным в ассемблерном листинге: В8 8ED8A10000 ... В данном случае ассемблерный листинг не точно соответствует реальному машинному коду, поскольку ассемблеру не был известен адрес операнда первой ^22 Часть II. Основы программирования на языке Ассемблер
инструкции. Теперь вы можете определить этот адрес, просматривая исполняемый код. Для просмотра содержимого регистров введите R и нажмите <Enter>. SP (указатель стека) должен содержать 0040Н, что соответствует размеру стека (32 слова по 2 байта или 64 (40Н) байтов). IP (указатель инструкций) должен содержать 0000Н. SS и CS правильно проинициализированы для исполнения; их значения зависят от адреса в памяти, по которому загружена ваша программа. Первая инструкция MOV АХ,хххх готова к выполнению --она и следующая инструкция MOV инициализируют регистр DS. Для выполнения первой инструкции MOV введите Т (отслеживание) и нажмите <Enter>. Обратите внимание на изменение содержимого IP. Для выполнения второй инструкции MOV вновь введите Т и нажмите <Enter>. Просмотрите содержимое DS - теперь он должен быть проинициализирован адресом сегмента данных. Третья инструкция MOV загружает содержимое FLDD в АХ. Выполните Т вновь и заметьте, что АХ теперь содержит 00D7. Теперь введите Т для выполнения инструкции ADD и отметьте, что АХ содержит 0154Н. Введите Т еще раз, и следующая инструкция MOV запишет содержимое АХ в слово со смещением 0004 в сегменте данных. Для того чтобы просмотреть содержимое сегмента данных, введите D DS:0. Операция показывает три элемента данных в виде D7 00 7D 00 54 01 с байтами каждого слова, записанными в обратной последовательности. Сейчас вы можете воспользоваться командой L для повторной загрузки программы для исполнения или командой Q для завершения работы с DEBUG. Листинг с перекрестными ссылками При запросе ассемблер генерирует файл, который вы можете использовать для создания таблицы перекрестных ссылок идентификаторов или символов программы. Расширение этого файла - .SBR в MASM 6.1, .CRF для MASM 5.1 и .XRF для TASM. Однако вам все равно придется преобразовать этот файл в отсортированный надлежащим образом файл перекрестных ссылок. На рис. 5.4 показац^листинг перекрестных ссылок, созданный для программы с рис. 5.2. Символы в первом столбце расположены в алфавитном порядке. Числа в следующем столбце, представленные в виде п#, указывают строку в файле .LST, в которой определен каждый символ. Числа справа от этого столбца - это номера строк, показывающие, какие другие предложения ссылаются на этот символ . Например, CODESEG определен в строке 17, а к нему обращаются в строках 19 и 29. FLDF определен в строке 14, а на него ссылаются в строке 25+, где "+" означает, что его значение изменяется в ходе выполнения программы (инструкцией MOV FLDF,AX). Глава 5. Трансляция, компоновка и исполнение программ 123
Symbol Cross-Reference (# definition, + modification) MAIN 18# 28 30 CODE 17 CODESEG 17# 19 29 DATA 11 DATASEG 11# 15 19 20 FLDD 12# 23 FLDE 13# 24 FLDF 14# 25 + STACK 4 STACK 4# 9 19 Рис. 5.4. Таблица перекрестных ссылок Ассемблирование программы создает множество промежуточных файлов. Вы можете свободно удалять файлы .OBJ, .CRF и .LST. Сохраняйте файлы .ASM на случай, если вам понадобится внести изменения в программу, и .ЕХЕ для выполнения программы. Диагностика ошибок Ассемблер выводит диагностическую информацию о любых ошибках, нарушающих его правила. Программа на рис. 5.5 аналогична программе, приведенной на рис. 5.2, за исключением нескольких преднамеренных ошибок, внесенных в программу с иллюстративными целями. Диагностическая информация будет меняться в зависимости от версии ассемблера. Вот объяснение ошибок: Строка Объяснение ошибки 9 Определение FLDF требует еще одного операнда 14 Вместо DX должно быть указано DS, хотя эта ошибка ассемблером не распознается 16 Вместо AS должно быть указано АХ 18 FLDQ должно быть записано как FLDF 19 Размеры элементов данных (байт и слово) должны быть согласованы (предупреждение) 22 Исправление других ошибок вызовет исчезновение этой ошибки 23 MIAN должно быть введено как MAIN 124 Часть II. Основы программирования на языке Ассемблер
1 2 "3 4 5 6 7 0000 8 0002 9 0004 a05asm3. 10 11 12 0000 13 0000 14 0003 15 16 a05asm3. 17 0005 18 0009 a05asm3. 19 000C a05asm3. 20 OOOF 21 0012 22 0014 a05aem3. 23 a05asm3. 1 Warning 1 5 Severe 00AF 0096 ASM(9) : B8 8B DO ASM(16) page 60,1321 TITLE A05ASM3 (EXE) Ошибки программирования .MODEL .STACK .DATA FLDD ВД FLDE ВД FLDF DW SMALL 64 175 150 error A2027: Operand expected | / .CODE MAIN PROC R MOV MOV MOV error A2009: 03 06 0002 R ADD A3 0000 ASM(18) A2 0000 ASM(19) B8 4C00 CD 21 A8M(22) A8M(23) Errors Errors U MOV ■ error A2009: R MOV . warning A4031: MOV INT MAIN ENDP : error A2006 END : error A2009 FAR AX,@data ;Адрес сегмента DX,AX ;данных в DS AS,FLDD ;Поместить 0175 в АХ Symbol not defined: AS AX,FLDE ;Прибавить 0150 к АХ FLDQ,AX ;Сохранить сумму в FLDF Symbol not defined: FLDQ FLDD,AL ;Сохранить значение байта Operand types must match AX,4C00H ;Завершение работы 21H : Phase error between passes MIAN : Symbol not defined: MIAN Рис. 5.5. Диагностические сообщения ассемблера при обнаружении ошибок Сообщение об ошибке 22, "Phase error between passes", выдается, если адреса, сгенерированные при первом проходе ассемблера по тексту программы, отличаются от сгенерированных при втором проходе. Для определения источника этой трудной в локализации ошибки в MASM 5.1 используйте ключ /D для вывода файлов и первого, и второго проходов, и. сравните указанные в этих файлах адреса смещений. Счетчик положения в ассемблере Ассемблер использует счетчик положения (location counter), который предназначен для учета каждого определенного в сегменте данных элемента данных. На рисунках 5.2 и 5.3 показана работа этого счетчика на примере трех определенных элементов данных: Глава 5. Трансляция, компоновка и исполнение программ 125
0000 ... FLDD DW ... 0002 ... FLDE DW ... 0004 ... FLDF DW ... Изначально счетчик положения установлен в 0, и в ячейке с этим смещением в сегменте данных ассемблер размещает первый элемент данных, FLDD. Поскольку FLDD определено как слово, ассемблер увеличивает значение счетчика положения на 2, до 0002, и в ячейке с этим смещением размещает слово FLDE. Затем ассемблер еще раз увеличивает значение счетчика положения на 2, до 0004, для размещения следующего элемента данных, FLDF, также определенного как слово. Далее ассемблер вновь увеличивает значение счетчика положения на 2, до 0006, но элементов данных, которые нужно определить и разместить в памяти, больше нет. Ассемблер предлагает несколько способов изменения текущего значения счетчика положения. Например, можно использовать директиву EQU для переопределения элементов данных с разными именами (см. гл. 4), использовать директиву ORG, чтобы начинать программу с определенного смещения (см. следующий параграф), и директивы EVEN и ALIGN -для выравнивания адресов по границам с четными номерами (см. гл. 6). Написание программ .СОМ Для программы типа .ЕХЕ компоновщик автоматически генерирует определенный формат и при сохранении ее на диске предваряет код программы специальным блоком заголовка размером 512 байт или более (в гл. 23 блок заголовка рассмотрен более подробно). Вы также можете писать и выполнять программы типа .СОМ. Преимущества этих программ - меньший размер по сравнению с программами типа .ЕХЕ и брлее легкая адаптация к применению в качестве резидентных программ. Формат программ .СОМ возник на раннем этапе развития микрокомпьютеров, когда размер программы был ограничен 64 килобайтами. Различия между программами .ЕХЕ и .СОМ Существенные различия между программами, которые должны исполняться как .ЕХЕ и .СОМ, включают размер программы, сегментирование и инициализацию. Размер программы Программа типа .СОМ использует единый сегмент для инструкций и данных, изначально ограниченный размером 64 К, включая префикс сегмента программы (PSP). PSP - это 256-байтный (100Н) блок, который загрузчик программ вставляет непосредственно перед программами при загрузке их в память с диска. 126 Часть II. Основы программирования на языке Ассемблер
Программа типа .СОМ всегда меньше, чем .ЕХЕ, выполняющая аналогичные функции; одна из причин этого т- наличие в программе .ЕХЕ 512-байтного заголовка, отсутствующего в программах типа .СОМ (не путайте блок заголовка, описанный в гл. 23, с префиксом сегмента программы PSP). Программа .СОМ - точное отображение исполняемой программы без информации о перемещаемых адресах. Сегменты Использование сегментов в программах .СОМ существенно отличается (и упрощено) по сравнению с .ЕХЕ. Готовая программа типа .СОМ совмещает PSP, стек, сегмент кода и сегмент данных в одном сегменте кода. Для программы типа .ЕХЕ обычно вы сами определяете сегмент данных и инициализируете DS адресом этого сегмента. Для программы .СОМ вы определяете данные в сегменте кода точно так же, как вы это делали с использованием DEBUG в гл. 3. Вы должны сами определять сегмент стека для программы типа .ЕХЕ, но ассемблер самостоятельно создаст стек для программы типа .СОМ. Поэтому, если вы пишете программу, которая должна будет выполняться в виде .СОМ, пропустите определение стека. Если сегмент размером 64 Кбайт достаточно велик для программы, загрузчик размещает стек в конце сегмента и устанавливает в SP значение, указывающее на вершину стека. Если сегмента размером 64 Кбайт не хватает для размещения программы, ассемблер помещает стек после программы, в верхней памяти (однако, вероятно, программа такого размера будет написана в формате .ЕХЕ). Некоторые небольшие программы в этой книге написаны в формате .СОМ, и их легко отличить от программ типа .ЕХЕ. На рис. 5.6 приведено размещение в памяти сегментов программ типов .ЕХЕ и .СОМ. ES SS DS- CS- ....... ....... ^ ^ ^ ^ PSP Стек Сегмент данных Сегмент кода ES DS CS SS ^ ^ PSP Сегмент кода г Стек Рис. 5.6. Сегментная структура .ЕХЕ и .СОМ программ Глава 5. Трансляция, компоновка и исполнение программ 127
Инициализация Когда загрузчик программ загружает для выполнения программу .СОМ, он автоматически инициализирует CS, DS, SS и ES адресом PSP. Теперь CS и DS содержат правильные адреса начала сегмента в период выполнения, и программа типа .СОМ не должна их инициализировать. Поскольку PSP имеет размер 100Н байтов, адресация начинается со смещения 100Н байт. Вы должны ввести в текст программы директиву ORG 100H сразу за предложением SEGMENT или .CODE в сегменте кода. Эта директива приказывает ассемблеру установить счетчик положения в значение 100Н. После этого ассемблер начнет генерировать объектный код, начиная с ячейки с адресом 100Н (сразу после PSP), располагая там код программы типа .СОМ. Написание программы в формате .СОМ Если ваша исходная программа уже написана в формате .ЕХЕ, вы можете использовать редактор для преобразования инструкций в формат .СОМ. Программа на рис. 5.7 аналогична программе, приведенной на рис. 5.2, но изменена и приведена в соответствие требованиям формата .СОМ. TITLE CODESEG BEGIN: FLDD -FLDE FLDF MAIN MAIN CODESEG A05COM1 SEGMENT ASSUME ORG JMP. DW DW DW PROC MOV ADD MOV MOV INT ENDP ENDS END COM-программа PARA 'Code' перемещения и сложения CS:CODESEG,DS:CODESEG,SS:CODESEG,ES:CODESEG 100H MAIN 215 125 •? NEAR AX,FLDD AX,FLDE FLDF,AX AX,4C00H 21H BEGIN ;Начинается в конце PSP ;Переход за данные ;Определения данных ;Поместить 0215 в АХ ;Прибавить 012 5 к АХ ;Сохранить сумму в FLDF ;Завершить работу Рис. 5.7. Исходная программа типа .СОМ с обычными определениями сегментов Отметим здесь следующее. • Нет определенных сегментов стека и данных. • Предложение ASSUME указывает ассемблеру, что CS, SS, DS и ES должны содержать адрес начала сегмента кода (адрес, с которого начинается PSP), когда программа загружается для выполнения. 128 Часть II. Основы программирования на языке Ассемблер
• Директива ORG 100H указывает ассемблеру увеличить значение его счетчика положения на 100Н байт от начала PSP. Загрузчик программы поместит значение 100Н в регистре IP (указателе инструкций) при загрузке программы .СОМ. Из-за этого первое предложение, следующее за ORG, должно быть исполняемой инструкцией. • Инструкция JMP передает управление выполнением на начало программы, пропуская определение данных. Некоторые программисты записывают элементы данных за инструкциями, так что в начальной команде JMP нет необходимости. Запись элементов данных в начале может немного ускорить процесс трансляции, но не дает других преимуществ. В этой книге данные определяются в начале программ просто из соображений стандартизации. • Метки BEGIN и MAIN имеют только описательное значение и не имеют значения для ассемблера. Для этих меток можете использовать любые корректные имена. • Стандартная функция INT 21Н 4СН завершает работу. Преобразование формата .ЕХЕ в .СОМ Процедура создания файла .СОМ различна в разных версиях ассемблера. • Команда ML в MASM 6.1 транслирует, компонует и преобразует листинг в файл типа .СОМ сразу, если в программе используется модель памяти TINY. • MASM 5.1 создает файл .OBJ, который затем компонуется для получения файла .ЕХЕ. Для преобразования полученного файла .ЕХЕ в .СОМ воспользуйтесь программой EXE2BIN. Имя этой программы означает "преобразовать файл типа .ЕХЕ в .BIN"; здесь .BIN означает двоичный файл, но задайте вашему результирующему файлу расширение .СОМ. • TASM позволяет вам создавать файлы типа .СОМ при помощи программы TLINK. За детальным описанием преобразования программы в формат .СОМ обращайтесь к приложению Е. Когда преобразование программы в формат .СОМ завершено, вы можете удалить созданные файлы .OBJ и .ЕХЕ. Программа типа .ЕХЕ имеет размер 792 байта, .СОМ - 24 байта. Различие вызвано, главным образом, наличием в файле .ЕХЕ 512-байтного блока заголовка. На рис. 5.8 показано использование упрощенных сегментных директив для написания программы типа .СОМ. Здесь также необходимо объявлять только сегмент кода, не объявляя сегменты стека и данных. В MASM 6.1 программа должна использовать модель памяти Tiny. Глава 5. Трансляция, компоновка и исполнение программ 129 9 —5-741
TITLE BEGIN: FLDD FLDE FLDF MAIN MAIN A05COM2 .MODEL .CODE ORG JMP DW DW DW PROC MOV ADD MOV MOV INT ENDP END COM-программа TINY 100H MAIN 215 125 •? NEAR AX,FLDD AX,FLDE FLDF,AX AX,4C00H 21H BEGIN перемещения и сложения данных ;Начало - в конце PSP ;Переход за данные ;Определения данных ;Поместить 0215 в АХ ;Прибавить 0125 к АХ ;Сохранить сумму в FLDF ;Завершить работу Рис. 5.8. .СОМ-программа с упрощенными сегментными директивами Советы по отладке Несоответствие программы хотя бы одному из требований формата .СОМ может привести к ее неработоспособности. Например, если программа EXE2BIN находит ошибку, оно просто сообщает вам, что не может преобразовать файл (но не указывает причины). Проверьте предложения SEGMENT, ASSUME и END. Если вы пропустите ORG 100H, выполняемая программа неправильно обратится к данным в PSP, что приведет к непредсказуемым результатам. Если вы запускаете программу типа .СОМ под DEBUG, используйте его команду D CS:100 для просмотра данных и инструкций. Попытка выполнить модуль типа .ЕХЕ, созданный из программы, написанной для формата .СОМ, приведет к ошибке. Удалите такой файл .ЕХЕ. Ключевые моменты • Ассемблер преобразует исходную программу в объектный файл и при необходимости генерирует таблицу перекрестных ссылок и листинг. • Таблица сегментов и групп, следующая за листингом, сгенерированным ассемблером, показывает сегменты и группы, определенные в программе. Таблица символов показывает все символы (имена элементов данных и метки инструкций). • Компоновщик преобразует объектный файл (.OBJ) в исполняемый (.ЕХЕ). 130 Часть II. Основы программирования на языке Ассемблер
• Упрощенные сегментные директивы генерируют следующие имена: DATA - для сегмента данных, STACK - для сегмента стека, и TEXT - для сегмента кода, а также несколько предопределенных эквивалентных имен. • Таблица перекрестных ссылок полезна для определения всех ссылок, возникающих при обращениях к элементам данных. • Программа типа .СОМ ограничена одним сегментом размером 64 К и меньше по размеру, чем программа типа .ЕХЕ, выполняющая аналогичные функции. В программе типа .СОМ не определяются сегменты данных и стека и не инициализируется регистр DS. • Программа, написанная как .СОМ, требует наличия директивы ORG 100H сразу после предложения SEGMENT в сегменте кода. Эта директива устанавливает адрес смещения на ячейку, следующую сразу за PSP, и выполнение программы начинается с этой ячейки. • Система размещает стек для программы .СОМ в конце программы. Вопросы и упражнения 1. Укажите, какая командная строка необходима, чтобы оттранслировать исходную программу MONITOR.ASM, создав объектный файл, файл листинга и файл перекрестных ссылок. 2. Укажите командную строку для компоновки MONITOR.OBJ из вопроса 1, предположив, что для этого требуется отдельная программа компоновки. 3. Укажите, какие команды требуются для того, чтобы запустить программу MONITOR.EXE из вопроса 2 (а) непосредственно из DOS или (б) под управлением DEBUG. 4. Объясните назначение следующих файлов: (a) *.ASM, (б) *.LST, (в) *.МАР, (г) *.CRF, (д) *.OBJ, (e) *.ЕХЕ. 5. Предполагая использование обычных сегментных директив и DATASEGM как имени сегмента данных, укажите две инструкции MOV, инициализирующие регистр DS. 6. Напишите ассемблерную программу, использующую стандартные определения сегментов, выполняющую следующие действия: (а) занести непосредственно значение 40 в регистр AL, (б) сдвинуть содержимое AL влево на 1 бит (SHL AL,1), (в) занести непосредственно значение 1ВН в регистр BL, (г) умножить AL на BL (MUL BL). Помните об инструкциях, необходимых для завершения программы. Программа не требует определения сегмента данных и инициализации регистра DS. Скопируйте заготовку программы и используйте редактор для внесения в нее необходимых изменений. Оттранслируйте, Глава 5. Трансляция, компоновка и исполнение программ 131 9*
скомпонуйте программу и используйте DEBUG для отслеживания выполнения программы, а также для просмотра инструкций и данных. 7. Перепишите программу из вопроса 6 с использованием упрощенных сегментных директив. Оттранслируйте и скомпонуйте ее, а затем сравните объектный код, таблицы символов и карты связей этих двух программ. Используйте DEBUG для отслеживания выполнения программы и просмотра инструкций и данных. 8. Добавьте в программу из вопроса 6 сегмент данных и выполните следующие действия. • Определите 1-байтные (DB) элементы: первый - с именем ITEMA и значением 40Н, второй - ITEMB со значением 1 АН. • Определите 2-байтный (DW) элемент с именем ГГЕМС и неопределенным значением. • Поместите значение ITEMA в AL и сдвиньте влево на 1 бит. • Умножьте AL на ГГЕМВ (MUL ITEMB). • Занесите произведение из АХ в ITEMC. • Оттранслируйте, скомпонуйте и отследите в DEBUG выполнение программы. 9. Перепишите программу из вопроса 8 с использованием упрощенных сегментных директив. Оттранслируйте и скомпонуйте ее, а затем сравните объектный код, таблицы символов и карты связей этих двух программ. Используйте DEBUG для тестирования программы. 10. Для каждого из следующих элементов данных укажите значение счетчика положения ассемблера: 0000 WORD1 DW 0 .... WORD2 DW 0 .... BYTE1 DB 0 EVEN WORD3 DW 0 BYTE2 DB 0 11. Каков максимальный размер программы типа .СОМ? 12. Какие сегменты можно определять для исходной программы, которая будет конвертирована в формат .СОМ? 13. Объясните, почему директива ORG 100H должна присутствовать в начале сегмента кода программы, подлежащей преобразованию в формат .СОМ? 14. Почему необязательно определять стек в программе типа .СОМ? 132 Часть II. Основы программирования на языке Ассемблер
15. Перепишите программу из вопроса 8 с использованием стандартных сегментных директив в формат .СОМ. Оттранслируйте ее, скомпонуйте, преобразуйте в формат .СОМ и выполните под управлением DEBUG. 16. Перепишите программу из вопроса 9 с использованием стандартных сегментных директив в формат .СОМ. Оттранслируйте ее, скомпонуйте, преобразуйте в формат .СОМ и выполните под управлением DEBUG. 17. Следующая программа содержит ряд ошибок периода трансляции, как показывают комментарии справа. Исправьте каждую ошибку. TITLE A04ASM2 (EXE) Program errors .MODEL SMALL ;1: Значение выходит ; за допустимые пределы ;2: Неправильный тип операнда : Типы операндов должны совпадать : Типы операндов должны совпадать ;5: Символ не определен Ассемблер не обнаружил еще одну ошибку, связанную с предложением MAIN PROC. В чем она состоит? DATA1 DATA2 DATA3 MAIN MAIN .STACK .DATA DB DB DW .CODE PROC MOV MOV MOV ADD MOV MOV INT ENDP END 64 25 280 ? AX,data DS,AX AX,DATAl AX,DATA2 DATA3,AX FX,4C00H 21H MAIN Глава 5. Трансляция, компоновка и исполнение программ 133
Глава 6 Символьные инструкции и адресация Цель: описать основы инструкций языка Ассемблер и требования к адресации данных. Введение В этой главе рассматриваются категории набора инструкций процессора. Подробно рассмотрены инструкции MOV, MOVSX, MOVZX, XCHG, LEA, INC, DEC, ADD, SUB и INT, а также использование констант в операндах инструкций в качестве непосредственных значений. Наконец, описаны основы форматов адресации, используемых в оставшейся части книги, а также выравнивание адресов и префикс переопределения сегмента. Обзор набора символьных инструкций Далее приведен список символьных инструкций семейства процессоров Intel, отсортированных по категориям. Хотя список весьма впечатляет своими размерами, многие из инструкций используются довольно редко. Арифметические ADC: сложение с переносом INC: инкремент (увеличение) на 1 ADD: сложение двоичных чисел MUL: беззнаковое умножение DEC: декремент (уменьшение) на 1 NEG: изменение знака DIV: деление без знака SBB: вычитание с заемом (отрицательный перенос) IDIV: деление со знаком (нацело) SUB: вычитание двоичных чисел IMUL: умножение со знаком XADD: обмен и сложение (целых чисел) Ввод/вывод IN: ввести байт или слово OUT: вывести байт или слово INSn: ввести строку (80286+) OUTSn: вывести строку (80286+) 134 Часть II. Основы программирования на языке Ассемблер
Логические операции AND: логическое И NOT: логическое НЕ Операции с флагами CLC: очистить флаг переноса CLD: очистить флаг направления CLI: очистить флаг прерывания CMC: дополнить флаг переноса LAHF: загрузить АН из регистра Flags POPF: получить значения регистра Flags из стека OR: логическое ИЛИ XOR: логическое ИСКЛЮЧАЮЩЕЕ ИЛИ PUSHF: поместить значения регистра Flags в стек SAHF: сохранить АН в регистре Flags STC: установить флаг переноса STD: установить флаг направления STI: установить флаг прерывания Передача данных LDS: загрузить регистр сегмента данных MOVS: переместить строку LEA: загрузить эффективный адрес LES: загрузить дополнительный сегментный регистр LODS: загрузить строку LSS: загрузить регистр сегмента стека MOV: переместить данные Переходы (условные) INTO: прервать по переполнению JA: перейти, если больше (для беззнаковых чисел) JAE: перейти, если больше или равно (для беззнаковых чисел) JB: перейти, если меньше (для беззнаковых чисел) JBE: перейти, если меньше или равно (для беззнаковых чисел) JC: перейти, если есть перенос JCXZ: перейти, если СХ содержит О MOVSX: переместить с установкой знака MOVZX: переместить с заполнением нулями STOS: сохранить строку XCHG: поменять местами XLAT: перевести JNC: перейти, если нет переноса JNE: перейти, если не равно JNG: перейти, если не больше JNGE: перейти, если не больше или равно JNL: перейти, если не меньше JNLE: перейти, если не меньше или равно JNO перейти, если нет переполнения Глава 6. Символьные инструкции и адресация 135
JE: перейти, если равно JG: перейти, если больше (для знаковых чисел) JGE: перейти, если больше или равно (для знаковых чисел) JL: перейти, если меньше (для знаковых чисел) JLE: перейти, если меньше или равно (для знаковых чисел) JNA: перейти, если не больше (для беззнаковых чисел) JNAE: перейти, если не больше или равно (для беззнаковых чисел) JNB: перейти, если не меньше (для беззнаковых чисел) JNBE: перейти, если не меньше или равно (для беззнаковых чисел) JNP: перейти, если не установлен флаг четности JNS: перейти, если не установлен флаг знака JNZ: перейти, если не ноль JO: перейти при переполнении JP: перейти при нечетности JPE: перейти при четности JPO: перейти при нечетности JS: перейти, если установлен флаг знака JZ: перейти, если ноль Переходы (безусловные) CALL: вызов процедуры JMP: безусловный переход INT: прерывание RET: возврат IRET: возврат из прерывания RETN/RETF: возврат в диапазоне Near/Far Преобразование ASCII-BCD AAA: приведение к ASCII после сложения AAD: приведение к ASCII после деления ААМ: приведение к ASCII после умножения Преобразование типов CBW: преобразовать байт в слово CDQ: преобразовать двойное слово в счетверенное (80386+) AAS: приведение к ASCII после вычитания DAA: десятичное выравнивание после сложения DAS: десятичное выравнивание после вычитания CWD: преобразовать слово в двойное слово CWDE: преобразовать двойное слово в расширенное двойное слово 136 Часть II. Основы программирования на языке Ассемблер
Сдвиг битов RCL: вращать налево через перенос RCR: вращать направо через перенос ROL: вращать налево ROR: вращать направо SAL: алгебраический сдвиг влево Сравнение BSF/BSR: сканировать биты (80386+) BT/BTC/BTR/BTS: проверить биты (80386+) СМР: сравнить CMPSn: сравнить строку Стековые операции ENTER: выделить кадр стека (80286+) LEAVE: отменить выделение кадра стека (80286+) POP: считать слово из стека SAR: алгебраический сдвиг вправо SHL: логический сдвиг влево SHR: логический сдвиг вправо SHLD: сдвиг влево двойного слова (80386+) SHRD: сдвиг вправо двойного слова (80386+) CMPXCHG: сравнить и поменять местами (80486+) CMPXCHG8B: сравнить и поменять местами (Pentium+) TEST: проверить биты РОРА: считать значения всех регистров общего назначения из стека (80286+) PUSH: поместить слово в стек POPF: считать флаги из стека Строковые операции CMPS: сравнить строку LODS: загрузить строку MOVS: переместить строку REP: повторить строку REPE: повторять, пока равно Циклы LOOP: повторять до завершения LOOPE: повторять, пока равно PUSHA: поместить значения всех регистров общего назначения в стек (80286+) PUSHF: поместить флаги в стек REPZ: повторять, пока ноль REPNE: повторять, пока не равно REPNZ: повторять, пока не ноль SCAS: сканировать строку STOS: сохранить строку LOOPNEW: повторять, пока не равно (80386+) LOOPNZW: повторять, пока не ноль (80386+) Глава 6. Символьные инструкции и адресация 137
LOOPNE: повторять, пока не равно LOOPZ: повторять пока ноль LOOPNZ: повторять, пока не ноль Управление процессором HLT: перейти в состояние останова NOP: не выполнять никакой операции LOCK: закрыть шину WATT: перейти в состояние ожидания Инструкции, для которых указан тип процессора, например, (803 86+), требуют использования директивы процессора (рассмотренной в гл. 3) для правильной трансляции. Инструкции перемещения данных В этом параграфе описаны некоторые широко используемые инструкции, связанные с перемещением данных. Инструкция MOV MOV перемещает (точнее, копирует) данные, на которые ссылается адрес во втором операнде, по адресу, указанному в первом операнде. Значение в источнике остается неизменным. Операнды, ссылающиеся на регистры и память, должны совпадать по размеру (оба должны быть байтами, словами или двойными словами). Формат инструкции MOV следующий: [метка: ] MOV Регистр/память, регистр/память/непосредственное значение Вот четыре примера корректных инструкций MOV с разными видами операндов, подразумевающих наличие следующих определений элементов данных: BYTEFLD DB ? /Определяем байт WORDFLD DW ? ;Определяем слово 1. Перемещение данных между регистрами MOV EDX,ECX /Регистр-регистр MOV ES,AX ;Регистр-сегментный регистр MOV BYTEFLD,DH ;Регистр-память, напрямую MOV [DI],BX ;Регистр-память, непрямая адресация 2. Перемещение непосредственных данных MOV СХ,40Н /Непосредственное значение-регистр MOV BYTEFLD,2 5 ;Непосредственное значение-память, ;напрямую MOV WORDFLD[BX],16Н ;Непосредственное значение-память, ;непрямая адресация 138 Часть II. Основы программирования на языке Ассемблер
3. Перемещение данных из памяти MOV CH,BYTEFLD ;Память-регистр, напрямую MOV CX,WORDFLD[BX] ;Память-регистр, косвенная адресация 4. Перемещения в сегментных регистрах MOV AX,DS ;Сегментный регистр-регистр MOV WORDFLD,DS ;Сегментный регистр-память Вы можете помещать в регистр байт (MOV CH,BYTEFLD), слово (MOV CX,WORDFLD) или двойное слово (MOV ECX, DWORDFLD). Операнд влияет только на часть изменяемого регистра, например, помещение байта в CL оставит без изменений содержимое СН. Неправильные инструкции MOV, например, таковы: MOV DL,WORD_VAL ;Слово-байт MOV CX,BYTE_VAL ;БаЙт-СЛОВО MOV WORD_VAL,ЕВХ /Двойное слово-слово MOV BYTE_VAL2,BYTE_VAL1 ;Память-память MOV ES,22 5 /Непосредственное значение- ;сегментный регистр MOV ES,DS /Сегментный регистр- сегментный регистр Выполнение таких действий требует более чем одной инструкции. Инструкции перемещения и заполнения: MOVSX и MOVZX У инструкции MOV приемник должен быть такого же размера, как и источник, например, байт и байт или слово и слово. Инструкции MOVSX и MOVZX, выполняющие перемещение с заполнением (80386+), выполняют перенос данных из байта или слова в слово или двойное слово. Вот их формат: [метка: ] MOVSX/MOVZX Регистр/память, регистр /память /непосредственное значение Инструкция MOVSX, предназначенная для использования со знаковыми арифметическими величинами, перемещает байт или слово в слово или двойное слово, корректно выполняя установку знакового бита результата. MOVZX предназначена для работы с беззнаковыми числами и заполняет неиспользуемые разряды результата нулями. Например, рассмотрим помещение байта 10110000 в слово; результат будет зависеть от примененной инструкции. MOVSX СХ,10110000В ;СХ = 11111111 10110000 MOVZX СХ,10110000В ;СХ = 00000000 10110000 Вот еще несколько примеров использования MOVSX и MOVZX: BYTEl DB 2 5 ;Байт WORD1 DW 40 ;Слово Глава 6. Символьные инструкции и адресация 139
DWORD DD 16 .386 ... MOVSX CX,BYTE1 MOVZX WORDl,BH MOVSX EBX,WORDl MOVZX DWORDl,CX ;Двойное слово ;Байт в слово ;Байт в слово ;Слово в двойное слово ;Слово в двойное слово Директива процессора .386 указывает ассемблеру воспринимать инструкции, появившиеся в процессоре 80386. Знаковые и беззнаковые величины подробно рассмотрены в главах 7 и 12. Инструкция XCHG XCHG выполняет другой вид перемещения данных: вместо простого копирования данных из одного места в другое она меняет местами содержимое двух элементов данных. Ее формат таков: [метка: ] XCHG Регистр/память, регистр/память Корректное использование XCHG включает перемещения данных между регистрами и между регистром и памятью. Вот два примера: WORDQ DW ? /Объявляем слово XCHG CL,BH ;Обмен содержимым между двумя ;регистрами XCHG CX,WORDQ ;Обмен содержимым между регистром и ;памятью Инструкция LEA LEA полезна для инициализации регистров адресами смещения. Формат LEA таков: [метка: ] LEA Регистр, память Обычно LEA используется для инициализации ВХ, SI или DI для индексации адреса в памяти, что часто делается в примерах этой книги. Вот еще пример: DATATBL BYTEFLD DB 25 DUP (?) DB ? ;Таблица из 25 байтов ;Один байт LEA BX,DATATBL MOV BYTEFLD,[BX] ;Загрузить адрес смещения ;Переместить первый байт из DATATBL Эквивалентом инструкции LEA является инструкция MOV с оператором OFFSET, генерирующая немного более компактный машинный код и используй 14 в виде MOV BX,OFFSET DATATBL /Загрузить адрес смещения 140 Часть II. Основы программирования на языке Ассемблер
Основные арифметические инструкции В этом параграфе содержатся описания основных арифметических инструкций для сложения и вычитания - INC, DEC, ADD и SUB. Инструкции INC и DEC Эти инструкции применяются соответственно для увеличения и уменьшения значений в регистрах и памяти на 1. Формат этих инструкций таков: [метка: ] INC Регистр/память [метка: ] DEC Регистр/память Заметьте, что INC и DEC используют только один операнд. В зависимости от результата эти инструкции могут изменять значение флагов OF (перенос в знаковый бит, флаг переноса не затрагивается), SF (плюс/минус) и ZF (ноль/не ноль). Инструкции условных переходов могут проверять эти флаги. Если однобайтовое значение содержит FFH и "увеличивается" командой INC до ООН, то ZF устанавливается в ноль, a SF в плюс. DEC "уменьшает" ООН до FFH и устанавливает SF в плюс, a ZF в не ноль. Инструкции ADD и SUB Здесь кратко описаны инструкции ADD и SUB; более подробное описание содержится в гл. 12. Их формат таков: [метка: ] ADD/SUB Регистр/память, регистр/память/непосредственное значение ADD AX,CX ADD EBX,DBLWORD Корректные операции включают в себя операции регистр-регистр, регистр- память, регистр-непосредственное значение, память-регистр, память-память, память-непосредственное значение. Вот несколько примеров: /Добавить регистр к регистру ;Добавить двойное слово ; из памяти к регистру SUB BL,10 ; Вычесть непосредственное значение ; из регистра Эти операции оказывают влияние на флаги AF, CF, OF, PF, SF и ZF. Например, нулевой результат устанавливает флаг ZF, а отрицательный - SF. Операции блочного перемещения В программах, рассмотренных на данный момент, встречались операции перемещения непосредственных данных в регистр, перемещения данных из Глава 6. Символьные инструкции и адресация 141
определенных элементов в памяти в регистр, перемещения содержимого регистра в память и перемещения содержимого одного регистра в другой. Во всех случая длина перемещаемых данных была ограничена одним, двумя или четырьмя байтами, а операции, перемещающие данные из одной области памяти непосредственно в другую, не встречались. В этом параграфе описано блочное перемещение. Другой метод достижения аналогичного результата - использование строковых инструкций - рассмотрен в гл. 11. В программе на рис. 6.1 сегмент данных содержит два 9-байтовых поля, определенных как HEADNG1 и HEADNG2. Цель программы - переместить содержимое HEADNG1 в HEADNG2 байт за байтом слева направо. Поскольку длина этих полей составляет 9 байт, требуется нечто большее, чем простые инструкции MOV. Программа использует ряд новых возможностей. TITLE HEADNG1 HEADNG2 A10MAIN А20: A10MAIN A06MOVE .MODEL .STACK .DATA DB DB .CODE PROC MOV MOV MOV MOV LEA LEA MOV MOV INC INC DEC JNZ MOV LEA INT MOV INT ENDP END (EXE) Операции SMALL 64 'InterTech' 9 DUP('*'), FAR AX,adata DS,AX ES,AX CX,09 SI,HEADNG1 DI,HEADNG2 AL,[SI] [DI],AL SI DI CX A20 AH,09H DX,HEADNG2 21H AX,4C00H 21H A10MAIN блочного перемещения $' Инициализация сегментных регистров Инициализация для перемещения девяти символов Инициализация адресов смещений данных Получить символ из HEADNG1, поместить в HEADNG2 •Увеличить адрес для HEADNG1 •Увеличить адрес для HEADNG2 •Уменьшить число повторений •Достигнут ноль? Если нет,повторить • Конец цикла •Выводим на экран HEADNG2 ;Завершение работы Рис. 6.1. Операции блочного перемещения 142 Часть II. Основы программирования на языке Ассемблер
Для последовательной обработки HEADNG1 и HEADNG2 программа инициализирует СХ значением 9 (длиной обоих полей) и использует SI и DI для индексации. Две инструкции LEA загружают адреса HEADNG1 и HEADNG2 соответственно в SI и DI следующим образом: LEA SI,HEADNG1 /Инициализация адреса смещения HEADNG1 LEA DI,HEADNG2 ;Инициализация адреса смещения HEADNG2 Программа использует адреса, хранящиеся в SI и DI, для перемещения первого байта HEADNG1 в HEADNG2. Квадратные скобки вокруг SI и DI в операндах MOV означают непрямую адресацию; инструкции должны использовать величину смещения, хранящуюся в этих регистрах, для обращения к области памяти. Поэтому MOV AL,[SI] означает "Поместить байт, хранящийся в памяти по адресу в SI (HEADNG1+0), в AL". А инструкция MOV [DI],AL означает "Поместить байт, хранящийся в AL, в память по адресу в DI (HEADNG2+0)". Программа должна повторять выполнение этих инструкций 9 раз, по одному разу для каждого из символов в соответствующих элементах данных. Для достижения этого она использует инструкцию условного перехода JNZ (перейти, если не ноль), ранее не применявшуюся и не рассматривавшуюся. Две инструкции INC увеличивают значения в DI и SI на 1, a DEC - уменьшает значение в СХ на 1. DEC также устанавливает или сбрасывает флаг ZF в зависимости от результата в регистре СХ. Если результат не нулевой, значит, все еще остались символы, подлежащие обработке, и JNZ переходит к метке А20 для повторения инструкций перемещения. А поскольку значения в SI и DI были увеличены на 1, следующие MOV обратятся к HEADNG1+1 и HEADNG2+1. Цикл будет повторяться аналогичным образом, пока не будут перемещены все 9 символов, до HEADNG2+8. Кроме того, программа содержит инструкции, выводящие на экран содержимое HEADNG2 после обработки: (1) загрузка кода функции 09Н в АН для запроса на вывод; (2) загрузка адреса HEADNG2 в DX; и (3) вызов прерывания INT21H. Операция INT показывает все символы, начиная с первого байта HEADNG2 и заканчивая завершающим знаком '$', определенным непосредственно за HEADNG2. Эта операция более детально описана в гл. 8. В качестве упражнения введите эту программу, оттранслируйте ее, скомпонуйте и используйте DEBUG для проверки ее выполнения. Обратите внимание на эффект, оказываемый ее выполнением на стек, регистры и IP (в особенности после выполнения инструкции JNZ). Используйте команду D DS:0 для наблюдения за изменением содержимого HEADNG2. Инструкция INT Эта инструкция позволяет программе прервать работу, например, для инициализации драйвера мыши: MOV АХ,ООН /Инициализация INT 33H ; драйвера мыши Глава 6. Символьные инструкции и адресация 143
INT выходит из нормального режима обработки инструкций, обращается к таблице векторов прерываний в нижней памяти, и находит в ней адрес соответствующей подпрограммы-обработчика. Затем выполняется соответствующий код из BIOS или операционной системы и осуществляется возврат в программу для продолжения работы. Чаще всего прерывание должно выполнить какие-то сложные действия операций ввода/вывода. Прерывание требует наличия возможности выхода из программы и, в случае успешного завершения, возврата в нее. Для этого INT выполняет следующие действия: • помещает содержимое регистра Flags в стек (предварительно увеличив значение SP на 2); ^ • очищает флаги Interrupt и Trap; • помещает в стек содержимое регистра СХ; • помещает в стек содержимое регистра IP (содержащего адрес следующей инструкции); • выполняет требуемую операцию. Для возвращения из обработчика прерывания в основную программу выполняется инструкция IRET (Interrupt Return - возврат из прерывания), возвращающая из стека назад все сохраненные значения. Восстановленные значения CS:IP обеспечивают продолжение обработки, начиная с инструкции, следующей за INT. у Поскольку процесс, описанный только что, выполняется полностью автоматически, вы только должны определить стек достаточного размера для сохранения значений регистров и указать требуемый код операции. Режимы адресации Адрес в операнде указывает источник данных, которые инструкция должна обработать. Некоторые инструкции, например, CLC или RET, не требуют операнда, в то время как другие могут использовать один, два или три операнда. Если инструкция использует два операнда, первый операнд указывает приемник, содержащий данные в регистре или в памяти, а второй - источник, содержащий или непосредственные данные, которые необходимо обработать, или адрес (в памяти или в регистре), по которому расположены эти данные. Большинство инструкций не изменяют содержимого источника. Три основных типа адресов - регистр, непосредственное значение и память. Адрес в памяти может принадлежать к одной из шести разновидностей, так что всего существует восемь режимов адресации. Регистровая адресация В этом режиме операнд предоставляет имя любого из 8-, 16- или 32- разрядных регистров. В зависимости от конкретной инструкции регистр может 144 Часть II. Основы программирования на языке Ассемблер
быть в первом операнде, во втором или в обоих, как показано на следующих примерах: MOV DX,WORD_MEM ;Регистр в первом операнде MOV WORD_MEM,CX ;Регистр во втором операнде MOV EDX,EBX ;Регистры в обоих операндах Поскольку работа с регистрами не требует обращений к памяти, это наиболее быстро выполняющиеся инструкции. у Непосредственная адресация Непосредственный операнд содержит константное значение или выражение. Вот несколько примеров корректных непосредственных констант: Шестнадцатеричная: 0148Н Десятичная: 328 (ассемблер преобразует его в 0148Н) Двоичная: 101001000В (ассемблер"преобразует его в 0148Н) Для многих инструкций с двумя операндами первый операнд может быть регистром или ячейкой памяти, а второй - непосредственным значением. Поле приемника (первый операнд) определяет длину элемента данных. Вот несколько примеров: BYTE_VAL DB 150 /Определяем байт WORD_VAL DW 3 00 ;Определяем слово DBWD_VAL DD 0 ;Определяем двойное слово SUB BYTE_VAL,50 MOV WORD_VAL/40H MOV DBWD_VAL,0 MOV AX,0245H Непосредственное значение и память (байт) Непосредственное значение и память (слово) Непосредственное значение и память (двойное слово) Непосредственное значение и регистр (слово) Инструкция в предыдущей строке помещает непосредственное значение 0245Н в АХ. 3-байтовый объектный код - В84502, где В8 означает "поместить непосредственное значение в АХ", а следующие два байта - непосредственно помещаемое значение (4502Н, в последовательности с переставленными байтами). Использование в качестве операнда непосредственного значения ускоряет обработку по сравнению с использованием числовой константы, определенной в сегменте данных, на которую необходимо ссылаться в операнде. Длина непосредственной константы не может превышать длины, определенной первым операндом. В следующем примере (с ошибкой) непосредственный операнд имеет длину 2 байта, но AL - только 1 байт: MOV AL,0245H Неправильная -длина непосредственного значения Однако если непосредственный операнд короче, чем принимающий, как в MOV АХ/048Н ;Допустимая длина непосредственного значения Глава 6. Символьные инструкции и адресация 145 10 — 5-741
то Ассемблер дополнит непосредственное значение до 0048Н и сохранит его в объектном коде как 4800Н. Прямое обращение к памяти В этом формате один из операндов ссылается на область в памяти, а второй - на регистр (единственные инструкции, в которых оба операнда могут обращаться непосредственно к памяти - MOVS и CMPS). DS -сегментный регистр, по умолчанию используемый для адресации данных в памяти, в виде DS:cMe- щение. Вот примеры такого формата: ADD BYTE_VAL,DL MOV BX/WORD_VAL Добавить значение из регистра к значению в памяти (байт) Поместить значение из регистра в память (слово) Адресация со смещением Эта адресация - разновидность адресации с прямым обращением к памяти - использует арифметические операции для изменения адреса. Следующие примеры используют такие определения: BYTE_TBL DB 12, 15, 16, 22, ...;Таблица байтов WORD_TBL DB 163, 227, 485, .../Таблица слов DBWD_TBL DB 465, 563, 897, .../Таблица двойных слов Операции с байтами Следующие инструкции обращаются к байтам из BYTETBL: MOV CL,BYTE_TBL[2] ;Получить третий байт из BYTE_TBL MOV CL,BYTE_TBL+2 ;Ta же операция Первый MOV использует арифметический оператор для получения третьего байта (его значение равно 16) из BYTETBL (BYTE_TBL[0] - первый байт, BYTE_TBL[1] - второй, BYTE_TBL[2] - третий, и так далее). Второй MOV использует оператор + для достижения точно такого же результата. Операции со словами Следующие инструкции обращаются к словам из WORDTBL: MOV СХ,WORD_TBL[4] ;Получить слово из WORD_TBL MOV CX,WORD_TBL+4 ; Та же операция Первый MOV использует арифметический оператор для получения третьего слова из WORDTBL (WORD_TBL[0] - первое слово, WORD_TBL[2] - второе слово, WORD_TBL[4] -третье слово). Операции с двойными словами Следующие инструкции обращаются к двойным словам из DBWDTBL: 146 Часть II. Основы программирования на языке Ассемблер
MOV ECX,DBWD_TBL[8] /Получить двойное слово из DBWD_TBL MOV ECX,DBWD_TBL+8 ;Ta же операция Первый MOV использует арифметический оператор для получения третьего двойного слова из DBWDTBL (DBWD_TBL[0] - первое двойное слово, DBWD_TBL[4] - второе, DBWD_TBL[8] -третье). Непрямая адресация памяти Непрямая адресация использует способность компьютера адресовать память в форме сегмент.смещение. Для этого используются основные (ВХ и ВР) и индексные регистры (DI и SI), указанные в квадратных скобках, что определяет обращение к памяти. Если вы указываете директиву процессора .386, .486 или .586, вы также можете использовать любой из регистров общего назначения (ЕАХ, ЕВХ, ЕСХ или EDX) для непрямой адресации. Непрямой адрес, например, [DI], указывает ассемблеру, что подлежащий использованию адрес памяти при исполнении программы будет храниться в регистре DI. BX, DI и SI связаны с DS как DS:BX, DS:DI и DS:SI для обработки данных в сегменте данных. ВР связан с SS как SS:BP для обработки данных в стеке. Если первый операнд содержит непрямой адрес, то второй операнд ссылается на регистр или непосредственное значение; если второй операнд содержит непрямой адрес, то первый операнд ссылается на регистр. Заметьте, что квадратные скобки, обрамляющие ВР, ВХ, DI или SI, подразумевают непрямой операнд, и процессор воспринимает содержимое регистра как адрес смещения при исполнении программы. В следующем примере LEA сначала инициализирует ВХ адресом смещения DATAVAL. Затем MOV использует этот адрес из ВХ для сохранения CL в ячейке памяти, на которую ВХ указывает, то есть в DATAVAL: DATA_VAL DB 50 /Определяем байт LEA BX,DATA_VAL ;Загружаем смещение в ВХ MOV [BX],CL /Помещаем CL в DATA_VAL Эффект двух инструкций LEA и MOV такой же, как инструкции MOV DATA_VAL,25, хотя индексная адресация редко применяется с такими тривиальными целями. Вот еще несколько примеров непрямых операндов: ADD CL,[ВХ] /Второй операнд - DS:BX MOV BYTE_PTR [DI],25 /Первый операнд - DS:DI ADD [BP],CL /Первый операнд - SS:ВР .386 MOV DX,[ЕАХ] /Второй операнд - DS:ЕАХ В следующем примере в качестве смещения использовано абсолютное значение: MOV СХ,DS:[38B0H] /Слово в ячейке памяти со смещением 38В0Н Глава 6. Символьные инструкции и адресация 147 ю*
Базовая адресация со смещением Этот вид адресации также использует основные регистры ВХ и ВР и индексные регистры SI и DI, но в сочетании со смещением (числом, т. е. величиной смещения) для получения реального адреса. Следующая инструкция MOV помещает 0 в ячейку, расположенную на два байта от начала DATA_TBL: DATA_TBL DB 3 65 DUP (?) /Определяем байты LEA BX,DATA_TBL ;Загрузить адрес DATA_TBL в ВХ MOV BYTE PTR [BX+2]/0 /Поместить 0 в DATA_TBL+2 Вот еще несколько примеров: ADD CL,[DI+12] SUB DATA_TBL[SI] ,2 5 MOV DATA_TBL[DI],DL Смещение из DI плюс 12 (или 12[DI]) SI содержит смещение (0-3 64) DI содержит смещение (0-3 64) .386 MOV DX,[EAX+4] /Смещение из ЕАХ плюс 4 ADD DATA_TBL[EDX] , CL ; EDX + смещение DATA_TBL Базово-индексная адресация Этот вид адресации также использует основные регистры ВХ и ВР и индексные регистры SI и DI для получения реального адреса. Например, [BX+DI] значит адрес в ВХ плюс адрес в DI. Обычно этот вид адресации используется при работе с двумерными массивами, при этом, например, ВХ указывает на столбец, a SI - на строку. Вот несколько примеров: MOV AX,[BX+SI] /Считать слово из памяти ADD [BX+DI], CL /Добавить байт к байту в 'памяти Базово-индексная адресация со смещением Этот вид адресации является вариацией предыдущего и использует базовый регистр, индексный регистр и смещение для получения реального адреса. Вот несколько примеров: MOV AX, [BX+DI+10] ;или 10[BX+DI] MOV CL,DATA_TBL ;или [BX+DI+DATA_TBL] .386 MOV EBX,[ECX*2+ESP+4] -Последняя строка заносит в ЕВХ содержимое двойного слова, расположенного по адресу ECX*2+ESP+4. 148 Часть II. Основы программирования на языке Ассемблер
Префикс переопределения сегмента Процессор при необходимости автоматически обращается к различным сегментным регистрам: к CS:IP - за очередной инструкцией для выполнения, DSiCMeinemie - за данными для обработки, SS:SP - для работы со стеком. Однако бывают случаи, когда вам самим приходится обрабатывать данные, относящиеся к другому сегментному регистру: ES, FS или GS. Такими данными может, например, быть большая таблица, загруженная с диска в память и размещенная в отдельном сегменте программы. Вы можете использовать любые инструкции для работы с данными в другом сегменте, но вы должны определить соответствующий сегментный регистр. Пусть, например, адрес другого сегмента занесен в ES, a BX содержит адрес смещения в этом сегменте. Требуется поместить слово (2 байта) из этого сегмента в регистр DX: MOV DX,ES:[BX] ;Поместить в DX слово/ расположенное по адресу ES: [BX] Префикс "ES:" представляет собой оператор переопределения, означающий "заменить обычное использование регистра DS использованием регистра ES". Следующий пример показывает помещение байта из CL в этот дополнительный сегмент по адресу, формируемому значением в SI плюс 36: MOV ES:[SI+36],CL ;Поместить по адресу ES:[SI+36] байт из CL Ассемблер генерирует объектный код с оператором переопределения в виде однобайтового префикса (26Н) непосредственно перед инструкцией, как если бы вы ввели две инструкции ES: MOV DX,[ВХ] ;Поместить в DX из ES:[ВХ] ES: MOV [SI+36],CL ; Поместить в ES:[SI+36] из CL Адреса типа NEAR и FAR Адрес в программе может быть ближним (NEAR) или дальним (FAR). Ближний адрес состоит только из 16-битовой части смещения адреса. Инструкция, использующая ближний адрес, подразумевает текущий сегмент, т.е. DS - для сегмента данных, CS - для сегмента кода. Дальний адрес содержит и адрес сегмента, и смещение в этом сегменте в 32-битовой форме сегмент:смещение. Инструкция может использовать дальний адрес и в пределах текущего сегмента, и за его пределами. Почти все программы на ассемблере в реальном режиме используют ближние адреса, и именно их ассемблер использует при отсутствии других указаний. Большие программы могут потребовать использования дальних адресов, как и программы, использующие модель памяти Flat. * Глава 6. Символьные инструкции и адресация 149
Выравнивание адресов данных Поскольку процессоры 8086 и 80286 имеют 16-разрядную шину данных, они работают быстрее, если запрашиваемые слова начинаются с четных (кратных 2) адресов. Рассмотрим ситуацию, когда в байтах со смещениями 0012Н и 0013Н хранится слово 63А7Н. Процессор может получить слово со смещения 0012Н, прямо поместив его, допустим, в АХ. Но слово может начинаться и с нечетного адреса, например, с 0013Н: XX 63 А7 XX Смещение: 0012 0013 0014 0015 В этом случае процессор осуществляет перенос слова в регистр в два этапа. Сначала он считывает байты с адресами 0012Н и 0013Н и помещает байт со смещением 0013Н (63Н) в AL. После этого он считывает байты с адресами 0014Н и 0015Н и помещает байт со смещением 0014Н (А7Н) в АН. Теперь АХ содержит А763Н. Нет необходимости производить какие-то особые процедуры программирования для четных и нечетных адресов или знать, четный или нечетный адрес у элемента данных. Процессоры 80386+ имеют 32-разрядную шину данных и, соответственно, предпочитают выравнивание элементов данных по адресам, нацело делящимся на 4 (двойные слова). Благодаря особенностям внутреннего устройства процессоры 80486+ предпочитают выравнивания по границам параграфов - адресам, кратным 16. Вы можете использовать директивы ALIGN или EVEN для принудительного выравнивания элементов. Например, директивы ALIGN 2 или EVEN выравнивают элементы данных по четным границам (слова), a ALIGN 4 - по границам, нацело делящимся на 4 (двойные слова). Когда ассемблер размещает элемент данных по выровненной границе, он соответственно увеличивает значение счетчика положения. Поскольку сегмент данных, определенный с выравниванием PARA, автоматически начинается с границы параграфа, вы можете разместить сначала элементы данных, являющиеся двойными словами, затем - словами, а затем - байтами. Принудительное выравнивание может быть полезно в приложениях, критичных по скорости выполнения. Ключевые моменты • Инструкция MOV перемещает (копирует) данные, к которым обращается операнд 2, по адресу, указанному в операнде 1. • Инструкция LEA полезна для инициализации регистра адресом смещения. "150 Часть II. Основы программирования на языке Ассемблер
• Инструкции INC и DEC увеличивают и уменьшают на 1 содержимое регистров или элементов данных в памяти. • Инструкция INT прерывает выполнение программы, передает выполнение операционной системе или BIOS для выполнения требуемого действия и возобновляет выполнение программы. • Операнд указывает источник данных для инструкции. Инструкция может не иметь операндов вообще или иметь от одного до трех операндов. • Если у инструкции 2 операнда, операнд 2 - источник, из которого берутся данные, подлежащие обработке (указывается адрес в памяти, регистр или непосредственно величина, подлежащая обработке). Операнд 1 - приемник, который указывает, куда помещаются данные, прошедшие обработку (адрес в памяти или регистр). • В формате непосредственных данных второй из двух операндов содержит константное значение или выражение. Непосредственные операнды должны быть подходящими по размеру для приемника, то есть быть байтом для байта, словом для слова или двойным словом для двойного слова. • В непосредственном формате адресации памяти один из операндов ссылается на область в памяти, а второй --на регистр. • Непрямая адресация использует преимущества способности процессора адресовать память в форме сегментхмещение. Для этого используются регистры ВХ, ВР, DI и SI, указанные в квадратных скобках, как операторы индекса. ВР связан с SS (в виде SS:BP) для обращения к данным в стеке. ВХ, DI и SI связаны с DS в виде DS:BX, DS:DI и DS:SI, соответственно, для обращения к данным в сегменте данных. • При непрямой адресации регистры можно комбинировать, например, [BX+DI], что значит "смещение из ВХ плюс смещение из DI". Вопросы и упражнения 1. Для инструкции с двумя операндами, какой из них является (а) источником, (б) приемником? 2. Для каждой из последующих независимых инструкций укажите результирующее значение операнда-приемника. Проверьте, корректна ли инструкция. Подразумевайте, что BYTE1 определен как DB 05. Инструкция (a) MOV СХ, (б) MOV CL (в) MOV АХ (г) ADD DL (д) INC DX Перед 25Н 0 BYTE1 BYTE1 СХ = СХ = АХ = DX = DX = ООООН FFFFH 1234Н 0120Н FFFFH После СХ = СХ = АХ = DX = DX = Глава 6. Символьные инструкции и адресация 151
DX = AX = ex = ex = FFFFH 1234H 1234H 1234H DX = AX = CX = ex = (е) INC DL (ж) XCHG AH,AL (з) SUB CX,CX (и) XCHG CX,CX 3. (а) Чем существенно различаются в исполнении две следующие инструкции: MOV DX,AC24H MOV DX,[AC24H] (б) Какой смысл имеют квадратные скобки во второй инструкции MOV? 4. (а) Чем существенно различаются в исполнении две следующие инструкции: MOV BX,WORDA MOV [BX],WORDA (б) Какой вид адресации использован в первом операнде второй инструкции? 5. Объясните смысл инструкции SUB CX,[BX+DI+4] 6. В следующем выражении (а) найдите ошибку, (б) предложите способ ее исправления: SUB [SI],[BX] 7. Подразумевая использование следующих определений данных, найдите ошибки в выражениях и укажите соответствующие правильные инструкции: BYTEl DB 48 BYTE2 DB 32 WORD3 DB 216 (а) MOV BYTE1/BYTE2 (б) SUB CL,WORD3 /операнд 2 правилен (в) ADD DH,051CH /операнд 2 правилен 8. Укажите символьные инструкции, выполняющие следующие действия над непосредственными данными: (а) добавление 26Н к ВХ, (б) вычитание 26Н из СХ, (в) сдвиг СН на два бита вправо, (г) сдвиг BYTE1 на два бита влево, (д) помещение значения 426 в СХ, (е) сравнение BYTE1 и 25Н. 9. Укажите, как одной инструкцией поменять местами содержимое слова WORD3 и регистра АХ. 10. Укажите инструкцию, загружающую в SI адрес (смещение) элемента, названного RATE_TBL. 11. Опишите в общих чертах назначение инструкции INT. 12. Объясните, как изменяет содержимое стека инструкция (a) INT, (б) IRET. 13. Введите, оттранслируйте, скомпонуйте и выполните под управлением DEBUG программу, которая: (а) определяет байты с именами BYTE1 и BYTE2 (содержащими любые значения) и слово с именем WORD3 (содержащее ноль), (б) помещает содержимое BYTE1 в AL, (в) добавляет содержимое 152 Часть II. Основы программирования на языке Ассемблер
BYTE2 к AL, (г) помещает непосредственное значение 42Н в DL, ,(д) меняет местами содержимое AL и DL, (е) перемножает DL и AL (MUL DL), (ж) перемещает произведение из АХ в WORD3. 14. Объясните, почему неправильна инструкция ADD [BX],25, и исправьте ошибку. 15. Укажите инструкции, необходимые для последовательного добавления каждого байта, определенного в BYTE_TBL, к содержимому регистра CL. Используйте адресацию с прямым смещением и пять инструкций ADD. BYTE_TBL DB 12, 15, 16, 10, 8 16. Используйте BYTE_TBL из вопроса 15 и укажите, какие инструкции необходимы, чтобы занести значение четвертого байта (10) в CL с использованием (а) непрямой адресации, (б) адресации со смещением от начала. 17. Используя следующую таблицу и инструкции инициализации, покажите результаты инструкций MOV: VALUE_TBL DB1, 2, 3, 4, 5, б, 7, 8 ' (а) (б) (в) (г) LEA BX,VALUE_TBL MOV DI,4 MOV CL,[BX] MOV DL, [BX+3] MOV AL, [BX+DI] MOV CH,2[BX+DI] Инициализируем ВХ и DI CL = DL = AL = •CH = Глава 6. Символьные инструкции и адресация 153
Глава 7 Логические операции и управляющие структуры Цель: описать управляющие структуры программы (циклы и условные ветвления), логические операции, операции побитового сравнения, общую организацию программ. Введение Вплоть до этой главы большинство программ представляли собой набор последовательно исполнявшихся инструкций. Однако реальные задачи программирования редко можно решить с помощью таких простых (линейных) программ. Обычно программы содержат множество проверок, результаты которых определяют, выполнять или не выполнять определенные последовательности инструкций. Некоторые инструкции также могут выполняться несколько раз подряд, пока не будет выполнено некоторое условие. Например, обычно подается запрос подтверждения на завершение программы. Такие задачи требуют передачи управления определенном частям программы, необязательно следующим одна за другой. Управление может передаваться вперед - для выполнения новых наборов инструкций, или назад - для повторного выполнения уже выполненных. Инструкции, передающие управление инструкциям, не следующим непосредственно за исполняемой инструкцией, делают это, изменяя значение смещения в регистре IP. В следующей таблице приведены инструкции, рассмотренные в этой главе: Операции сравнения СМР TEST * Jnnn - все Операции перехода CALL JMP Jnnn* LOOP RETn Логические операции AND NOT OR XOR условные операции перехода, например Операции сдвига и вращения SAR/SHR SAL/SHL RCR/ROR RCL/ROL ), JNE или JL 154 Часть II. Основы программирования на языке Ассемблер
Адреса типа Short, Near и Far Ассемблер различает три типа адресов, различающихся по удалению точки перехода от текущей позиции. 1. Адрес short, ограниченный расстоянием от -128 (80Н) до 127 (7FH). 2. Адрес near, ограниченный расстоянием от -32768 (8000Н) до 32767 (7FFFH) в пределах того же сегмента. 3. Адрес far, указывающий на инструкцию на расстоянии более 32Кбайт внутри того же сегмента или в другом сегменте. Операция перехода достигает точки с адресом short при помощи однобайтной величины смещения, а типа near - при помощи одно- или двухбайтной. Адрес типа far достигается с помощью указания адреса сегмента и смещения: обычно для этого используется инструкция CALL, поскольку она позволяет выполнить переход по указанному адресу и последующее возвращение. В следующей таблице указаны допустимые расстояния переходов для инструкций JMP, Jnnn, LOOP и CALL. Запоминать эти величины нет необходимости, поскольку обычно использование этих инструкций не вызывает трудностей. Инструкция Short от-128 до 127 в том же сегменте Near от -32768 до 32767 в том же сегменте Far более 32К или в другой сегмент JMP да да да Jnnn да да (80386+) нет LOOP да нет нет CALL не применяется да да Операнд в JMP, Jnnn (инструкции условного перехода) или LOOP ссылается на метку другой инструкции. В следующем примере производится переход на метку L10, соответствующей инструкции INC: JMP L10 v L10: INC CX Метка инструкции, например, L10:, завершается символом двоеточия (:), присваивающим ей атрибут near - переход на эту метку мqжeт быть произведен из этого же сегмента. Пропуск двоеточия - одна из типичных ошибок, которая обнаруживается ассемблером. Заметьте, что метка в качестве операнда инструкции (например, JMP L10) не заканчивается двоеточием. Метку можно также указать в отдельной строке, например Глава 7. Логические операции и управляющие структуры 155
L10: INC CX В обоих случаях адрес LI0 будет указывать на первый байт инструкции INC. Инструкция JMP JMP - это наиболее часто используемая инструкция для передачи управления. Переход при помощи этой инструкции - безусловный, поскольку инструкция всегда передает управление. JMP также очищает очередь предварительно загружаемых на выполнение инструкций в процессоре, поэтому программа с большим количеством этих инструкций может много потерять в скорости выполнения. Формат инструкции JMP таков: 1 [метка:] JMP адрес short/near/far Переходы шипов Short и Near Инструкция JMP в пределах текущего сегмента может быть типа short или near (или far, если точка перехода - в процедуре с атрибутом FAR). При первом проходе по транслируемой программе ассемблер определяет длину каждой инструкции. Однако инструкция JMP может иметь длину 2, 3 или 4 байта. Инструкция JMP, выполняющая переход по адресу в пределах от -128 (80Н) до +127 (7FH) байт от текущей позиции - это переход rnima short. Для нее ассемблер выделит один байт (ЕВ) для инструкции и один байт для адреса. Операнд воспринимается как величина смещения, которую процессор прибавляет к значению в регистре IP при исполнении программы. Инструкции JMP, осуществляющей переход на расстояние более -128 или +127 байт, присваивается тип near. Для нее ассемблером генерируется другой код (Е9) и двухбайтный операнд (для 8086/80286) или четырехбайтный (для 80386+). Пока что мы не будем рассматривать переход типа far. Переходы вперед и назад Переход может осуществляться вперед или назад. Ассемблер мог уже встретить точку назначения для перехода назад в пределах -128 байт, как в случае L10: ;Адрес перехода JMP L10 ; Переход назад 156 Часть II. Основы программирования на языке Ассемблер
В этом случае ассемблер генерирует двухбайтную машинную инструкцию. При переходе вперед ассемблер еще не дошел до точки назначения инструкции JMP: JMP L2 0 /Переход вперед L20: ;Адрес перехода Поскольку в данный момент ассемблеру не известно расстояние перехода вперед, некоторые версии предполагают, что переход относится к типу near и генерируют трехбайтную инструкцию ЕВхх90, где хх - адрес перехода, а 90 - машинный код инструкции NOP. Однако если в действительности этот переход типа short, можно использовать оператор SHORT, чтобы явно указать ассемблеру на то, что требуется сгенерировать двухбайтовую инструкцию ЕВХХ, например, вот так: JMP SHORT L2 0 Программа: использование инструкции JMP Программа на рис. 7.1 демонстрирует применение JMP. Программа инициализирует АХ и ВХ значением 0, а СХ - 1. Цикл выполняет следующие действия: • добавляет 1 к АХ; • добавляет АХ к ВХ; • сдвигает на один бит влево (умножает на 2) значение в СХ. 0100 0100 0100 0103 0106 0109 0109 оюс 010Е ОНО 0112 В8 ВВ В9 05 03 D1 ЕВ 0000 0000 0001 0001 D8 Е1 F7 TITLE A10MAIN А20: A10MAIN A07JUMP (СОМ) Использование JMP для повторов .MODEL .CODE SMALL | ORG 100Н .PROCNEAR MOV MOV MOV ADD ADD SHL JMP ENDP END AX,00 BX,00 CX,01 AX,01 BX,AX CX,1 A20 A10MAIN Установить АХ и ВХ в ноль, СХ в 01 •Увеличить на 1 значение в АХ •Прибавить АХ к ВХ •Удвоить значение в СХ •Повторить Рис. 7.1. Использование команды JMP Глава 7. Логические операции и управляющие структуры "157
В конце цикла (после SHL) инструкция JMP A20 передает управление инструкции с меткой А20. В результате многократного выполнения инструкций в АХ будут последовательно храниться числа 1, 2, 3, 4,..., в ВХ - сумма чисел из АХ, то есть соответственно 1, 3, 6, 10, ..., а в СХ - степени двойки (1, 2, 4, 8, ...). Поскольку цикл является бесконечным, программа никогда не завершится. Обычно такого рода циклы применяются только в особых случаях, например, в системах непрерывного мониторинга. В программе А20 имеет смещение 9 байт от ссылающейся на нее инструкции JMP. Вы можете убедиться в этом, просмотрев машинный код, соответствующий инструкции JMP: EBF7. ЕВ - машинный код для перехода типа short, a F7 - шестнадцатеричное представление относительного адреса точки назначения (-9). Поскольку это переход назад, то операнд отрицателен. Сейчас IP содержит смещение (0112Н) следующей подлежащей исполнению инструкции. Операция JMP добавляет F7 (точнее, FFF7, поскольку IP - 16-разрядный) к IP, содержащему смещение 112Н для инструкции, следующей за JMP: Десятичное Шестнадцатеричное значение Регистр IP: 274 0112 Операнд JMP: -9 FFF7 (двоичное дополнение) Адрес перехода: 265 (1)0109 Ассемблер вычисляет адрес перехода, равный 0109Н (перенос 1 в старший разряд игнорируется), как видно из листинга программы, показывающего смещение инструкции с меткой А20. Инструкция изменяет значение смещения в IP, очищает последовательность подготовленных к выполнению инструкций и продолжает выполнение инструкций, начиная с метки А20. В качестве полезного упражнения используйте DEBUG для наблюдения за исполнением нескольких итераций программы и изменением содержимого регистров АХ, ВХ, СХ и IP. После 8 выполненных итераций АХ будет содержать 08, ВХ - 36 (24Н), а СХ - 256 (100Н). Введите Q для выхода из DEBUG. i Инструкция LOOP Как видно из программы, приведенной на рис. 7.1, инструкция JMP может использоваться для создания бесконечного цикла. Но обычно цикл должен выполняться определенное число раз или пока будет выполняться определенное условие. Инструкция LOOP, предназначенная для организации таких циклов, требует начального значения в регистре СХ. При каждом исполнении цикла LOOP отнимает от значения в СХ единицу. Если СХ достигает 0, управление проходит сквозь LOOP к следующей инструкции; если СХ не равен 0, управление передается к инструкции с адресом, указанным в операнде LOOP. Адрес, 158 Часть II. Основы программирования на языке Ассемблер
указанный в операнде, должен соответствовать переходу типа short и находиться в пределах от -128 до 127 байт от текущего значения IP. Если операнд приведет к переходу на расстояние больше допустимых пределов, ассемблер выдаст сообщение, подобное "relative jump out of range" ("относительный переход за допустимые пределы"). Формат инструкции LOOP таков: [метка:] LOOP адрес short В программе, приведенной на рис. 7.2, демонстрируется применение инструкции LOOP. Эта программа выполняет те же действия, что и предыдущая, за исключением того, что инициализирует СХ значением 8 и потому выполняет 8 итераций. Поскольку сейчас СХ используется для реализации цикла, вместо него для операции SHL используется DX. Инструкция LOOP заменяет инструкцию JMP A20, а для более быстрой обработки DSfC AX (увеличение АХ на 1) заменяет инструкцию ADD AX,01. 0100 0100 0100 0103 0106 0109 оюс оюс 010D 010F 0111 0113 0116 0118 В8 SB ВА В9 40 03 D1 Е2 В8 CD 0001 0001 0001 000А D8 Е2 F9 4С00 21 TITLE A10MAIN А20: A10MAIN A07LOOP (СОМ) Демонстрация инструкции LOOP .MODEL SMALL .CODE ORG PROC MOV MOV MOV MOV INC ADD SHL LOOP MOV INT ENDP END 100H NEAR AX,00 3X,00 DX, 01 CX, 8 AX BX,AX DX, 1 A20 AX,4C00H 21H A10MAIN Установить регистры АХ и BX в ноль, DX в 01 Проинициализировать для 8 повторений Увеличить на 1 значение АХ Прибавить АХ к ВХ Удвоить DX Уменьшить на 1 значение СХ, и повторить инструкции, если оно не нулевое Завершить работу Рис. 7.2. Использование команды Loop Как и для JMP, машинный код для LOOP содержит расстояние от конца инструкции до адреса метки А20, которое инструкция добавляет к IP при выпол-ч нении программы. В качестве полезного упражнения используйте DEBUG для наблюдения за исполнением нескольких итераций программы и изменением содержимого регистров АХ, ВХ, СХ, DX и IP. Когда СХ уменьшится до 0, АХ будет содержать 0008Н, ВХ - 0024Н, а СХ - 01 ООН. Введите Q для выхода из DEBUG. Глава 7. Логические операции и управляющие структуры ^ 59
Существует две разновидности инструкции LOOP. Обе они при каждом исполнении уменьшают СХ на 1, но инструкция LOOPE/LOOPZ (повторять, пока равно/ноль) продолжает повторяться до тех пор, пока СХ равен нулю (для LOOPE) или установлено состояние ноль (для LOOPZ). LOOPNE/LOOPNZ (повторять, пока не равно/ноль) повторяется, пока СХ не равно 0 (для LOOPNE) или не установлено состояние ноль (для LOOPNZ). Ни LOOP, ни LOOPxx не изменяют значений каких-либо флагов в регистре Flags. Однако, поскольку другие инструкции в цикле изменяют состояния флагов, программа (рис. 7.2) использует LOOP, а не LOOPxx. Регистр флагов - Flags Далее в этой главе нам потребуются дополнительные знания о регистре флагов. Этот регистр содержит 16 битов, состояние которых изменяется различными инструкциями в зависимости от результатов их выполнения. Во всех случаях флаг остается установленным вплоть до следующего принудительного изменения его состояния. Регистр Flags содержит следующие обычно используемые биты: 15 14 13 12 11 О 10 D 9 I 8 Т 7 S б Z 5 4 А 3 2 Р 1 0 С В этом параграфе содержится описание этих флагов, справа налево. Под "элементом данных" подразумеваются данные в регистре или памяти. CF (Carry Flag - флаг переноса) Содержит бит переноса (1 или 0) из старшего (первого слева) разряда элемента данных после проведения беззнаковых арифметических операций и некоторых операций сдвига и вращения. Некоторые операции, обращающиеся к диску, используют CF для индикации успеха операции (1) или неудачи (0). Инструкции JC и JNC проверяют состояние этого флага. PF (Parity flag - флаг четности) Содержит результат проверки на четность младших восьми битов элемента данных после арифметической операции. Четное число единичных битов устанавливает PF в 0, а нечетное - в 1. Не следует путать этот флаг с битом четности, описанным в гл. 1, состояние которого редко интересует программистов. Флаг PF проверяется инструкциями JP и JPO. 160 Часть II. Основы программирования на языке Ассемблер
AF (Auxiliary carry flag - флаг дополнительного переноса) Связан с арифметическими действиями над данными в форматах ASCII и BCD, описанными в гл. 13. AF устанавливается в 1, если операция над байтом приводит к переносу из бита 3 (четвертого справа) в бит 4. ZF (Zero flag - флаг нуля) Сбрасывается или устанавливается в зависимости от того, каков результат логической или арифметической операции. Учтите, что ненулевой результат устанавливает ZF в 0, а ненулевой - в 1. Однако это на первый взгляд некорректное решение правильно с точки зрения логики: 0 означает «нет» (результат не равен нулю), а 1 - «да» (равен). Некоторые инструкции, например, JE и JZ, проверяют состояние этого флага. SF (Sign flag - флаг знака) Устанавливается в соответствии со знаком (самым старшим - первым слева - битом) элемента данных после арифметической операции: положительное значение устанавливает его в 0, а отрицательное - в 1. Некоторые инструкции, например, JG и JL, проверяют состояние этого флага. TF (Trap flag - флаг пошагового режима) Если этот флаг установлен в 1, то процессор вынужден работать в пошаговом режиме, исполняя инструкции по одной за раз под управлением пользователя. Программы отладки используют TF для пошагового исполнения программ, и это - практически единственное его применение. Прерывание INT 03 может изменять состояние этого флага. IF (Interrupt flag - флаг прерывания) Блокирует внешние прерывания, если установлен в 0, и разрешает их, если установлен в 1. Инструкцией STI флаг прерывания может быть установлен в 1, а инструкцией CLI - в 0. Обычно он используется в критических ситуациях. DF (Direction flag - флаг направления) Используется строковыми операциями для определения направления передачи данных. Если DF установлен в 0, строковые операции передают данные слева направо; если установлен в 1 - справа налево. Эти операции рассмотрены в гл. 11. OF (Overflow flag - флаг переполнения) Указывает на перенос из старшего (первого слева) знакового бита элемента данных после арифметической операции. JO и JNO проверяют этот флаг. Глава 7. Логические операции и управляющие структуры *\6Л 11— 5-741
Инструкция CMP CMP используется для сравнения двух числовых величин, по крайней мере, одна из которых содержится в регистре. Ее формат таков: [метка:] СМР Регистр/память, регистр/память/непосредственное значение СМР можно использовать для сравнения символьных данных (строк), но инструкция CMPS (описанная в гл. 11) лучше подходит для работы с ними. В зависимости от результата инструкция СМР может изменять флаги AF, CF, OF, PF, SF и ZF, хотя вам не нужно проверять состояния этих флагов по отдельности. В следующем примере проверяется, равно ли содержимое регистра DX значению второго операнда инструкции СМР (в данном случае - нулю): СМР DX,00 ;DX = 0? JE L10 ;Если да, то перейти к ЫО (действия, если DX не равно 0) L10: ... ;Если да, то переходим сюда Если содержимое DX равно 0 (а сравнение производится путем вычисления их разности), СМР устанавливает ZF в 1 и, возможно, изменяет состояния других флагов (эти изменения будут зависеть от их исходного состояния). Инструкция JE (Jump if equal - перейти, если равно) проверяет только ZF. Поскольку ZF содержит 1 (указывая, что проверяемые значения равны ), JE передает управление (переходит) на метку L10. Практически инструкция СМР сравнивает первый операнд со вторым; например, позволяет выяснить, действительно ли значение первого операнда больше, равно или меньше значения второго? (СМР работает почти так же, как SUB, но без дополнительного цикла записи, требуемого для исполнения). В следующем параграфе описываются различные команды передачи управления в зависимости от результатов проверки. Инструкции условного перехода Процессор поддерживает разнообразные инструкции условного перехода, результат исполнения которых зависит от состояния различных флагов. Например, можно сравнить или просуммировать два элемента данных и в зависимости от результатов операции выполнить (или не выполнить) переход в некоторую точку программы. Формат инструкции условного перехода имеет вид: [метка : ] Jnnn Адрес типа short (для 80386+ - типа near) 162 Часть II. Основы программирования на языке Ассемблер
Ранее мы уже выяснили, что инструкция LOOP уменьшает значение в СХ на 1 и, если результат не равен 0, производится переход по указанному адресу. Можно заменить инструкцию LOOP A20 из программы, приведенной на рис. 7.2, двумя инструкциями: одна - будет уменьшать на 1 значение в СХ, а другая - выполнять условный переход: DEC СХ ;Эквивалент JNZ A20 ; LOOP A20 DEC уменьшает СХ на 1 и устанавливает/сбрасывает флаг ZF; если флаг установлен в 1 (значение в СХ равно 0), то JNZ не выполняет передачу управления, а если ZF = 0, то выполняет. (Условные переходы также очищают очередь подготовленных к исполнению инструкций в процессоре.) Хотя возможности LOOP ограничены, эта инструкция генерирует меньше машинного кода и исполняется быстрее, чем сочетание инструкций DEC и JNZ. Точно так же, как и в JMP и LOOP, машинный код операнда в JNZ содержит расстояние между концом инструкции и адресом метки А20, которое процессор прибавляет к адресу в регистре IP. Для процессоров 8086/80286 условный переход может быть только типа short, т.е. от -128 до 127 байт. Если переход производится в более удаленную точку, ассемблер выдает сообщение об ошибке, наподобие "relative jump out of range" ("относительный переход за допустимые пределы"). Процессор 80386 и последующие допускают 32-разрядные (типа near) адреса смещений, позволяя совершить переход по любому адресу в пределах доступной памяти. Если вы включите в состав программы директиву процессора .386, ассемблер автоматически сгенерирует 4-байтный машинный код для инструкции перехода вперед (пример 1); использование оператора SHORT приводит к созданию 2-байтного машинного кода (пример 2). Пример 1: Пример 2 : .386 .386 СМР ВХ,СХ СМР ВХ,СХ JE L20 JE SHORT L20 Знаковые и беззнаковые данные Различные инструкции условного перехода применяются при работе с разными типами данных. Тип данных (знаковые или беззнаковые), над которыми вы проводите арифметические операции или операцию сравнения, может определять выбор инструкции условного перехода. Беззнаковое число (логические данные) подразумевает, что все его биты - это биты данных. Типичные примеры Глава 7. Логические операции и управляющие структуры 163
беззнаковых чисел - порядковые номера элементов в списке, телефонные номера, многие соотношения. Знаковые числа (арифметические данные) подразумевают, что старший разряд (первый слева) соответствует знаку: 0 - положительному числу, 1 - отрицательному. Типичные примеры таких чисел: банковский баланс, температура - они могут быть положительными и отрицательными. В следующем примере, предположим, что CL содержит 11000110, a DL - 00010110. Инструкция CMP CLJDL сравнивает содержимое CL с содержимым DL. Если подразумевается, что регистры содержат беззнаковые числа, то содержимое CL больше, чем содержимое DL; если регистры содержат знаковые числа, число в CL меньше, чем в DL, поскольку в CL содержится отрицательное число. В любом случае для сравнения используется инструкция СМР, но для случая с беззнаковыми данными для условного перехода необходимо использовать инструкцию, например, JB, а для знаковых данных - JL. Инструкции условных переходов, применяющиеся при работе с беззнаковыми (логическими) данными Символьный Описание Проверяемые код флаги JE/JZ Перейти, если равны/ноль ZF JNE/JNZ Перейти, если не равны/не ноль ZF JA/JNBE Перейти, если больше/перейти, если не CF,ZF меньше или не равно JAE/JNB Перейти, если больше или равно/перейти, CF если не меньше JB/JNAE Перейти, если меньше/перейти, если не CF больше или не равно JBE/JNA Перейти, если меньше или равно/перейти, AF,CF если не больше Каждая из двух описанных в одной строке таблицы операций выполняет одно и то же действие; в каждом конкретном случае использования выбирайте ту из них, которая выглядит более соответствующей контексту. 164 Часть II. Основы программирования на языке Ассемблер
Инструкции условных переходов, применяющиеся при работе со знаковыми (арифметическими) данными Символьный Описание Проверяемые код флата JE/JZ Перейти, если равно/ноль ZF JNE/JNZ Перейти, если не равно/не ноль ZF JG/JNLE Перейти, если больше/перейти, если не OF,SF,ZF меньше или не равно JGE/JNL Перейти, если больше или равно/перейти, OF,SF если не меньше JL/JNGE Перейти, если больше/перейти, если не OF,SF меньше или не равно JLE/JNG Перейти, если меньше или равно/перейти, OF,SF,ZF если не больше Инструкции переходов при равенстве (JE/JZ) или неравенстве (JNE/JNZ) включены в обе таблицы, поскольку их применение допустимо при работе с данными любого типа. Специальные инструкции условных переходов Символьный Описание Проверяемые код флаги JCXZ Перейти, если СХ = 0 нет JC Перейти при переносе (аналогично JB) CF JNC Перейти при отсутствии переноса CF J0 Перейти при переполнении OF JNO Перейти при отсутствии переполнения OF JP/JPE Перейти при четности (Parity=parity even) PF JNP/JPO Перейти при нечетности (No parity=parity odd) PF JS Перейти при наличии отрицательного знака (sign) SF JNS Перейти при отсутствии отрицательного знака SF JCXZ проверяет, равно ли нулю содержимое СХ. Эта инструкция должна выполняться непосредственно за арифметической операцией или операцией сравнения. Эта инструкция может применяться, например, перед началом цикла LOOP, чтобы не выполнять инструкции из цикла ни разу, если СХ изначально Глава 7. Логические операции и управляющие структуры 165
содержит 0. JC и JNC часто используются для проверки успешности операций с диском. Нет необходимости запоминать все эти инструкции и флаги, значения которых они проверяют. Однако заметьте, что инструкции для беззнаковых данных содержат символы A (above - выше, больше), Е (Equal - равно) и В (below - ниже, меньше), а для знаковых - G (greater - больше), Е (equal - равно) и L (less - меньше). Инструкции, проверяющие флаги переноса, четности и переполнения, применяются в особых случаях. Ассемблер преобразует исходный код в объектный вне зависимости от выбранной инструкции, но помните, что инструкции, кажущиеся одинаковыми (например, JAE и JGE), проверяют различные флаги (поскольку JAE подразумевает работу с беззнаковыми данными, a JGE - со знаковыми). Процессоры 80386+ допускают условные переходы по адресам типа far. Вы можете указывать, какого типа переход выполняется в каждом случае, задавая, соответственно, Jnnn SHORT адрес или Jnnn FAR адрес. Одновременная проверка нескольких условий При условных переходах истинное логическое значение вызывает переход, а ложное - выполнение следующей инструкции. В программе может возникнуть потребность проверить несколько условий одновременно. Например, в следующем примере 1 проверяется, является ли хотя бы одно соотношение справедливым (логическое ИЛИ), а в примере 2 - являются ли все соотношения справедливыми (логическое И). Пример 2: все соотношения Пример 1: хотя бы одно из соотношений CMP AL,BL JE equal CMP AH,BL JE equal CMP AL,CL JE equal not_equal: <инструкции, выполняющиеся, если все проверенные соотношения ложны> equal: <инструкции, выполняющиеся, если какое-либо из соотношений истинно> CMP AL,BL JNE not_equal CMP AH,BL JNE not_equal CMP AL,CL JNE not_equal equal: <инструкции, выполняющиеся, если все проверенные соотношения истинны> not_equal: <инструкции, выполняющиеся, если какое-либо из соотношений ложно> 166 Часть II. Основы программирования на языке Ассемблер
В примере 1 выражения проверяются на истинность, и выполнение хотя бы одного из них приводит к переходу на инструкции, подлежащие выполнению при справедливости хотя бы одного из выражений. В примере 2 выражения проверяются на ложность, и если хотя бы одно из них не выполняется, производится переход к инструкциям, которые должны выполняться при невыполнении хотя бы одного условия. В некотором роде эти примеры противоположны, т.к. JE повсеместно заменяется на JNE, a equal - на not_equal. Вызов процедур Сегменты кода в программах, рассмотренных до сих пор, содержали единственную процедуру, выглядевшую так: имя процедуры PROC FAR имя процедуры ENDP Операнд FAR в этом случае сообщает ассемблеру и компоновщику, что определяемая процедура - это точка, с которой должно начинаться исполнение программы, а директива ENDP определяет конец этой процедуры. Однако сегмент кода может содержать сколько угодно процедур, каждая из которых выделяется собственной парой директив PROC...ENDP. Вызванная процедура (или подпрограмма) - это часть кода, выполняющая строго определенные функции (например, установку курсора в определенное место или чтение символа с клавиатуры). Разбиение программы на несколько процедур имеет определенные преимущества: • уменьшает объем кода, поскольку процедура может быть вызвана из любого места программы произвольное количество раз; • улучшает структурированность программы; • облегчает отладку и модификацию программы (ошибки легче локализовать, а структуру - изменять). Обычно в начале процедуры приводятся комментарии, описывающие назначение и структуру данной процедуры, используемые регистры и операции со стеком. Операции CALL и RETn Инструкция CALL предназначена для передачи управления вызываемой процедуре путем вызова этой процедуры. (Единственные примеры в этой книге, которые переходят к процедуре, а не вызывают ее, - это начальные фрагменты программ типа .СОМ.) Инструкция RETn, выполняющая возврат из процедуры, Глава 7. Логические операции и управляющие структуры 167
передает управление вызвавшей процедуре. RETn обычно является последней инструкцией в процедуре. Форматы этих инструкций таковы: [метка:] [метка:] CALL RET[n] имя процедуры [непосредственное значение] Ассемблер может понять из процедуры, вызывает инструкция RET переход типа near или far, и генерирует соответствующий объектный код. Однако для облегчения понимания программы можно указывать RETN - для переходов типа near, и RETF - для переходов типа far. Какой именно объектный код для инструкций CALL и RET будет сгенерирован, зависит от того, относятся они к процедуре типа near или far. Вызов и возврат из процедуры типа near Вызов процедуры из текущего сегмента принадлежит к типу near и выполняет следующие действия: • уменьшает адрес в SP на 2 (одно слово) и сохраняет содержимое IP в стеке (операция PUSH); теперь в стеке хранится точка программы, к которой должен быть произведен возврат из вызываемой процедуры; • помещает адрес начала процедуры в IP (а также очищает очередь подготовленных к исполнению инструкций в процессоре). RET (или RETn), выполняющая возврат из процедуры типа near, проделывает, фактически, действия, обратные только что описанным: • извлекает из стека (операция POP) старое значение IP (и вновь очищает очередь инструкций); • увеличивает адрес в SP на 2. Теперь CS:IP указывают на инструкцию, следующую за инструкцией CALL, вызвавшей процедуру; выполнение программы может продолжаться. Вызов и возврат из процедуры типа far CALL типа far вызывает процедуру, отмеченную как FAR, и, возможно, размещенную в другом сегменте кода. Такой вызов помещает в стек значения и из CS, и из IP, а соответствующая инструкция RET (или RETF) - извлекает их из стека. Вызовы и переходы типа far рассмотрены в гл. 22. Пример вызова и возврата из процедуры типа near На рис. 7.3 показана типичная организация программы, использующей процедуры типа near. Отметьте следующие особенности. 168 Часть II. Основы программирования на языке Ассемблер
0000 0000 0003 0006 0008 0008 0008 0008 ооос ооос ооос 000D Е8 0008 \ В8 4С00 CD 21 Е8 ОООС СЗ СЗ R R TITLE A07CALLP (EXE) Вызовы процедур A10MAIN ; A10MAIN BIO / BIO CIO CIO .MODEL SMALL .STACK 64 .DATA .CODE PROC CALL . . . MOV INT ENDP PROC CALL RET ENDP PROC RET ENDP END FAR B10 AX,4C00H 21H NEAR C10 NEAR A10MAIN ;Вызвать процедуру /Завершить работу ;Вызвать процедуру /Вернуться в точку /Вернуться в точку В10 СЮ вызова вызова Рис. 7.3. Вызов процедур • Программа делится на процедуру типа far, A10MAIN, и две процедуры типа near, B10 и СЮ. Каждая процедура имеет уникальное в пределах программы имя и собственное предложение ENDP, отмечающее ее окончание. • Директива PROC процедуры A10MAIN имеет атрибут FAR, поскольку это - процедура, с которой начинается исполнение программы. • Директивы PROC процедур В10 и СЮ имеют атрибут NEAR, указывающий, что эти процедуры размещены в текущем сегменте кода. Поскольку этот атрибут присваивается процедурам по умолчанию, во многих последующих примерах в этой книге он пропущен. • В процедуре A10MAIN инструкция CALL передает управление процедуре В10 и начинает ее исполнение. • В процедуре В10 инструкция CALL передает управление процедуре СЮ и начинает ее выполнение. • В процедуре СЮ инструкция RET вызывает возврат управления процедуре В10 - к инструкции, непосредственно следующей за CALL C10. Глава 7. Логические операции и управляющие структуры 159
• В процедуре В10 инструкция RET передает управление инструкции, непосредственно следующей за CALL B10. • Процедура A10MAIN продолжает выполняться с этой точки. • RET всегда выполняет возврат к последней точке вызова. Если бы В10 не закончилась инструкцией RET, выполнение программы прошло бы непосредственно в процедуру СЮ. Если бы и в этой процедуре не было инструкции RET, процессор продолжал бы выполнять инструкции одну за другой в порядке их расположения в памяти и с непредсказуемыми результатами. Как уже говорилось ранее, вы можете передавать управление процедуре типа near обычной инструкцией перехода. Однако в целях улучшения организации программы и облегчения ее понимания обычно используется инструкция CALL (для вызова процедуры) и соответствующая ей RET/RETN (для возврата управления основной программе). Взаимодействие исполняемой программы со стеком До сих пор в программах не требовалось часто помещать данные в стек и извлекать их из него. Соответственно в листингах определялся стек очень небольшого размера. Однако, как видно на рис. 7.3, процедура программы может с помощью CALL вызвать другую процедуру, которая в свою очередь может вызвать еще одну, и т.д. Каждая инструкция CALL будет сохранять адрес возвращения в стеке, и стек должен быть достаточно велик, чтобы вместить все эти адреса. На самом деле все проще, чем кажется на первый взгляд, и стек размером в 32 слова вполне достаточен для большинства программ из этой книги. И CALL, и PUSH сохраняют в стеке элемент данных величиной в одно слово. RET и POP извлекают его из стека и делают доступным элемент, сохраненный ранее. Все эти операции изменяют значение в SP, чтобы сделать текущим следующее слово. Поэтому RET и POP должны строго соответствовать инструкциям CALL и PUSH. Напомним, что при загрузке для выполнения программы типа .ЕХЕ загрузчик инициализирует регистры следующими значениями: • DS и ES - адресом PSP, 256-байтного (100Н) заголовка, предваряющего исполняемый код программы в памяти; • CS - адресом сегмента кода, точкой входа программы; • IP - нулем, если первая исполняемая инструкция расположена в начале сегмента кода; 170 Часть II. Основы программирования на языке Ассемблер
• SS - адресом сегмента стека; • SP - смещением вершины стека; например, для стека, определенного как .STACK 64 (64 байта или 32 слова), SP изначально содержит 64 или 40Н. Отследим исполнение простой программы, приведенной на рис. 7.3. На практике вызываемые процедуры могут содержать любое количество инструкций. Вы можете посчитать полезным оттранслировать программу, скомпоновать ее и воспользоваться отладчиком, чтобы отследить ее выполнение. При этом понаблюдайте за значениями в регистрах IP и SP и содержимым стека. Позиция в стеке, в которую в данный момент помещается новое слово, расположена в вершине стека. В данном примере загрузчик проинициализирует SP размером стека, 64 байта (40Н). Слова в памяти состоят из переставленных местами байтов; например, 0003 превращается в 0300. Программа выполняет следующие ниже действия. • CALL B10 уменьшает значение в SP на 2 (с 40Н до ЗЕН). После этого данная инструкция помещает значение IP (в данный момент равное 0003 - адресу следующей инструкции) в вершину стека по адресу ЗЕН. Процессор использует адрес, получаемый из пары CS:IP, для передачи управления В10, как показано ниже: CALL B10 (сохраняем 0003): 003Е 003С 003А 0300 хххх хххх <- -SP = 003Е • В процедуре В10 инструкция CALL СЮ уменьшает SP на 2 (до ЗСН). Затем она сохраняет значение из IP (равное 000В) в вершине стека со смещением ЗСН. Процессор использует CS:IP для передачи управления СЮ: CALL СЮ (сохраняем 000В) : 003Е 003С 003А 0300 овоо хххх <- -SP = 003С Для возврата из СЮ инструкция RET извлекает смещение (000В) из вершины стека по адресу ЗСН, помещает его в IP и увеличивает значение в SP на 2 (до ЗЕН). Смещение в IP вызывает автоматическое возвращение к инструкции со смещением 000BH, находящейся в процедуре В10: RET (извлекаем 000В): 003Е оозс 003А 0300 ОВОО хххх <--SP = 003Е RET в конце процедуры В10 заносит адрес (0003) из вершины стека по адресу 003ЕН в IP и увеличивает значение в SP на 2 (до 0040Н). Смещение в IP Глава 7. Логические операции и управляющие структуры 171
вызывает автоматический переход к инструкции со смещением 0003Н, где выполнение программы завершается. <--SP = 0040 RET (извлекаем 0003): 003Е I 0300 ' 003С 0В00 003 А хххх Если вы используете отладчик для просмотра стека, вы заметите, что данные, оставшиеся от программы, исполнявшейся ранее, остались в неприкосновенности. Переполнение стека Рассмотрим программу, определяющую стек размером в 12 слов. При исполнении этой программы последовательности инструкций CALL и PUSH заполняют стек, начиная со старших адресов в сторону младших. В момент, когда стек полностью использован, SP = 0. Без предварительных RET или POP следующая инструкция CALL или PUSH вновь "уменьшит" значение в SP от 0 до FFFEH и сохранит новый элемент данных за пределами выделенного стека, с непредсказуемыми результатами. (Эта довольно часто встречающаяся ошибка называется переполнением стека - "stack overflow". - Прим.ред.) Передача параметров Весьма часто при вызове процедуры ей передаются определенные параметры (или аргументы), используемые процедурой при выполнении. Программа может передавать параметры по значению (передается сам элемент данных) или по ссылке (передается адрес элемента данных). Кроме того, параметры могут передаваться через регистры или стек. Передача параметров по значению Нижеприведенные примеры 1 и 2 демонстрируют передачу параметров по значению. Пример 1. Передача значений в регистрах MOV AX,MULTICAND MOV BX,MULTIPLER CALL M30MULT Пример 2. Передача значений через стек PUSH MULTICAND PUSH MULTIPLER CALL M30MULT M30MULT PROC NEAR MUL BX M30MULT PROC NEAR PUSH BP 172 Часть II. Основы программирования на языке Ассемблер
RET MOV BP,SP M30MULT ENDP MOV AX, [BP+6] MUL WORD PTR [BP+4] POP BP RET 4 M30MULT ENDP Программа передает процедуре множимое и множитель в качестве параметров, а процедура просто перемножает их. Эта версия MUL предполагает, что множимое находится в АХ, а множитель - в операнде, и помещает произведение в пару DX:AX. Рассмотрим пример 2 подробнее. В момент входа в процедуру M30MULT регистр SP указывает на адрес возврата в стеке. Дня доступа к стеку необходима непрямая адресация; SP не может ее реализовать, но ВР может. Процедура работает следующим образом. • Помещает значение из ВР в стек для сохранения. Теперь стек выглядит так: Множимое Множитель Адрес возврата Значение из ВР [ВР+6] [ВР+4] [ВР+2] <—SP и ВР • Копирует смещение из SP в ВР. • Использует ВР для непрямой адресации при получении параметров, находящихся в стеке по адресам [ВР+6] и [ВР+4]. • Извлекает значение ВР из стека (одновременно увеличивая на 2 значение в SP для получения доступа к адресу возврата). • Выполняет инструкцию RET 4 следующим образом. 1. В IP загружается адрес возврата, а значение в SP увеличивается на 2 (теперь оно указывает на множитель в стеке). 2. Непосредственное значение RET (т.е. 4) добавляется к содержимому SP, что приводит к практическому "стиранию" из стека двух переданных параметров. Заметьте, что это действие необходимо потому, что процедура не извлекала из стека параметры явным образом (инструкцией POP). Передача параметров по ссылке Примеры 3 и 4, приведенные ниже, иллюстрируют передачу параметров по ссылке - метод, используемый для вызова подпрограмм в языках высокого уровня. Передаваемые параметры представляют собой адреса множителя и множимого. Глава 7. Логические операции и управляющие структуры 173
Пример 3. Адреса в регистрах LEA BX,MULTICAND LEA SI,MULTIPLER CALL M30MULT M30MULT PROC NEAR MOV AX, [BX] MUL WORD PTR [SI] RET M30MULT ENDP Пример 4. Адреса в стеке .386 ;требуется для двух ; последующих инструкций PUSH PUSH OFFSET MULTICAND PUSH OFFSET MULTIPLER CALL M30MULT M30MULT PROC NEAR PUSH BP MOV BP,SP MOV BX, [BP+6] MOV DI,[BP+4] MOV AX, [BX] MUL WORD PTR [DI] POP BP RET 4 M30MULT ENDP Воспользуйтесь отладчиком и отследите выполнение этих примеров. Выбор метода передачи параметров зависит от обстоятельств и соглашений, принятых при написании программы. Например, передача параметров по значению имеет смысл только для данных небольшого объема; передача массива в качестве параметра потребует использования ссылки. Кроме того, языки высокого уровня передают параметры по ссылке. Обычно при вызове процедуры значения используемых регистров помещаются в стек, а после возврата из нее - извлекаются из стека. Если программа использует стек для передачи параметров, вызываемая процедура сохраняет значения регистров после помещения значения из SP в ВР: PUSH ВР MOV BP,SP PUSHF PUSHA ;сохраняем ВР ;сохраняем флаги ;сохраняем все регистры РОРА POPF POP ВР ;восстанавливаем все регистры ;восстанавливаем флаги ;восстанавливаем ВР В гл. 22 описана передача параметров отдельно оттранслированным подпрограммам. 174 Часть II. Основы программирования на языке Ассемблер
Булевы (логические) операции Булева алгебра важна при проектировании цифровой электроники, но интенсивно используется и в программировании. Инструкции булевой логики - AND, OR, NOT, XOR и TEST. Любая из них может использоваться для побитовых операций. Формат этих операций (кроме NOT, см. ниже) таков: [метка:] операция Регистр/память, регистр/память/непосредственное значение Первый операнд ссылается на байт, слово или двойное слово в регистре или памяти - изменяться будет значение только в этом операнде. Второй операнд ссылается на регистр, память или непосредственное значение, но операция типа память-память некорректна. Для вычисления результата операция использует биты двух операндов и устанавливает флаги CF, OF, PF, SF и ZF соответственно вычисленной величине (флаг AF не определен). Заметьте, что операция AND (логическое И) с нулевыми битами всегда дает в результате 0, a OR (логическое ИЛИ) с единичными битами всегда дает 1. Инструкция AND При использовании операции AND, если оба сравниваемых бита равны 1, результат операции равен 1; все остальные сочетания дают в результате 0: Операнд 1: 0101 AND операнд 2: ООН Результат в операнде 1: 0001 Дня последующих независимых примеров предположим, что BL содержит ООП 1010, аСН- 10100011: 1. AND BL,0FH ;Устанавливает BL в 0000 1010 2. AND BL,00H ;Устанавливает BL в 0000 0000 3. AND BL,CH ;Устанавливает BL в 0010 0010 Пример 2 показывает один из способов обнуления регистра. Инструкция OR При использовании операции OR, если хотя бы один из сравниваемых битов равен 1, результат равен 1; если оба бита равны 0, то результат равен 0: Операнд 1: 0101 OR операнд 2 : ООН Результат в операнде 1: 0111 Для последующих независимых примеров предположим, что BL содержит ООП 1010, аСН- 10100011: Глава 7. Логические операции и управляющие структуры 175
1. OR CH,BL /Устанавливает СН в 1011 1011 2. OR CH,CH /Устанавливает флаги SF и ZF Хотя, возможно, использовать СМР проще, можно использовать OR и так: 1. OR DX,DX /Проверить DX JZ адрес /Если DX = 0, выполнить переход 2. OR DX,DX /Проверить DX JS адрес /перейти, если отрицательное Инструкция XOR Эта инструкция (соответствующая логической операции неэквивалентности), дает в результате 1, если проверяемые биты различны по значению, и 0, если равны. Операнд 1: XOR операнд 2: Результат в операнде 1: 0101 ООН ОНО Для последующих независимых примеров предположим, что BL содержит ООН 1010: 1. XOR BL,0FFH 2. XOR BL,BL /Устанавливает BL в 1100 0101 /Устанавливает BL в 0000 0000 В примере 1 показан один из способов инверсии двоичных разрядов, а в примере 2 - еще один способ обнуления регистра. Инструкция TEST Эта инструкция устанавливает флаги так же, как и инструкция AND, но не изменяет биты первого операнда. Если какие-либо соответствующие биты обоих операндов одновременно равны 1, TEST очищает (устанавливает в 0) флаг ZF. Вот несколько примеров: TEST CX,0FFH JZ адрес TEST BL,00000001B JNZ адрес TEST CL,11110000B JNZ адрес /CX равно 0? /Перейти, если CX = 0 /Содержит ли BL нечетное число? /Перейти, если в BL - нечетное число /Хотя бы один из четырех старших ; битов CL равен 1? /Перейти, если да Инструкция NOT Эта инструкция просто инвертирует все биты в байте, слове или двойном слове в регистре или памяти - 0 становится 1, а 1 - 0. Формат этой инструкции таков: [метка: ] NOT регистр/память 176 Часть II. Основы программирования на языке Ассемблер
Например, если BL содержит ООП 1010, то инструкция NOT BL изменит это значение на 1100 0101 (аналогично ранее приведенной операции XOR BL,0FFH). Но флаги при этом не изменяются. Инструкция NOT отличается от инструкции NEG следующим: NEG вычисляет двоичное дополнение к числу, то есть изменяет двоичное число А на -А, инвертируя биты и добавляя 1. Обычно NOT используется при работе с беззнаковыми данными, a NEG - со знаковыми. Программа: замена строчных букв заглавными и наоборот Бывают случаи, когда необходимо выполнить преобразование строчных букв в соответствующие заглавные и наоборот. Например, это может потребоваться при работе с файлом данных, в котором все буквы хранятся в заглавном виде. Или, например, программа может позволять пользователю вводить строку из заглавных или строчных букв (например, "YES" или "yes"), а затем для выполнения проверки выполнять преобразование в заглавные. Заглавные буквы от А до Z имеют коды ASCII от 41Н до 5АН, а строчные - от 61Н до 7АН. Единственное различие между ними - 5-й бит, который равен 0 для заглавных букв и 1 - для строчных, как показано ниже: Заглавные буквы Буква А: 01000001 Буква Z: 01011010 Биты: 76543210 Строчные буквы Буква а: 01100001 Буква z: 01111010 Биты: 76543210 Программа, приведенная на рис. 7.4, преобразует содержимое элемента данных CONAME из заглавных букв в строчные, начиная с адреса CONAME+1. Программа инициализирует ВХ адресом CONAME+1 и использует адрес для помещения каждого символа поочередно в АН. Если его ASCII-код - от 41Н до 5АН, то операция XOR устанавливает бит 5 в 0: XOR АН,00100000В Все символы, кроме заглавных букв, остаются неизменными. После этого программа помещает измененный символ обратно в CONAME и увеличивает значение в ВХ на 1, получая доступ к следующему символу. Программа выполняет 15 возвратов к началу цикла, по одному для каждого символа из CONAME. В данном случае ВХ используется в качестве индексного регистра для адресации элементов памяти. Дня этой же цели можно использовать регистры SI и DI. В конце программа выводит измененное содержимое CONAME. Глава 7. Логические операции и управляющие структуры 12 — 5-741 177
TITLE BEGIN: CONAME A10MAIN A20: A30: A10MAIN A07CASE (COM) .MODEL SMALL ORG JMP DB PROC LEA MOV MOV CMP JB CMP JA XOR MOV INC LOOP MOV LEA INT MOV INT ENDP END .CODE 100H A10MAIN 'LASER-12 NEAR Замена заглавных букв на строчные SYSTEMS * , BX,CONAME+l CX,15 AH, [BX] AH,4lH A30 AH,5AH A30 , AH,00100000B [BX],AH BX A20 AH,09H DX, CONAME 21H AX,4C00H 21H BEGIN *$• 1-й изменяемый символ Общее число изменяемых символов Символ из CONAME Является ли он заглавной буквой? Да, преобразовываем Восстанавливаем в CONAME •Переходим к следующему символу •Повторяем 15 раз • Выполнили, ; теперь выводим ; CONAME •Завершаем работу Рис. 7.4. Замена заглавных букв на строчные Сдвиг битов Инструкции сдвига, являющиеся важным компонентом логических возможностей компьютера, могут: • ссылаться на регистр или адрес в памяти; • сдвигать биты влево или вправо; • сдвигать до 8 бит в байте, до 16 бит в слове или до 32 бит - в двойном слове; • сдвигать биты логически (без учета знака) или арифметически (с учетом знака). 178 Часть II. Основы программирования на языке Ассемблер
SHR/SAR/SHRD: сдвиг битов вправо SHR (Shift Logical Right - логический сдвиг вправо), SAR (Shift Arithmetic Right) и SHRD (Shift Right Doubleword - сдвиг двойного слова вправо) - это инструкции, сдвигающие вправо биты, содержащиеся в указанном регистре или по указанному адресу в памяти. Формат инструкций SHR и SAR следующий: [метка:] SHR/SAR регистр/память, CL/непосредственное значение Второй опеанд определяет величину сдвига и задается в регистре CL или в виде непосредственного значения . Для процессоров 8086/8088 непосредственное значение сдвига может быть только 1; последующие процессоры разрешают использовать непосредственные значения вплоть до 31. Очередной бит, сдвигаемый вправо за пределы регистра, заносится во флаг переноса (CF). SHR выполняет сдвиг для логических (беззнаковых) данных, а SAR-для арифметических (со знаком): SHR: SAR: 0 >>> S » \L Инструкция SHRD рассмотрена ниже. Приведенная далее последовательность инструкций демонстрирует использование SHR для сдвига беззнаковых данных: Инструкция Комментарий Двоичное Десятичное CF MOV ВН, 10110111В ;Инициализируем ВН 10110111 183 SHR ВН,01 ;Сдвигаем ВН на 1 вправо 01011011 91 1 MOV CL,02 /Установим величину сдвига SHR BH,CL ;Сдвигаем ВН еще на 2 вправо 00010110 22 1 SHR ВН,02 ;Сдвигаем ВН еще на 2 вправо 00000101 5 1 Первая инструкция SHR сдвигает содержимое ВН на один бит вправо. Сдвинутый единичный бит теперь хранится во флаге CF, а слева в ВН ввод'ится бит 0. Вторая инструкция SHR сдвигает ВН еще на 2 бита. Флаг CF в результате будет хранить единицу, а слева в ВН последовательно вводятся 2 нуля. Третья инструкция SHR сдвигает ВН еще на 2 бита. SAR имеет одно важное отличие от SHR: SAR использует бит знака для заполнения крайних слева освобождающихся битов. Поэтому при ее использовании положительные и отрицательные числа сохраняют свой знак. Это видно на следующем примере, демонстрирующем применение SAR при работе с числами со знаком: Глава 7. Логические операции и управляющие структуры 179
Инструкция Комментарий Двоичное Десятичное CF MOV ВН, 10110111В ;Инициализируем ВН 10110111 -73 SAR ВН,01 /Сдвигаем ВН на 1 вправо 11011011 -37 MOV CL,02 ;Установим величину сдвига SAR BH,CL /Сдвигаем ВН еще на 2 вправо 11110110 -10 SAR ВН,02 /Сдвигаем ВН еще на 2 вправо 11111101 -3 Сдвиг вправо особенно полезен в качестве деления значений пополам и выполняется значительно быстрее, чем, собственно, операция деления. В только что приведенных примерах сдвиг на один бит вправо производит, фактически, деление на 2 с остатком (остаток попадает во флажок CF), а сдвиг на 2 бита - деление на 4. Деление пополам нечетных чисел, например, 5 и 7, даст результат 2 и 3, соответственно, и установит флаг CF в 1. (При делении пополам отрицательных нечетных чисел, например, -5 и -7, мы получим, соответственно, -3 и -4. - Прим.ред.) После операции сдвига можно использовать инструкцию JC для проверки состояния флага CF. Сдвиг вправо в 32-разрядных регистрах Рассмотрим 32-битовое значение, в котором старшие 16 бит расположены в регистре DX, а младшие 16 - в АХ, в виде DX.AX. В следующем примере данные из этой пары перемещаются в регистр ЕСХ, а операция сдвига вправо делит результат на 2: MOV CX,DX SHL ЕСХДб MOV СХ,АХ SHR ЕСХ,01 DX в младшие разряды ЕСХ Сдвиг в старшие разряды ЕСХ АХ в младшие разряды ЕСХ Делим ЕСХ на 2 с остатком В процессоре 80386 и последующих для сдвига вправо 16- и 32-разрядных значений может использоваться инструкция SHRD. Ее формат таков: [метка:] SHRD регистр/память, регистр, CL/'непосредственное значение Первый операнд принимает сдвинутые биты. Второй операнд по размеру равен первому и содержит биты, подлежащие сдвигу. Третий операнд (регистр CL или непосредственное значение) содержит величину сдвига. Вот несколько примеров: SHRD CX,DX,4 SHRD EAX,EDX,CL ;Сдвиг вправо на 4 бита из DX в СХ ;Сдвиг из EDX в ЕАХ на количество ; бит, указанное в CL 180 Часть II. Основы программирования на языке Ассемблер
SHUSAL/SHLD: сдвиг битов влево SHL (Shift Logical Left - логический сдвиг влево), SAL (Shift Arithmetic Left) и SHLD (Shift Left Doubleword - сдвиг двойного слова влево) - это инструкции, сдвигающие биты, содержащиеся в указанном регистре или по указанному адресу в памяти, влево. Формат инструкций SHL и SAL следующий: [метка: ] SHL/SAL регистр/память, CL/непосредственное значение Второй операнд задает величину сдвига, являющуюся непосредственным значением или хранящуюся в регистре CL. Для процессоров 8086/8088 непосредственное значение сдвига может быть только 1; последующие процессоры разрешают использовать непосредственные значения вплоть до 31. Очередной бит, сдвигаемый влево за пределы регистра, заносится во флаг переноса (CF). SHL и SAL работают как с арифметическими (со знаком), так и с логическими (беззнаковыми) величинами, и в их работе нет различий: SHL и SAL: С <<< <<< о Инструкция SHLD рассмотрена ниже. Приведенная далее последовательность инструкций демонстрирует использование SHL для сдвига беззнаковых данных: Инструкция MOV ВН,00000101В SHL ВН,01 MOV CL,02 SHL BH,CL SHL BH,02 Комментарии ;Инициализируем ВН ;Сдвигаем ВН на 1 влево ;Установим величину сдвига ;Сдвигаем ВН еще на 2 влево 00101000 ;Сдвигаем ВН еще на 2 влево 10100000 Двоичное Десятичное 00000101 5 CF 00001010 10 40 160 Первая инструкция SHL сдвигает содержимое ВН на один бит влево. Выдвинутый бит теперь хранится во флаге CF, а справа в ВН вводится бит 0. Вторая инструкция SHL сдвигает ВН еще на 2 бита. Флаг CF в результате будет содержать нуль, а справа в ВН последовательно вводятся 2 нуля. Третья инструкция SHL сдвигает ВН еще на 2 бита. Сдвиг влево всегда заполняет освобождающиеся справа биты нулями. Как следствие, инструкции SHL и SAL идентичны, и SAL могла бы применяться в предыдущем примере с тем же результатом. Сдвиги влево особенно полезны для умножения на числа, являющиеся степенями двойки. Эти операции выполняются значительно быстрее, чем, собственно, инструкция умножения. Например, сдвиг на один бит влево эквивалентен умножению на 2, а сдвиг на 3 бита - умножению на 8. Глава 7. Логические операции и управляющие структуры 181
После операции сдвига можно использовать инструкцию JC для проверки состояния флага CF. Сдвиг влево в 32-разрядных регистрах Рассмотрим 32-битовое значение, в котором старшие 16 бит расположены в регистре DX, а младшие 16 - в АХ, в виде DX:AX. В следующем примере данные из этой пары перемещаются в регистр ЕСХ, а операция сдвига влево удваивает результат: MOV CX,DX ;DX в младшие разряды ЕСХ SHL ЕСХ,16 ;Сдвиг в старшие разряды ЕСХ MOV СХ,АХ ;АХ в младшие разряды ЕСХ SHL ЕСХ,01 ;Умножаем ЕСХ на 2 В процессоре 80386 и последующих для сдвига влево 16- и 32-разрядных значений может использоваться инструкция SHLD. Ее формат таков: [метка:] SHLD регистр/память, регистр, CL/непосредственное значение Первый операнд принимает сдвинутые биты. Второй операнд по размеру равен первому и содержит биты, подлежащие сдвигу. Третий операнд (регистр CL или непосредственное значение) содержит величину сдвига. Вот несколько примеров: SHLD CX,DX,4 ;Сдвиг влево на 4 бита из DX в СХ SHLD EAX,EDX,CL /Сдвиг из EDX в ЕАХ на количество ; бит, указанное в CL Циклический сдвиг битов Инструкции циклического сдвига, относящиеся к группе логических команд компьютера, могут выполнять следующие действия: • ссылаться на регистр или адрес в памяти; • циклически сдвигать биты влево или вправо; бит, выходящий за пределы регистра, помещается в первый с противоположной стороны разряд и одновременно копируется во флаг переноса (CF); • циклически сдвигать до 8 бит в байте, 16 бит - в слове или 32 бит - в двойном слове; • циклически сдвигать биты логически (без учета знака) или арифметически (с учетом знака). Формат этих инструкций таков: 182 Часть II. Основы программирования на языке Ассемблер
[метка:] вращение регистр /память, CL/непосредственное значение Второй операнд содержит величину циклического сдвига, представленную константой (непосредственным значением) или ссылкой на регистр CL. В процессорах 8086/8088 непосредственное значение циклического сдвига могло быть только 1; большее значение должно было храниться в регистре CL. Последующие процессоры позволяли использовать любую непосредственную величину вплоть до 31. ROR/RCR: циклический сдвиг битов вправо Инструкции ROR и RCR циклически сдвигают биты в указанном регистре или элементе данных вправо. Каждый бит, выдвигаемый вправо из младшего разряда, заносится во флаг CF. ROR работает с логическими (беззнаковыми), а RCR-c арифметическими (знаковыми) величинами. ROR: RCR: ► Приведенная ниже последовательность инструкций демонстрирует использование ROR: Инструкция MOV BL,10110111В ROR BL/01 MOV CL/03 ROR BL/CL ROR BL/03 Комментарий /•Инициализируем BL ;Циклически сдвигаем BL на 1 бит вправо ,-Установим величину сдвига /•Циклически сдвигаем BL еще на 3 бита вправо /•Циклически сдвигаем BL еще на 3 вправо Двоичное 10110111 11011011 01111011 01101111 CF 1 0 0 Первая инструкция ROR перемещает первый справа бит в крайнюю левую позицию и одновременно заносит его в CF. Вторая и третья инструкции ROR перемещают по очереди 3 младших бита в первую справа позицию и одновременно в CF. Следующая инструкция RCR сдвигает биты в ЕСХ: RCR ЕСХ, 6 /-Перемещаем циклически направо на 6 бит ; содержимое двойного слова Глава 7. Логические операции и управляющие структуры 183
RCR отличается от ROR: каждый бит, который RCR сдвигает направо за пределы регистра, заносится в CF, а предыдущий бит из CF заносится в освободившуюся позицию слева. ROL/RCL: циклический сдвиг битов влево Инструкции ROL и RCL циклически сдвигают биты в указанном регистре или элементе данных влево. Каждый бит, выдвигаемый из старшего разряда, заносится также во флаг CF. ROL работает с логическими (беззнаковыми), а RCL-c арифметическими (знаковыми) величинами. ROL: С \А N RCL: с ы— к Приведенная ниже последовательность инструкций демонстрирует использование ROL: Инструкция Комментарий Двоичное CF MOV BL,10110111B ROL BL,01 MOV CL,03 ROL BL,CL ROL BLf03 ;Инициализируем BL 10110111 /Циклически сдвигаем BL на 1 01101111 бит влево ;Установим величину сдвига ;Циклически сдвигаем BL еще на 01111011 3 бита влево /Циклически сдвигаем BL еще на 11011011 3 бита влево Первая инструкция ROL перемещает первый слева бит в крайнюю правую позицию и одновременно заносит его в CF. Вторая и третья инструкции ROL перемещают по очереди 3 старших бита в первую справа позицию и одновременно в CF. Следующая инструкция RCL сдвигает биты в ЕВХ: RCL ЕВХ,4 ;Перемещаем циклически налево на 4 бита ; содержимое двойного слова RCL отличается от ROL в следующем: каждый бит, который RCL сдвигает налево за пределы регистра, заносится в CF, а предыдущий бит из CF заносится в освободившуюся позицию справа. 184 Часть II. Основы программирования на языке Ассемблер
Сдвиги и циклические сдвиги двойных слов Можно использовать инструкции циклических и обычных сдвигов для умножения и деления чисел в формате двойного слова на степени двойки. Рассмотрим 32-разрядное число, в котором старшие 16 разрядов хранятся в DX, а младшие 16 - в АХ, в виде DX:AX. Инструкции для "умножения" этого числа на 2 таковы: SHL АХ,1 ;Умножаем число RCL DX,1 ; в DX:AX на 2 Инструкция SHL сдвигает все биты в АХ налево, а самый старший бит помещает во флаг CF. RCL помещает этот бит в младший разряд DX, предварительно сдвинув его содержимое влево. Для умножения на 4 просто повторите эту пару инструкций дважды. Для "деления" числа, размещенного в паре регистров DX:AX, на 2 используются инструкции SAR DX, 1 ;Делим число RCR АХ,1 в DX:AX на 2 Инструкция SAR сдвигает все биты в DX направо, а самый младший бит помещает во флаг CF. RCR циклически сдвигает АХ направо, помещая бит из CF на освободившееся место в старшем разряде. Для реализации деления на 4 повторите эту пару инструкций дважды. Проектирование программы Далее описана рекомендуемая последовательность написания программы на ассемблере. 1. Четко сформулируйте задачу, которую должна решать создаваемая программа. 2. Набросайте общую схему алгоритма программы и спланируйте общие логические механизмы. Например, если решается задача, связанная с перемещениями многобайтных элементов данных, начните с определения полей - источников данных. Затем спланируйте основные блоки инструкций: инициализацию, использование инструкций условного перехода и цикла. Далее приведен пример обобщенного проекта алгоритма программы: • инициализировать сегментные регистры; • выполнить подпрограмму перемещения элемента данных; • выполнить переход по циклу с инструкцией LOOP; • завершить работу. Глава 7. Логические операции и управляющие структуры 185
Второй пункт (подпрограмма перемещения элемента данных) может быть спланирован более подробно, например: • инициализировать регистры для счета; • переместить один символ из исходного поля; • увеличить значение смещения для адресации следующего символа; • уменьшить на 1 значение в счетчике элементов; Третий пункт (переход по циклу с инструкцией LOOP) может быть спланирован аналогичным образом. 1. Разделите программу на логически связанные блоки, четко следующие один за другим. Процедуры, имеющие длину до 25 строк (полностью помещающиеся на экране) отлаживать легче, чем процедуры большего размера. 2. Используйте другие программы в качестве примеров. Попытки запомнить весь технический материал и программировать "из головы" обычно приводят к множеству ошибок. 3. Используйте комментарии для пояснения назначения важных или редко применяемых инструкций. Полезно также приводить комментарии, указывающие в начале процедуры ее назначение и используемые регистры, а также входные и выходные данные. 4. Для облегчения ввода программы используйте шаблоны, копирование фрагментов или модификацию уже существующих программ. 5. Проверяйте корректность написанного кода, даже если значительная его часть - просто пустые процедуры. 6. Используйте численные значения, являющиеся корректными, но близкими по величине к пределу, а также ноль и отрицательные значения, чтобы убедиться, что программа корректно выполняется для этих значений. После этого попробуйте ввести заведомо некорректные данные и посмотрите, как поведет себя программа. Программы, встречающиеся далее в этой книге, часто используют инструкции JMP, LOOP, CALL, условные переходы и вызываемые процедуры. Рассмотрев основы языка ассемблера, мы можем переходить к углубленному его изучению и реальным примерам. Ключевые моменты • Адрес типа short достигается при помощи смещения и ограничен интервалом от 127 до -128 байт. Адрес типа near также ограничен, но большим интервалом - от -32768 до 32767 байт, причем, в том же сегменте. Адрес типа far 186 Часть II. Основы программирования на языке Ассемблер
указывает на точку в другом сегменте и достигается изменением адреса сегмента и смещения. • Метка инструкции, например, "L10:", заканчивается двоеточием, указывающим на то, что это - метка типа near. • Метки адресов условных переходов и инструкций LOOP должны быть типа short. Операнд генерирует 1 байт машинного кода: от 01Н до 7FH для смещений от +1 до +127 и от FFH до 80Н для смещений от -1 до -128. • При использовании LOOP инициализируйте СХ положительным ненулевым значением, поскольку LOOP уменьшает на 1 это значение и проверяет его на отличие от нуля при каждом проходе. • Если инструкция устанавливает какой-то флаг или очищает его, он сохраняет это состояние до следующего изменения. • Выбирайте подходящую по контексту инструкцию условного перехода - одни из них работают со знаковыми, другие - с беззнаковыми данными. • Используйте CALL для вызова процедур и включайте в текст процедуры инструкцию RET/RETN для возврата. Вызванная процедура может вызывать другие процедуры, и, если все сделано правильно, инструкции RET/RETN обеспечат правильный возврат. • Используйте сдвиг влево для удваивания числа и сдвиг вправо - для деления его пополам. Используйте правильные инструкции сдвига при работе со знаковыми и беззнаковыми данными. Вопросы и упражнения 1. Объясните свойства адреса типа (a) short, (б) near и (в) far. 2. (а) Каково максимальное расстояние перехода для JMP (near), LOOP и инструкций условного перехода? (б) Чем именно в машинном коде обусловлены эти пределы? 3. Инструкция JMP начинается с ячейки со смещением 02D4H. Определите смещение точки перехода, основываясь на объектном коде операнда данной инструкции: (а) 2АН, (б) 6ЕН, (в) В8Н. 4. Объясните, сколько раз выполняется каждая инструкция LOOP: (a) MOV СХ,1 (б) L5: MOV CX,10 L5 : LOOP L5 LOOP L5 Глава 7. Логические операции и управляющие структуры 187
(в) MOV СХ,0 (г) MOV СХ,10 L5: L5: INC CX LOOP L5 LOOP L5 5. Напишите программу, вычисляющую числа Фибоначчи: 1, 1, 2, 3, 5, 8, 13 ... (Кроме первых двух единиц, каждое последующее число есть сумма двух предыдущих чисел.) Используйте инструкцию LOOP и выполните 12 итераций. Оттранслируйте программу, скомпонуйте и проследите в DEBUG ее выполнение. 6. Напишите программу, вычисляющую сумму значений всех байт в BYTETBL и сохраняющую ее в BYTE-TOTAL. (Подсказка: используйте непрямую адресацию, одну инструкцию ADD, INC BX и LOOP). BYTE_TBL DB 5,6,4,9,7 BYTE-TOTAL DB 0 7. CHAR_STRING содержит заглавные буквы, которые программа должна преобразовать в строчные. Загрузите последовательно каждый символ в регистр, добавьте к нему 20Н и поместите его назад в строку. Используйте инструкцию LOOP. (Подсказка: используйте непрямую адресацию и увеличивайте значение в ВХ для каждого символа.) CHAR_STRING DB 'ABCDEFGHIJ' 8. Полагая, что СХ и DX содержат беззнаковые данные, а АХ и ВХ - знаковые, задайте СМР (если необходимо) и инструкции условного перехода для выяснения: (а) АХ равен или меньше ВХ? (б) СХ равен или меньше DX? (в) АХ больше DX? (г) СХ больше DX? (д) DX равен 0? (е) Есть ли переполнение? 9. Какие флаги будут затронуты и каково будет их состояние: (а) при работе в пошаговом режиме, (б) при перемещении строковых данных справа налево, (в) при отрицательном результате, (г) при нулевом результате, (д) при переполнении. 10. Введите и оттестируйте программу, содержащую приведенные ниже последовательности инструкций. Определите значение в регистре результата и значения флагов OF, ZF, SF и CF. (a) MOV AL,FFH (б) MOV BL,24H ADD AL,1 SUB BL,BL (в) MOV CL,10101010B (r) MOV DL,11001100B ADD CL,01010101B ADD DL,01110011B 188 Часть II. Основы программирования на языке Ассемблер
11. Обратитесь к рис. 7.3 и объясните ход выполнения программы, если в процедуре В10 не будет инструкции RET. 12. Объясните различия между процедурами, определенными директивой PROC с операндами NEAR и FAR. 13. Укажите три способа входа в процедуру в исполняемой программе. 14. Оттранслируйте и скомпонуйте программу, приведенную на рис. 7.3, сначала с моделью Small, затем с моделью Medium. Сравните получившийся объектный код и карты связей. 15. В программе процедура D10 вызывает процедуру ЕЮ, ЕЮ вызывает F10, а F10 вызывает G10. Сколько адресов возврата содержит стек после этих вызовов? 16. Полагая, что DL содержит 01111001, а элемент данных, названный BOOLAMT, содержит 11100011, определите результат в DL каждой отдельно взятой операции: (a) AND DL,BOOL_AMT, (б) OR DL,BOOL_AMT, (в) XOR DL,BOOL_AMT, (г) AND DL,00000000B, (д) XOR DL,l 111111 IB. 17. Модифицируйте программу, приведенную на рис. 7.4, следующим образом: определите содержимое CONAME в виде набора строчных букв и перепишите программу так, чтобы она преобразовала их в заглавные. 18. Предположим, что DX содержит двоичное число 10111001 10111001. Определите, что будет содержать DX после выполнения следующих отдельно взятых инструкций: (a) SHL DL,1, (б) SHL DX,2, (в) SHR DX,1, (г) SAR DX,2, (д) SAL DH,3, (e) ROR DX,3, (ж) ROR DL,3. 19. Используйте операции перемещения, сдвига и сложения для инициализации DX значением 24 и умножения его на 10. 20. Пример в конце параграфа "Циклический сдвиг битов" умножает содержимое пары DX:AX на 2. Напишите аналогичную программу для (а) умножения на 4, (б) деления на 4, (в) умножения 48 бит в DX:AX:BX на 2. 21. Переместите содержимое пары DX:CX в ЕВХ и используйте сдвиг для умножения ЕВХ на 4. Глава 7. Логические операции и управляющие структуры 189
Часть III Работа с клавиатурой и видео Глава 8. Введение в управление видео и клавиатурой Глава 9. Видеосистема Глава 10. Работа с клавиатурой
Глава 8 Введение в управление видео и клавиатурой Цель: описать простейшие методы вывода информации на экран и обработки ввода с клавиатуры. Введение Вплоть до этой главы программы определяли элементы данных либо в виде непосредственных данных в операнде инструкции, либо в области данных. Однако большинство программ требуют получения данных из внешнего источника, например, клавиатуры, мыши, диска или модема, и должны выводить данные в пригодном для практических целей виде на диск, принтер или дисплей. В этой главе рассмотрены основы вывода информации на дисплей и ввода ее с клавиатуры. Инструкция ENT (Interrupt - прерывание) обеспечивает поддержку основных функций ввода-вывода. В этой главе рассматриваются два прерывания: INT ЮН - для работы с экраном, и ENT 21H, функции которого позволяют выводить данные на экран и считывать их с клавиатуры. Эти функции запрашивают определенные действия; значение, выбирающее определенную функцию, помещается в регистр АН, и обработчик прерывания выполняет соответствующие этому значению действия. Низкоуровневые операции BIOS, например INT 10H, передают управление непосредственно BIOS. Однако для выполнения некоторых более сложных действий INT 21Н сначала передает управление операционной системе. Например, ввод с клавиатуры может сопровождаться подсчетом количества введенных символов и сравнением их числа с максимально допустимым. INT 21H выполняет значительную часть этой дополнительной высокоуровневой обработки, после чего передает управление BIOS, выполняющей низкоуровневую часть операции. Условимся, что в этой книге значению 0DH соответствует символ Enter на клавиатуре и возврат каретки на экране и принтере. В этой главе рассматриваются следующие функции прерываний INT ЮН и INT21H: Глава 8. Введение в управление видео и клавиатурой 191
ШТ ЮН: Значение в АН 02Н 06Н Функция Установка курсора Прокрутка экрана ШТ 21Н: Значение в АН Функция 02Н 09Н ОАН 3FH 40Н Вывести на дисплей символ Вывести на дисплей строку Ввод с клавиатуры Ввод с клавиатуры Вывести на дисплей строку В главах 9 и 10 рассмотрены дополнительные возможности работы с экраном. Свойства экрана Типичный видеомонитор в текстовом режиме может выводить 25 строк текста (имеющих номера от 0 до 24) и 80 столбцов (с номерами от 0 до 79). Строки и столбцы формируют сетку адресуемых ячеек, в каждой из которых может находиться курсор. Вот несколько примеров расположения курсора: Десятичный формат Шестнадцатеричный формат Позиция на экране Строка Столбец Строка Столбец Верхний левый угол Верхний правый угол Центр экрана Нижний левый угол Нижний правый угол 00 00 12 24 24 00 79 39/40 00 79 ООН ООН ОСН 18Н 18Н ООН 4FH 27Н/28Н ООН 4FH Система выделяет участок памяти для Области видеоадаптера (Video Display Area) или буфера. Положение его в BIOS изменяется в зависимости от текущего режима видеоадаптера, например, текстового или графического, цветного или монохромного. В текстовом режиме область видеоадаптера занимает 4 Кбайт, 2 Кбайта из которых предназначены для хранения символов, и еще 2 Кбайта - для атрибутов каждого символа, например, инвертирования, мигания, повышенной яркости или подчеркивания. 192 Часть III. Работа с видео и клавиатурой
Область видеоадаптера также поддерживает "страницы" экрана, пронумерованные от 0 до 7. Страницы и атрибуты детально рассмотрены в гл. 10. В этой главе мы будем подразумевать использование страницы 0, установленной по умолчанию. Прерывания, обеспечивающие работу с дисплеем, передают ваши данные прямо в область видеоадаптера. Расположение этой области в памяти изменяется в зависимости от режима, и нет гарантий, что адреса этой области всегда будут неизменными, поэтому прямая запись данных в область видеоадаптера хотя и выполняется быстро, но несколько рискованна. Поэтому для работы с дисплеем обычно используются соответствующие функции прерываний INT ЮН и INT 21H, знающие корректные адреса. Установка курсора Установка курсора - одно из основных действий, поскольку его положение определяет, где будет выведен следующий символ, введенный с клавиатуры или полученный из другого источника. Операция BIOS для работы с экраном: INT ЮН в сочетании с кодом функции в регистре АН. Например, функция 02Н для INT ЮН указывает BIOS установить курсор. Вы загружаете требуемый номер страницы, обычно 0, в ВН, номер строки - в DH, и номер столбца - в DL. Содержимое других регистров не имеет значения. Следующий пример устанавливает курсор в строку 8, столбец 15: MOV АН,02Н ;Запросить установку курсора MOV ВН,00 ;Страница 0 MOV DH,08 ;Строка 8 MOV DL,15 ;Столбец 15 INT 10H /Вызвать обработчик прерывания Для установки в DX одновременно номеров и строки, и столбца можно воспользоваться инструкцией MOV с непосредственным значением во втором операнде: MOV DX,080FH ;Строка 8, столбец 15 Очистка экрана Функция 06Н прерывания INT 10H выполняет очистку и прокрутку экрана. Вы можете очищать весь экран или его часть, начиная с любой позиции на экране и заканчивая любой позицией с большими номерами строки и столбца. Загрузите следующие значения в регистры: АН - 06Н (функция прерывания INT 10H); AL - число строк прокрутки или ООН для полного экрана; Глава 8. Введение в управление видео и клавиатурой 193 13 — 5-741
ВН - значения атрибутов (цвет, мигание и так далее); СХ - начальные строка и столбец; DX - конечные строка и столбец. СХ и DX определяют область экрана, подлежащую прокрутке, AL - число строк, на которые нужно прокрутить вверх эту область. Для очистки всего экрана укажите начальную позицию в СХ в виде 00:00Н, а конечную - в DX в виде 18:4FH. Атрибут 71Н в следующем примере устанавливает весь экран в режим с белым фоном (7) и синими символами (1): MOV АХ,0600Н ;АН = Об (прокрутка), AL = 00 (весь экран) MOV ВН,71Н ;Белый фон (7), синие символы (1) MOV СХ,0000Н /Верхняя строка, левый столбец MOV DX,184FH ;Нижняя строка, правый столбец INT 10H ;Вызов обработчика прерывания Например, для прокрутки области экрана от строки 05, столбца 00 до строки 12, столбца 79 загрузите 0500Н в СХ и 0C4FH в DX. Постарайтесь избегать установки позиции курсора за пределами 184FH. В следующей главе прокрутка экрана рассматривается более подробно. Программа часто должна выводить для пользователя сообщения, запрашивающие ввод данных или исполнение определенного действия. Сначала мы рассмотрим несколько простых методов, полезных для упражнений и маленьких программ, а затем рассмотрим методы, использующие дескрипторы файлов (file handles). Функция 09Н прерывания INT 21Н для вывода на экран Простота исходной функции 09Н прерывания INT 21H для вывода на экран делает ее привлекательной для начинающих. Она требует определения строки, подлежащей выводу, в области данных и завершения этой строки символом доллара ($) или 24Н, который функция воспринимает, как требование завершить вывод. Недостаток этого метода в том, что вы не сможете вывести на экран этот символ. Следующий пример показывает, как применить этот способ вывода: CUST_MSG DB 'Customer паше?','$' /Строка для вывода Вы можете ввести символ доллара сразу за выводимой строкой, как в вышеприведенном примере, или включить его в выводимую строку в виде 'Customer name?$f, или в следующей строке программы в виде DB '$'. Установите в АН код функции 09Н, используйте LEA для загрузки адреса строки в DX и вызовите обработчик прерывания INT 21H: 194 Часть III. Работа с видео и клавиатурой
MOV AH,09H /Запросить вывод на экран LEA DX,CUST_MSG ;Загрузить адрес приглашения INT 21H ;Вызвать обработчик прерывания Вызов INT выводит символы слева направо и принимает за знак окончания вывода символ доллара ($). Операция не изменяет содержимого регистров. Если при выводе строка выходит за край экрана, не помещающаяся часть автоматически переносится на новую строку, а при необходимости производится прокрутка экрана. Если вы пропустите символ доллара в конце выводимой строки, обработчик будет продолжать вывод до тех пор, пока не обнаружит знака доллара - если он его обнаружит. Вывод символов таблицы ASCII Большая часть из 256 символов ASCII может быть выведена на экран. Некоторые значения, например, ООН и FFH, не соответствуют никаким выводимым символам и представляются на экране как пустое место, хотя пустой символ (пробел) имеет в ASCII особый код - 20Н. Программа, приведенная на рис. 8.1, выводит все символы ASCII-таблицы. Процедура A10MAJN вызывает следующие три процедуры: • B10SCREEN, использующую функцию 06Н прерывания INT 10H для очистки экрана; • C10CURSOR, использующую функцию 02Н прерывания INT 10H для установки курсора в позицию 08:00Н; • D10DISPLY, использующую функцию 09Н прерывания ЕМТ 21Н для вывода на экран содержимого переменной ASCHAR, инициализируемой значением ООН и последовательно увеличивающей значение на 1 вплоть до FFH. Первая выводимая строка начинается с пустого места (ООН), двух "улыбающихся лиц" (01Н и 02Н), сердечка (03Н), бубны (04Н), трефы (05Н) и пики (06Н). Символ 07Н в ASCII соответствует подаче звукового сигнала. Программа пропускает все символы с 08Н до ODH (08H соответствует удалению последнего введенного символа (backspace), 09H - табуляции, ОАН - переводу строки, a 0DH - возврату каретки к началу следующей строки). И, конечно, при использовании функции 09Н прерывания DMT 21Н символ доллара, имеющий код 24Н, не выводится совсем. Музыкальной ноте соответствует символ ОЕН, а символы с кодами от 7FH до FFH составляют расширенную таблицу ASCII. Отметим, что попытка вывода на экран символов Backspace, табуляции, перевода строки и возврата каретки - обычный способ выполнения этих операций. Предлагаем оттранслировать приведенную ниже программу, скомпоновать ее и преобразовать в файл типа .СОМ для выполнения. Глава 8. Введение в управление видео и клавиатурой 195 13*
TITLE BEGIN: ASCHAR ; .286 AlOMAIN AlOMAIN A08DISAS (СОМ) Вывод набора символов ASCII | .MODEL .CODE ORG JMP DB / PROC CALL CALL CALL MOV INT ENDP SMALL 100H SHORT AlOMAIN | 00, ■$' Главная процедура: | NEAR B10SCREEN C10CURSOR D10DISPLY AX,4C00H 21H ;Очистка экрана ;Установка курсора ;Вывод символов ;Завершение работы ; Очистка экрана и установка атрибутов: 1 7 BIOSCREEN BIOSCREEN PROC PUSHA MOV MOV MOV MOV INT POPA RET ENDP NEAR AX,0600H BH,07 CX,0000 DX,184FH 10H /Сохранение значений регистров ;Прокрутка целого экрана /Атрибуты: белый шрифт на черном фоне /Верхний левый угол /Нижний правый угол /Вызов обработчика прерывания /Восстановление значений регистров /Возврат к точке вызова ; Установка курсора в ячейку 08,00: 1 C10CURSOR C10CURSOR ; Вывод D10DISPLY D20: D40: D10DISPLY PROC PUSHA MOV MOV MOV INT POPA RET ENDP NEAR AH,02H BH,00 DX,0800H 10H символов ASCII ООН PROC PUSHA MOV LEA CMP JB CMP JBE MOV INT INC LOOP POPA RET ENDP END CX,256 DX,ASCHAR ASCHAR,08H D30 ASCHAR, 0DH D40 AH,09H 21H ASCHAR D20 BEGIN /Сохранение значений регистров /Запрос на установку курсора /Страница 0 /Строка 8, столбец 0 /Вызов обработчика прерывания /Восстановление значений регистров /Возврат к точке вызова - FFH, кроме символов от 08Н до 0DH: /Сохранение значений регистров /Подготовить 256 повторений /Указать адрес ASCHAR /Код символа меньше 08Н? /Да, вывести /Код символа меньше или равен 0DH? /Да, пропустить /Вывод ASCII-символа /Перейти к следующему символу /Уменьшить значение в СХ, / повторить, если не ноль /Восстановление значений регистров /Возврат к точке вызова Рис. 8.1. Вывод набора символов ASCII 196 Часть III. Работа с видео и клавиатурой
Функция ОАН прерывания INT 21Н для ввода с клавиатуры Функция ОАН прерывания INT 21H для ввода данных с клавиатуры весьма эффективна. Ей необходим список параметров (parameter list), содержащий ссылки на поля, которые должна обрабатывать инструкция INT. (Этому понятию в языках высокого уровня соответствуют запись или структура). Во-первых, обработчик прерывания должен знать максимальное количество символов, которое можно ввести, чтобы не допустить ввода пользователем лишних символов. Если происходит ввод излишних символов, программа подает звуковой сигнал и не воспринимает вводимые символы. Во-вторых, обработчик помещает в список параметров число действительно введенных байтов. Список параметров состоит из следующих элементов. 1. Первое предложение указывает имя списка параметров в форме LABEL BYTE. LABEL - это директива с атрибутом типа BYTE, вызывающая выравнивание по границе байтов. Поскольку это обычное выравнивание, ассемблер не увеличивает значение в счетчике положения. LABEL используется для назначения имени списку параметров. 2. Первый байт в списке параметров содержит предел - максимальное количество символов, которое можно ввести. Минимум - 0, и, поскольку это поле имеет размер 1 байт, максимум - FFH или 255. Вы определяете это значение, исходя из того, какие данные вы собираетесь вводить. 3. Второй байт используется операцией для хранения в двоичной форме числа действительно введенных символов. 4. Третий байт начинает поле, которое должно хранить введенные символы, слева направо. Следующий пример определяет список параметров для области ввода с клавиатуры: PARA_LIST LABEL BYTE ;Начало списка параметров MAX_LEN DB 20 ;Максимальное допустимое ; число символов ACT_LEN DB ? ;Число введенных символов KB_DATA DB 20 DUP (' ') ;Символы, введенные с клавиатуры В списке параметров директива LABEL указывает ассемблеру выполнять выравнивание полей по границе байтов и присваивает данной ячейке имя PARALIST. Поскольку LABEL не занимает места, PARALIST и MAXLEN ссылаются на одну и ту же ячейку памяти. MAXLEN определяет максимальное допустимое количество вводимых символов (20), ACTLEN предоставляет Глава 8. Введение в управление видео и клавиатурой 197
операции место для хранения числа действительно введенных символов, а KB_DATA резервирует 20 ячеек для вводимых символов. Вы можете использовать для этих полей любые корректные имена. Для запроса ввода с клавиатуры установите код функции в АН равным ОАН, загрузите адрес списка параметров (в примере - PARALIST) в DX и вызовите обработчик прерывания INT 21H: MOV АН,ОАН ;Запросить ввод с клавиатуры LEA DX,PARA_LIST /Загрузить адрес списка параметров INT 21H ;Вызвать обработчик прерывания Обработчик принимает вводимые пользователем с клавиатуры символы, проверяет их общее количество, и по мере ввода выводит их на экран в месте текущего расположения курсора. После вывода очередного символа обработчик перемещает курсор в следующую позицию. Для окончания ввода пользователь должен нажать <Enter>. Обработчик передаст этот символ (0DH) в поле ввода KBDATA, но не учитывает его при вычислении длины введенной строки. Если вы введете имя, например, Wilson + <Enter>, список параметров будет выглядеть примерно так: Символ ASCII Шестнадцате- ричный код Операция помещает длину введенного имени, 06Н, во второй байт списка параметров, в примере названный ACTLEN. Символ Enter, 0DH, хранится в байте KB_DATA+6 (в таблице этот символ обозначен как #, поскольку коду 0DH не соответствует никакой выводимый на экран символ). Поскольку максимальная длина в 20 символов включает и 0DH, пользователь может ввести только 19 символов. Эта операция воспринимает и обрабатывает нажатие клавиши Backspace, но ее нажатие не увеличивает значение счетчика символов. Операция не воспринимает больше, чем 20 символов, отличных от Backspace. В предыдущем примере, если пользователь введет 20 символов без нажатия <Enter>, обработчик вызывает подачу звукового сигнала и далее воспринимает только нажатия клавиши <Enter>. Хотя данная операция полезна при вводе данных, она не воспринимает нажатия дополнительных клавиш, например, функциональных (F1...F12) или клавиш перемещения курсора. Если вы хотите обрабатывать нажатия этих клавиш, воспользуйтесь функцией 01Н прерываний INT 16H или INT 21H, рассмотренных в гл. 10. 20 14 6 06 W 57 i 69 1 6С S 73 о 6F п 6Е # 0D 20 20 20 ... 198 Часть III. Работа с видео и клавиатурой
Программа: ввод имен Программа, приведенная на рис. 8.2, запрашивает ввод имени с клавиатуры, после чего выводит введенное с клавиатуры имя и подает звуковой сигнал. Если пользователь вводит, например, имя Dana Wilson, программа выполняет следующие действия. 1. Делит пополам длину имени (11/2 = 5, остаток игнорируется). 2. Вычитает результат из 40: 40 - 5 = 35. TITLE ; PARLIST MAXNLEN ACTULEN KBNAME PROMPT ' .386 A10MAIN А20: АЗО: A10MAIN / BIOINPUT A08CTRNM (EXE) Ввод имен с клавиатуры, вывод их в | .MODEL .STACK .DATA LABEL DB DB DB DB .CODE PROC MOV MOV MOV CALL MOV CALL CALL CALL CMP^ JE CALL CALL JMP MOV INT ENDP центре экрана, подача звукового сигнала | SMALL 64 BYTE 20 ? 21 DUP(' ' ) 'Name? ', '$' Список параметров имен: максимальная длина имени количество введенных символов введенное имя ;Требуется для применения MOVZX | FAR AX,@data инициализация сегментных | DS, АХ ES,AX регистров Q10CLEAR ;Очистка экрана | DX,0000 ;Установка курсора в 00,00 | Q20CURSOR В10INPUT Организация ввода имени 1 Q10CLEAR ;Очистка экрана ACTULEN,00 ;Введено ли имя? АЗО ;Нет, выход C10CENTER ;Установить сигнал, '$' / и выполнить центровку D10DISPLY ;Вывести имя А20 ;Повторить АХ,4С00Н ;Завершить работу 21Н Вывод приглашения и восприятие ввода с клавиатуры: PROC PUSH PUSH MOV LEA INT MOV LEA NEAR АХ ;Сохраняем используемые DX АН,09Н ,-: DX,PROMPT 21Н ан,оан ,-: регистры Запрос на вывод на экран приглашения пользователя Запрос ввода с клавиатуры DX,PARLIST /Указание адреса PARLIST Рис. 8.2. Ввод с клавиатуры и вывод имен на экран Глава 8. Введение в управление видео и клавиатурой 199
BIOINPUT ClOCENTER ; NEG CALL ClOCENTER ; DlODISPLY -v DlODISPLY / QIOCLEAR QIOCLEAR / / Q20CURSOR Q20CURSOR INT POP POP RET ENDP PROC MOVZX MOV MOV MOV SHR DL ADD MOV 21H DX AX Восстановление значений регистров /Установить сигнал и '$' 1 ; и выполнить центровку NEAR BX,ACTULEN Использует ВХ и DX Заменить ODH на 07Н KBNAME[BX],07 | KBNAME[BX+1], DL,ACTULEN DL,1 DL,40 DH,12 Q20CURSOR ENDP PROC MOV LEA INT RET ENDP PROC PUSHA MOV MOV MOV MOV POPA INT RET ENDP PROC MOV MOV INT RET ENDP END RET '$' ;Установить ограничитель вывода Определить центральный столбец: разделить длину строки на 2, изменить знак, прибавить 40 Центральный столбец Установить курсор Вывести имя в центр экрана: 1 NEAR АН,09Н DX,KBNAME 21Н Очистка экра* NEAR АХ,0600Н ВН,30 СХ,0000 DX/184FH ЮН Использует АН и DX Вывод имени ia и установка атрибутов: •Сохранить значения регистров ;Запросить прокрутку экрана ;Атрибут цвета •От 00,00 ; до 24,79 ;Восстановить значения регистров Установка курсора в столбец и строку: NEAR АН,02Н ВН,00 ЮН A10MAIN ;ВХ устанавливается при входе ,• Использует АН и ВН ;Запрос на установку курсора ,• Страница 0 Рис. 8.2 (Окончание) В процедуре ClOCENTER инструкция SHR сдвигает длину 11 вправо на 1 бит, практически выполняя деление на 2: биты 00001011 преобразуются в 00000101 (или 5). Инструкция NEG меняет знак, преобразуя 5 в -5. ADD добавляет 200 Часть III. Работа с видео и клавиатурой
значение 40, устанавливая в регистре DL стартовую позицию на 35 столбец. При таких установках имя будет выведено, начиная со строки 12, столбца 35. Заметьте, что в C10CENTER инструкции, помещающие символ звукового сигнала (07Н) в область ввода, размещают его непосредственно за именем: MOVZX BX,ACTULEN ; Заменяем 0DH на 07H в ВХ MOV KBNAME[BX] , 07Н MOVZX помещает в ВХ число введенных символов. В следующей команде [ВХ] действует как индексный регистр для выполнения расширенной адресации. MOV суммирует длину в ВХ с адресом KBNAME и помещает 07Н по вычисленному адресу. Для длины 11 инструкция помещает 07Н в ячейку с адресом KBNAME+11 после имени (замещая символ Enter). Инструкция MOV KBNAME[BX+1],'$' /Установить ограничитель вставляет ограничивающий символ '$' после 07Н, так что функция 09Н прерывания INT 21Н может вывести имя и подать звуковой сигнал. В процедурах C10ENTER, D10DISPLY и Q20CURSOR использование регистров описано в комментариях. На практике процедуры помещают содержимое регистров в стек и извлекают его из стека. Полезные приемы программирования В следующих параграфах рассмотрено несколько полезных процедур. Ввод только клавиши <Enter> В программе (рис. 8.2) прием новых имен продолжается до тех пор, пока при очередном запросе пользователь просто не нажмет <Enter>. Функция 09Н прерывания ENT 21Н воспринимает это нажатие и помещает длину ООН в список параметров приблизительно так: Список параметров (в шестнадцатеричной форме) 14 00 0D 00 Если длина равна 0, программа определяет, что ввод закончен, как видно из инструкции CMP ACTULEN,00 в A10MAIN. Удаление символа Enter Вводимые символы можно использовать в различных целях, например, для добавки к тексту, заполнения отчета или сохранения на диске. Для этого вам может потребоваться заменить символ <Enter> (0DH) на пробел (20Н) во всех местах, где он встречается в KBNAME, например, так: MOVZX BX,ACTULEN Остановить ВХ в 00 0В (11) MOV KBNAME[BX],2ОН ;Заменить символ Enter пробелом Инструкция MOVZX помещает в ВХ длину 11. MOV помещает пробел (20Н) по адресу, указанному в первом операнде: адрес KBNAME плюс содержимое ВХ , т.е. KBNAME+11. Глава 8. Введение в управление видео и клавиатурой 201
Очистка области ввода Каждый введенный символ заменяет предыдущий в области ввода, и остается в области ввода, пока его не заменит новый. Например: Ввод Monroe Franklin Adams Первое имя, Monroe, занимает только 6 байт. Второе имя, Franklin, полностью замещает более короткое имя Monroe. Поскольку третье имя, Adams, короче, чем Franklin, оно замещает только Frank, a Enter замещает символ 1. Оставшиеся две буквы "in" все еще следуют за Adams. Перед вводом нового имени KBNAME можно очищать, например, так: MOV СХ,2 0 /Инициализируем для 20 повторений MOV 31,0000 /Начальная позиция для имени Список параметров 14 14 14 06 08 05 4D 46 41 6F 72 64 (в шестнадцатеричной форме) 6Е 61 61 72 6Е 6D 6F 6В 73 65 6С 0D 0D 69 69 20 6Е 6Е 20 0D 0D 20 20 20 L10: MOV KBNAME[SI], 20Н INC SI /Поместить один пробел в имя /Увеличить значение в SI на 1 / для обработки следующего символа LOOP L10 /Повторить 20 раз Вместо SI можно использовать регистр DI или ВХ. Если бы процедура помещала в строку сразу 2 пустых символа в виде слова, потребовалось бы всего 10 повторений. Однако поскольку KBNAME определена с помощью DB, необходимо будет сначала переопределить ее длину с помощью WORD и PTR, как показано ниже: MOV СХ,10 LEA SI,KBNAME L10: MOV WORD PTR [SI] , 2020H ADD SI,2 LOOP L10 /Подготовим 10 повторений /Начальная позиция для имени /Поместить пару пробелов в имя /Увеличить значение в SI на 2 для / обработки следующих символов /Повторить 10 раз Команду MOV, помеченную L10, следует трактовать так: "поместить пробельное слово в ячейки памяти с адресом из SI". В этом примере LEA используется для начальной установки цикла очистки KBNAME, который применяет в команде MOV, помеченной L10, косвенную регистровую адресацию, поскольку нельзя задавать инструкцию вроде MOV WORD PTR[KBNAME],202ОН /Первый операнд некорректен Очистка области ввода решает проблему коротких имен, следующих за ранее введенными более длинными. Для ускорения выполнения можно очищать только позиции, следующие за последним введенным именем. 202 Часть III. Работа с видео и клавиатурой
Использование управляющих символов для вывода на экран Один из способов увеличения эффективности вывода на экран - использование символов перевода строки, возврата каретки и табуляции. Вы можете вводить их в виде символов ASCII или в шестнадцатеричной форме, как указано в таблице. Управляющий Код Шестнадцатеричный Воздействие на курсор символ ASCII код Возврат каретки 13 Перевод строки 10 Табуляция 09 0DH ОАН 09Н Перемещает в первую слева позицию в текущей строке Переводит на следующую строку Переводит к следующей позиции табуляции Эти символы можно использовать для управления курсором при вводе с клавиатуры или выводе информации на экран. Вот пример, выводящий содержимое строки REPTTTLE, заканчивающейся возвратом каретки (13) и переводом строки (10) для установки курсора в начало следующей строки. Использование директивы EQU для переопределения управляющих символов может сделать программу более легкой для понимания: ;или EQU 0DH ;или EQU ОАН ;или EQU 09H TAB,'Годовая статистика осадков•, CR, LF, '$' /Запросить вывод на экран /Загрузить в DX адрес заголовка /Вызвать обработчик прерывания CR LF TAB REPTITLE EQU EQU EQU DB MOV LEA INT 13 10 09 TAB,'Годое АН,09Н DX,REPTITLE 21H Функция 02Н прерывания INT 21H для вывода на экран Функция 02Н прерывания ENT 21Н полезна для вывода на экран одиночных символов. Загрузите в регистр DL символ, который нужно вывести в текущей позиции курсора, и вызовите прерывание INT 21Н с кодом функции 02Н. Символы табуляции, возврата каретки и перевода строки обрабатываются как обычно, а курсор перемещается в соответствующие позиции. Инструкции для использования этой функции таковы: Глава 8. Введение в управление видео и клавиатурой 203
MOV AH/02H ;Запросить вывод символа на экран MOV ВЪ,символ ;Подлежащий выводу символ INT 21H ;Вызвать обработчик прерывания Следующий пример демонстрирует использование этой функции для вывода строки символов. Строка, которую нужно вывести, называется CO_TITLE. В примере адрес строки СО_ТПЪЕ загружается в DI, а ее длина - в СХ. Цикл включает в себя увеличение значения в DI на 1 (инструкцией INC) для каждого из символов по очереди и уменьшение СХ (при помощи LOOP) вплоть до вывода последнего символа. Вот необходимые инструкции: CO_TITLE DB *Intertech Corp. •, 13 ДО MOV АН,02Н ;Запросить вывод символа на экран MOV СХ,17 ; Длина строки символов LEA DI,CO_TITLE ;Адрес строки символов L10: MOV DL, [DI] /Выводимый символ INT 21H ;Вызов обработчика прерываний INC DI ;Переход к следующему символу LOOP L10 /Повторение цикла ;Продолжение программы Дескрипторы файлов В этом параграфе описано использование дескрипторов файлов для работы с экраном и клавиатурой. Дескриптор файла - это просто число, соответствующее некоторому устройству. В следующей таблице приведены стандартные дескрипторы файлов, не требующие предварительного определения. Дескриптор Устройство 00 Ввод, обычно с клавиатуры (CON) , может быть перенаправлен 01 Вывод, обычно на дисплей (CON), может быть перенаправлен 02 Вывод сообщений об ошибках, на дисплей (CON), не может быть перенаправлен 03 Вспомогательное устройство (AUX) 04 Принтер (LPT1 или PRN) Как видно, обычные дескрипторы: 00 - для клавиатуры, и 01 - для дисплея. Дескрипторы для дисковых устройств (описанные в гл. 17) должны устанавливаться вашей программой. Вы можете также перенаправлять ввод/вывод на другие устройства, хотя эта возможность здесь не рассматривается. 204 Часть III. Работа с видео и клавиатурой
Функция 40Н прерывания INT 21Н для вывода на дисплей Функция 40Н прерывания ENT 21Н предназначена для работы с дисплеем. Для вызова этой функции загрузите в регистры следующие значения: АН = 40Н (код функции) СХ = Число выводимых символов ВХ = 01 (дескриптор файла) DX = Адрес выводимых символов Успешное выполнение операции INT приводит к установке флага переноса (CF) в 0, а также помещению в АХ числа записанных байтов. Неудача при выполнении операции INT устанавливает флаг CF в 1 и помещает в АХ код ошибки: 05Н - нет доступа к устройству (устройство отсоединено или неисправно), 06Н - некорректный дескриптор. Поскольку АХ может содержать как код ошибки, так и число записанных байтов, единственный способ узнать об ошибке - проверить состояние флага CF, хотя ошибки при работе с дисплеем весьма редки: JC адрес_обработчика_ошибки /Проверка корректности вывода Эта операция воспринимает символы 07Н (звуковой сигнал), 08Н (Backspace), ОАН (перевод строки) и 0DH (возврат каретки) точно так же, как и функция 09Н прерывания INT 21H. Следующий пример демонстрирует применение функции 40Н: PROMPT DB 'Part number?*, 0DH,ОАН /Выводимые данные MOV АН,40Н /Запросить вывод MOV ВХ,01 /Дескриптор файла для дисплея MOV СХ,14 /14 символов, включая 0DH и ОАН LEA DX,PROMPT /Выводимый элемент данных INT 21H /Вызвать обработчик прерывания Упражнение: использование функции 40Н для вывода на экран Воспользуемся DEBUG для изучения внутренних механизмов использования дескриптора файла в ходе вывода вашего имени на экран. Загрузите DEBUG и, когда появится его приглашение, введите А 100, чтобы начать ввод следующих предложений на ассемблере (кроме номеров в начале каждой строки), начиная с адреса 100Н: 100 MOV АН, 40 102 MOV ВХ,01 Глава 8. Введение в управление видео и клавиатурой 205
105 MOV CX,xx (вместо хх укажите длину вашего имени в шестнадцатеричной форме) 108 MOV DX,10F 10В INT 21 10D JMP 100 10F DB 'ххххххх' (в апострофах укажите свое имя) Инструкции загружают в АН код функции 40Н и устанавливают 10FH в DX - адрес элемента данных DB, содержащих ваше имя. После ввода этих инструкций вновь нажмите <Enter>. Для дизассемблиро- вания программы воспользуйтесь командой U (U 100,10Е), а для наблюдения за выполнением программы - R, а затем - последовательно Т для выполнения каждой команды. При достижении INT 21 введите Р для выполнения подпрограммы обработчика прерывания и перехода к инструкции JMP; на экран должно быть выведено ваше имя. Функция 3FH прерывания INT 21Н для ввода с клавиатуры Функция 3FH прерывания INT 21H предназначена для ввода данных с клавиатуры с использованием дескриптора файла, хотя это несколько неуклюжая операция. Загрузите в регистры следующие значения: АН = 3FH (код функции) СХ = Максимальное число вводимых символов ВХ = 00 (дескриптор файла) DX = Адрес в памяти для хранения вводимых символов Успешное выполнение операции INT приводит к установке флага переноса (CF) в 0, а также помещению в АХ числа записанных байтов. Неудача при выполнении операции INT может произойти из-за некорректной установки дескриптора; при этом флаг CF устанавливается в 1, а в АХ помещается код ошибки: 05Н - нет доступа к устройству (устройство отсоединено или неисправно), 06Н - некорректный дескриптор. Поскольку АХ может содержать как код ошибки, так и число записанных байтов, единственный способ узнать об ошибке - проверить состояние флага CF, хотя ошибки при работе с клавиатурой весьма редки. Как и функция 0AH прерывания INT 21H, функция 3FH также воспринимает клавишу Backspace, но игнорирует нажатия клавиш Fl—F12, клавиш управления курсором и других управляющих клавиш. Это серьезно ограничивает ее применение. Следующие инструкции демонстрируют использование функции 3FH: 206 Часть III. Работа с видео и клавиатурой
KBINPUT DB 20 DUP(' ') /Область для вводимых данных MOV AH,3FH ;Запросить ввод с клавиатуры MOV ВХ,00 /Дескриптор файла для клавиатуры MOV СХ,20 ;20 символов, включая 0DH и 0AH LEA DX,KBINPUT ;Адрес для вводимых данных INT 21H ;Вызвать обработчик прерывания Операция INT ожидает ввода символов, но, к сожалению, не проверяет, превосходит ли общее число введенных символов число, указанное в СХ. В качестве признака завершения ввода воспринимается нажатие клавиши <Enter>. Например, при вводе строки Intertech Corp<Enter> в KBINPUT будет занесено Intertech Corp|0DH|0AH|. За введенными вами буквами следует знак возврата каретки, соответствующий клавише <Enter>, и знак перевода строки, который вы не вводили. Поэтому размер буфера памяти для вводимых символов, задаваемый в программе, должен на 2 превосходить длину строки, ввод которой ожидается. Если вы введете меньше символов, чем при предыдущем вводе, то незадействованные байты будут содержать конец предыдущей строки. Успешно выполненная операция INT устанавливает флаг переноса (CF) в 0 и помещает в АХ числа записанных байтов. В предыдущем примере это число равно 14 плюс 2 для символов перевода строки и возврата каретки. Программа также может использовать число в АХ для определения количества введенных символов. Хотя это свойство не слишком важно при вводе простых строк, например, YES или N0, о нем нужно помнить при вводе других данных, например, имен. Если число введенных символов превышает указанное в регистре СХ, операция принимает все введенные символы. Рассмотрим ситуацию, в которой лимит символов в СХ равен 8, а введена строка "PC Exchange". Операция устанавливает первые 8 символов в области хранения данных в "PC Ecxha", но без символов перевода строки или возврата каретки в конце, и устанавливает длину введенной строки в 08. Однако при следующем вызове этой функции программа не воспринимает символы непосредственно с клавиатуры, поскольку в ее буфере все еще хранится остаток предыдущей строки. Функция заносит в первые три байта остаток "nge", добавляет к ним символы возврата каретки и перевода строки, устанавливает АХ в 05 и, поскольку обе операции корректны, они устанавливают флаг CF в 0: первый вызов: PC Excha AX = 08 второй вызов: nge, 0DH, 0AH АХ = 05 Глава 8. Введение в управление видео и клавиатурой 207
Программа может определить, не была ли введена слишком длинная строка, проверив, меньше ли число в АХ числа в СХ, и являются ли двумя последними символами введенной строки символы 0DH и ОАН. Если это не так, может потребоваться дополнительный вызов INT для приема оставшейся части символов. После всего изложенного возникает вопрос, зачем вообще помещать в СХ максимальное количество вводимых символов? Упражнение: использование функции 3FH для ввода с клавиатуры Воспользуемся DEBUG для изучения использования дескриптора файла для ввода с клавиатуры с помощью функции 3FH. Программа позволяет вводить до 12 символов. Загрузите DEBUG и, когда появится его приглашение, введите А 100, чтобы начать ввод следующих предложений на ассемблере (кроме номеров в начале каждой строки), начиная с адреса 100Н: 100 MOV AH,3F 102 MOV BX,00 105 MOV 0x,0с 108 MOV DX,10F 10В INT 21 10D JMP 100 10F DB 'xxxxxxx' (в апострофах укажите свое имя) Инструкции загружают в АН код функции 3FH и устанавливают 10FH в DX - адрес элемента данных DB, содержащих вводимые данные. После ввода этих инструкций вновь нажмите <Enter>. Для дизассемблиро- вания программы воспользуйтесь командой U (U 100,10Е), для наблюдения за выполнением программы используйте R, а затем - последовательно Т для выполнения каждой команды. При достижении INT 21 введите Р для выполнения подпрограммы обработчика прерывания; обработчик будет воспринимать вводимые символы вплоть до нажатия <Enter>. Просмотрите содержимое АХ и состояние флага CF, используйте команду D DS:10F для просмотра введенных символов в памяти. Ключевые моменты • Прерывания, работающие с дисплеем, передают данные в область видеоадаптера. • Инструкция INT 10H передает управление BIOS для работы с дисплеем. Две наиболее часто используемые функции - 02Н (установить курсор) и 06Н (прокрутить экран). 208 Часть III. Работа с видео и клавиатурой
• При использовании функции 09Н прерывания INT 21Н для вывода на дисплей укажите ограничивающий символ "$" сразу за выводимыми данными. Пропуск ограничителя может иметь впечатляющие последствия. • Функция О АН прерывания INT 21Н для ввода с клавиатуры требует списка параметров. Первый байт списка содержит максимальное количество вводимых символов, а второй - число действительно введенных. • Дескриптор файла - это число, соответствующее определенному устройству. Дескрипторы с номерами от 00 до 04 предопределены, а остальные могут определяться в вашей программе. • Для выполнения функцией 40Н прерывания INT 21H вывода на дисплей загрузите в регистр ВХ дескриптор 01. • Для выполнения функцией 3FH прерывания INT 21H ввода с клавиатуры загрузите в регистр ВХ дескриптор 00. Эта функция добавляет символы возврата каретки и перевода строки в конце введенной последовательности символов, но не проверяет количества введенных символов. Вопросы и упражнения 1. На экране с 80 столбцами, каковы шестнадцатеричные координаты (а) нижнего правого угла? (б) верхнего левого угла? 2. Напишите инструкции, устанавливающие курсор в строку 12, столбец 24. 3. Напишите инструкции для очистки области экрана, начинающейся со строки 08, столбца 0, с атрибутом цвета 71Н. 4. Определите элементы данных и напишите инструкции, необходимые для вывода на экран функцией 09Н прерывания INT 21H сообщения "What is the current date (mm/dd/yy)?". После вывода на экран сообщения должен подаваться звуковой сигнал. 5. Определите элементы данных и напишите инструкции, необходимые для ввода данных с клавиатуры функцией 0AH прерывания INT 21H, соответствующих формату из вопроса 4. 6. В параграфе "Очистка области ввода" показана очистка области, названной KBNAME, предназначенной для хранения вводимых с клавиатуры символов. Измените приведенную там программу так, чтобы она очищала только символы, не используемые в последнем введенном имени. 7. Внесите в программу, приведенную на рис. 8.2, следующие изменения: (а) увеличьте максимальную длину вводимых имен до 25 символов, (б) вместо строки 12 выводите имена в центре строки 7, (в) вместо очистки всего Глава 8. Введение в управление видео и клавиатурой 209 14 — 5-741
экрана очищайте только строки с 0 по 7 и используйте в операции атрибут цвета 16Н, (г) в начале каждой процедуры сохраняйте используемые регистры в стеке и извлекайте их оттуда в конце процедуры. Оттранслируйте, скомпонуйте и выполните программу. 8. Укажите стандартные дескрипторы файлов для (а) принтера, (б) ввода с клавиатуры, (в) обычного вывода на экран. 9. Определите элементы данных и напишите инструкции, необходимые для вывода на экран сообщения "What is the current date (mm/dd/yy)?" функцией 40H прерывания INT 21H. В конце строки сообщения должны быть символы 0DH и ОАН. После вывода на экран сообщения должен подаваться звуковой сигнал. 10. При исполнении программы команда INT 21Н устанавливает флаг CF в 1 и помещает в АХ значение 06Н. Какова возможная причина этой ошибки? 11. Определите элементы данных и напишите инструкции, необходимые для ввода с клавиатуры функцией 3FH прерывания INT 21H данных, соответствующих формату из вопроса 9. 12. Повторите задание из пункта 7, используя функции 3FH и 40Н прерывания INT 21Н для ввода с клавиатуры и вывода на экран. Оттранслируйте, скомпонуйте и выполните программу. 210 Часть III. Работа с видео и клавиатурой
Глава 9 Видеосистема Цель: описать дополнительные возможности работы с дисплеем, включая прокрутку, инвертирование, установку режимов и атрибутов и использование графики. Введение В этой главе выполняется углубленное рассмотрение материала по работе с дисплеем, рассмотренного в гл. 8. В первом параграфе описаны компоненты видеосистемы: монитор, область видеоданных и видеоконтроллер. В следующем параграфе рассмотрены такие свойства дисплея, как режимы, атрибуты и страницы. Далее, в большом параграфе, посвященном прерыванию BIOS INT ЮН, предназначенному для работы с дисплеем, описаны такие действия, как установка режима, установка курсора, прокрутка экрана, вывод символов и установка атрибутов. В последних нескольких параграфах рассмотрены различные функции, используемые для вывода на экран графики, а также непосредственный вывод на дисплей и создание рамок меню. В этой главе рассмотрены следующие функции INT 10H: Установить цветовую палитру Записать пиксел Прочитать пиксел Записать в режиме телетайпа Получить текущий видеорежим Обратиться к регистрам палитры Обратиться к знакогенератору Выбрать альтернативную программу Вывести строку символов Вернуть видеоинформацию ООН 01Н 02Н озн 05Н 06Н 07Н 08Н 09Н ОАН Установить видеорежим Установить размер курсора Установить положение курсора Вернуть состояние курсора Установить активную страницу Прокрутить экран вверх Прокрутить экран вниз Прочитать сивол/атрибут Вывести символ/атрибут Вывести символ ОВН ОСН 0DH ОЕН 0FH ЮН 11Н 12Н 13Н 1ВН Глава 9. Видеосистема 14* 211
Компоненты видеосистемы Стандартные (или когда-то бывшие таковыми) адаптеры дисплея - это MDA (monochrome display adapter - адаптер монохромного дисплея), CGA (color graphics adapter - цветной графический адаптер), EGA (enhanced graphics adapter - улучшенный графический адаптер) и VGA (video graphics array - видеографический адаптер). VGA и последовавшие за ним различные системы SVGA (Super VGA) полностью вытеснили ранее использовавшиеся. Вследствие общедоступности VGA, в этой книге описывается только этот видеоадаптер. Основные компоненты видеосистемы - это монитор, область видеоданных, видео-BIOS и видеоконтроллер. Другие встроенные устройства включают в себя знакогенератор, контроллер режима, генератор видеосигнала и декодер атрибутов. На рис. 9.1 показана взаимосвязь между этими устройствами. данные от программы Область памяти дисплея атрибуты Декодер атрибутов биты данных Генератор символов символы Генератор видеосигнала Монитор Видеоконтроллер строчная и кадровая синхронизация i синхронизация адресация Управление режимом Рис. 9.1. Компоненты видеосистемы Монитор Экран монитора содержит набор близко расположенных друг к другу горизонтальных линий, называемый растром. Каждая линия состоит из сотен точек, называемых пикселами. Каждая из точек состоит из трех зерен люминофора - по одной для каждого из трех аддитивных цветов: красного, синего и зеленого. Три электронных луча вызывают свечение этих зерен, формируя цвет пикселов. Лучи перемещаются по экрану вдоль строки слева направо и сверху 212 Часть III. Работа с видео и клавиатурой
вниз, начиная свое движение в каждом кадре с верхнего левого угла и заканчивая его в нижнем правом. Изменения в интенсивности лучей вызывают изменение яркости свечения люминофора и, соответственно, изменения в яркости пикселов. Различные сочетания интенсивностей трех основных цветов создают всевозможные цвета и оттенки. Область памяти дисплея Программа посылает данные - символы в текстовом режиме и пикселы в графическом - в область памяти дисплея (также называемую буфером) в оперативной памяти при помощи операций INT или, помещая данные непосредственно в эту область памяти. Данные проходят довольно сложный процесс преобразования, перед тем как, наконец, выводятся на экран. Начальный адрес этой области зависит от типа видеоадаптера и выбранного режима. Прерывания, обслуживающие вывод на экран, передают ваши данные непосредственно в эту область памяти. Далее приводятся адреса начала сегментов для основных видеорежимов: • А000:[0] используется для дескрипторов шрифтов в текстовом режиме и для графики высокого разрешения в видеорежимах от 0DH до 1ЗН; • В000:[0] - для монохромного текста в режиме 07Н; • В800:[0] - для текста и графики в режимах от ООН до 06Н. Схемы видеоадаптера непрерывно просматривают данные в видеообласти и в соответствии с ними обновляют экран. Данные в видеообласти могут быть символами таблицы ASCII (текстом) или графикой. В текстовом режиме каждый символ в видеообласти занимает два байта: один байт - для, собственно, кода символа, а второй -для определения его цвета и яркости. В графическом режиме в видеообласти хранятся группы битов, определяющие цвет и интенсивность каждого пиксела. Область памяти дисплея позволяет хранить данные в страницах. Страница содержит данные для одного полного экрана, а всего страниц 8, с номерами от О до 7. Страница 0 всегда выбрана по умолчанию и, для текстовых режимов, начинается с адреса области памяти дисплея В800[0]. Страница 1 начинается с адреса В900[0], страница 2-е адреса ВА00[0], страница 3-е адреса ВВ00[0], и т.д. Вы можете работать с любой станицей в памяти, но Одновременно на экран выводится только одна страница. Как отмечалось выше, в текстовом режиме каждый выводимый на экран символ занимает два байта. Следовательно, одна страница занимает 80x25x2 = 4000 байт. В действительности для одной страницы выделяется область памяти размером 4 Кбайт (4096 байт). Так что за каждой страницей следует блок из 96 неиспользуемых байтов. Глава 9. Видеосистема 213
Видеоконтроллер Видеоконтроллер генерирует сигналы, управляющие разверткой на экране. Он также содержит счетчик, указывающий текущий адрес в области видеоданных. Этот счетчик указывает схемам видеоадаптера, какие данные в текущий момент обрабатывать и передавать монитору. Контроллер синхронизирует передачу данных с синхросигналом. Сразу после горизонтального прохода лучей контроллер выдает вертикальный синхросигнал, вызывающий переход к следующей строке вниз и возврат луча к левому краю экрана. Таким образом последовательно проходится вся занятая изображением область экрана. Видеоконтроллер также управляет размером и положением курсора и выбором страницы, подлежащей выводу на экран. В его составе также есть несколько регистров, к которым программы могут получить доступ для записи и чтения их содержимого. Знакогенератор, показанный на рис. 9.1, преобразует коды ASCII области видеоданных в сочетания пикселов, составляющих символы. Декодер атрибутов расшифровывает байт атрибутов области видеоданных в сигналы, определяющие цвет символа. Видео-BIOS Видео-BIOS выполняет роль интерфейса видеоадаптера и выполняет такие задачи, как установку курсора и вывод символов. ОЗУ видео-BIOS поддерживает две области видеоданных: 1. 40:[49Н] содержит такие данные, как текущий режим, число столбцов и размер области дисплея; 2. 40:[84Н] содержит, в частности, число строк и высоту символов. Более подробно видео-BIOS описана в гл. 24. Подпрограммы в ПЗУ BIOS адаптера VGA начинаются с адреса С000:[0]. Видеорежимы Видеорежим определяет, выводится ли на экран текст или графика, в цвете или в монохромном режиме, с каким разрешением и каким количеством цветов. Функция ООН прерывания BIOS INT ЮН используется для инициализации видеорежима исполняемой в данный момент программы или для переключения между текстовыми и графическими режимами. Например, режим 03 - это текстовый режим с 25 строками и 80 столбцами, цветной, с разрешением дисплея 720x400 точек. Функция 0FH прерывания INT 10H считывает номер текущего видеорежима и помещает его в регистр AL. Установка режима также приводит к очистке экрана. Обе упомянутые функции подробно описаны далее. 214 Часть III. Работа с видео и клавиатурой
Текстовый (алфавитно-цифровой) режим используется для вывода символов таблицы ASCII. Он работает одинаково и для цветного, и для монохромного режимов, за исключением того, что цветной режим не позволяет выполнять подчеркивание символов. В нижеследующей таблице приведены наиболее распространенные текстовые режимы, с номером режима, приведенным в первом столбце слева: Режим 00 01 02 03 07 СтолбцыхСтроки 25x40 25x40 25x80 25x80 25x80 Дисплей Тип Цветной Цветной Цветной Цветной Монохромный Адрес B800 В800 В800 В800 ВООО Страницы 0-7 0-7 0-3 0-3 0 Разрешение 360x400 360x400 720x400 720x400 720x400 Цвета 16 16 16 16 Текстовые режимы 00 и 01 - 40-столбцовый формат - изначально были предназначены для CGA, но поддерживаются и адаптерами VGA. Текстовые режимы 02 и 03 - 80-столбцовый формат - также изначально были предназначены для CGA, но поддерживаются и адаптерами VGA. Текстовый режим 07 (монохромный): стандартный монохромный режим. Графические режимы рассмотрены далее, в параграфе "Использование графических режимов". Атрибуты В текстовом режиме байт атрибутов определяет свойства каждого выводимого символа. Когда программа устанавливает атрибут, он остается в установленном состоянии до следующего явного изменения. Поэтому все дальнейшие символы после того, для которого установлен атрибут, будут иметь такие же атрибуты. Можно использовать функции прерывания INT 10H для генерации атрибутов, а также выполнения прокрутки экрана, считывания символов и атрибутов или вывода на экран. Чтобы просмотреть область памяти дисплея, воспользуйтесь командой DEBUG D В800:0. В этой области памяти после каждого байта, содержащего код символа, стоит байт, содержащий его атрибуты. Байт атрибутов имеет следующую структуру: Фон BL 7 R 6 G 5 В 4 Символ I 3 R G . 2 1 В 0 Атрибут: Номер бита: Буквы R, G и В указывают позиции битов, соответствующих красному, зеленому и синему цветам. Глава 9. Видеосистема 215
• Бит 7 (BL) устанавливает атрибут мерцания (может быть заблокирован). • Биты 6—4 определяют цвет фона символа. • Бит 3 (I) устанавливает для символа нормальную (0) или повышенную (1) яркость. • Биты 2—0 определяют составляющие цвета символа. Фон может иметь один из восьми цветов, а сам символ - один из шестнадцати. Миганце и яркость относятся только к символу, хотя можно использовать функцию ЮН прерывания INT 10H для отключения возможности мерцания и перехода фона в шестнадцатицветный режим. Также можно выбрать один из шестнадцати цветов для рамки. Используя различные сочетания трех основных цветов (красного, зеленого и синего), можно представить 8 различных цветов (включая черный и белый), а с использованием нормальной или повышенной интенсивности (I в нижеследующей таблице) - 16 цветов. Цвет I R В Шестнадцатеричный код Черный Синий Зеленый Голубой Красный Пурпурный Коричневый Белый Серый Светло-синий Светло-зеленый Светло-голубой Светло-красный Светло-пурпурный Желтый Ярко-белый 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 о о о о 1 1 1 1 о о 1 1 о о 1 1 о о 1 1 о о 1 1 о 1 о 1 о 1 о 1 о 1 о 1 о 1 о 1 о 1 2 3 4 5 6 7 8 9 А В С D Е F Если цвета фона и символа совпадают, то символ будет невидимым. Также можно использовать байт атрибутов для того, чтобы вызвать мерцание символа. Видеосистема вызывает мерцание, заменяя цвет фона цветом символа примерно каждые две секунды, так что символ исчезает с дисплея. Вот несколько типичных значений атрибута (где BL - blinking, мерцание). 216 Часть III. Работа с видео и клавиатурой
Фон Черный Синий Зеленый Белый Зеленый Символ Синий Красный Голубой Светло- пурпурный Серый (мерцающий) Фон BL 0 0 0 0 1 R G 0 0 ' 0 0 0 1 1 1 0 1 в ' 0 1 0 1 0 Символ I 0 0 0 1 1 R G 0 . 0 1 0 0 1 1 0 0 0 в 1 0 1 1 0 Шестнадца- теричный код 01 14 23 7D А8 Для монохромного монитора байт атрибутов используется так же, как и для цветного, за исключением того, что бит 0 устанавливает подчеркивание символа. Для определения атрибутов можно устанавливать биты следующим образом: • Нормальный режим (черный фон, белые символы): оооо ош (07н) • Инверсный режим (черные символы, белый фон): ош оооо (7он) Значение четырех битов байта атрибутов соответствует битам 0—3 регистра цветов контроллера (Controller's Color Plane Enable), в свою очередь указывающих на один из 16 регистров палитры. Биты 0—5 регистра палитры связываются с шестью сигналами RGB (три - цветовых и три - яркостных). Значения битов в регистре палитры указывают на один из 256 регистров в ЦАП (цифро- аналоговый преобразователь), определяющих выводимый цвет. Можно создавать цвета, выбирая атрибут для каждого символа. Также можно изменять цвета в регистрах палитры с помощью функции ЮН прерывания INT 10H, рассмотренной далее. Атрибут сохраняет установленное значение до новой установки. Атрибуты устанавливаются следующими функциями прерывания INT ЮН (рассмотренными далее): • 06Н - прокрутка экрана вверх; • 07Н - прокрутка экрана вниз; • 09Н - вывести символы с указанными атрибутами; • 1ЗН - вывести строку символа. Например, следующая последовательность инструкций с помощью функции 09Н прерывания INT ЮН выводит 12 коричневых, мигающих (1110) звездочек (*) на синем (0001) фоне: MOV АН, 09Н /Запросить вывод на экран MOV AL, '* * ;Звездочка MOV ВН,00Н /Страница 0 Глава 9. Видеосистема 217
MOV BL,1EH ;Цветовые атрибуты (0001 1110) MOV CX,12 ;12 символов INT ЮН ; Вызвать обработчик прерывания Можно просмотреть выполнение этих инструкций в DEBUG и попробовать указывать различные атрибуты. Функции прерывания INT 10H Обработчик прерывания INT 10H выполняет множество функций (выбираемых по коду функции в регистре АН), применяющихся для работы с дисплеем. В зависимости от возвращаемых значений обработчик может сохранять значения регистров ВХ, СХ, DX, DI, SI, DS, ES и ВР. Некоторые функции могут устанавливать или считывать отдельные биты и элементы данных в области видеоданных BIOS, описанной в гл. 24, с адресов 0040:пп. Прерывание INT 10H пытается обработать все, что вы ему передаете, и не возвращает сигналов о выполнении или флагов ошибок. Будьте аккуратны и осторожны, передавая коды функций обработчику этого прерывания; вам не удастся нанести системе непоправимый ущерб, но в результате ваших действий экран может стать пустым и темным, и, как следствие, вам придется перезапустить компьютер. Ниже описаны функции прерывания INT ЮН. Функция ООН прерывания INT ЮН: установка видеорежима Эта функция предназначена для установки видеорежима. Загрузите код функции (ООН) в регистр АН и код режима, который хотите установить - в AL. В следующем примере устанавливается стандартный цветной текстовый режим для любого типа цветного монитора: MOV АН,ООН /Запрос на установку видеорежима MOV AL,03H /Стандартный цветной текстовый режим INT 10H /Вызов обработчика прерывания Эта операция не возвращает значений. Она также очищает экран, но это ее действие можно отменить, установив бит 7 режима в 1 и передав в AL соответствующий код с помощью MOV AL,83H. Функция 01Н прерывания INT 10H: установка размера курсора Курсор не является частью набора символов ASCII и существует только в текстовом режиме. Видеоконтроллер управляет положением курсора и его 218 Часть III. Работа с видео и клавиатурой
размером, и размер можно изменять специальной функцией 01Н прерывания INT 10H. Стандартный размер курсора для цветного адаптера VGA - строка 13 для вершины и 14 - для основания, а для монохромного - 6 для вершины и 7 для основания. Для изменения размера курсора по вертикали с помощью функции 01Н установите в регистрах следующие значения: • в СН (биты 4—0) - вершина курсора (начальная закрашенная строка); • в CL (биты А—0) - нижний край курсора (конечная закрашенная строка). В следующем примере курсор увеличивается до максимального размера (0:14): MOV АН,01Н ;Запросить установку размера курсора MOV СН,00 /Начальная строка MOV CL,14 /Конечная строка INT 10H /Вызвать обработчик прерывания Операция не возвращает значений. После ее выполнения курсор примет вид закрашенного мигающего прямоугольника. Его размеры можно изменять в любых допустимых пределах, например, 04:08, 03:10 и т.д. Установка значения 20Н в СХ делает курсор невидимым: MOV СХ,20Н. Курсор сохраняет свои параметры до тех пор, пока они снова не будут изменены. Функция 02Н прерывания INT ЮН: установка положения курсора Эта операция в текстовом режиме устанавливает курсор в произвольное положение на экране согласно координатам в формате строка: столбец (функция 13Н также устанавливает курсор). Установите значения в следующих регистрах: ВН = номер страницы (0 по умолчанию), DH = строка, DL = столбец. В нижеприведенном примере курсор устанавливается в строку 12, столбец 30 на странице 0: MOV АН,02Н /Запросить перемещение курсора MOV ВН,00 /Страница 0 (обычная) MOV DH,12 /Строка 12 MOV DL,3 0 /Столбец 3 0 INT 10H /Вызвать обработчик прерывания Положение курсора на каждой странице не зависит от его положения на других. Операция не возвращает значений. Функция ОЗН прерывания INT ЮН: получение состояния курсора Функция ОЗН может использоваться для определения текущих столбца, колонки и размера курсора в текстовом режиме. Она может использоваться, Глава 9. Видеосистема 219
например, в ситуациях, когда ваша программа должна временно использовать экран, а затем вернуть его в прежнее состояние. Загрузите номер страницы в ВН, так же, как для функции 02Н: MOV АН,ОЗН ;Запросить состояние курсора MOV ВН,00 ; Страница 0 (обычная) INT 10H ;Вызвать обработчик прерывания Операция не изменяет состояния регистров АХ и ВХ и возвращает такие значения: СН = верхняя строка размера курсора CL = нижняя строка размера курсора DH = строка DL = столбец В следующем примере функция ОЗН используется для получения данных о курсоре; после этого функция 02Н используется для перемещения курсора в следующий столбец на экране: MOV АН,ОЗН /Запросить состояние курсора MOV ВН,00 /Страница 0 INT 10H /Вызвать обработчик прерывания MOV АН,02Н /Запросить перемещение курсора INC DL ;в следующий столбец INT 10H /Вызвать обработчик прерывания Функция 05Н прерывания INT ЮН: выбор активной страницы Функция 05Н позволяет выбирать страницу, выводимую на экран в текстовом или графическом режиме. Можно создать несколько различных страниц и переключаться между ними с помощью этой функции. Эта операция - просто запрос, не возвращающий значений: MOV АН,05Н /Выбор функции для ' вывода страницы MOV AL,^страницы /Номер страницы INT 10H /Вызвать обработчик прерывания Функция 06Н прерывания INT ЮН: прокрутка экрана вверх Эта операция в текстовом или графическом режиме прокручивает экран вверх в выбранной области (на активной видеостранице). Выводимые строки прокручиваются вверх за пределы выводимой на экран области, а внизу прокручиваемой области появляются пустые строки. 220 Часть III. Работа с видео и клавиатурой
Эта функция уже использовалась в гл. 8, где установка регистра AL в О вызывала прокрутку всего экрана вверх, очищая его. Установка ненулевого числа в AL вызывает прокрутку этого числа строк вверх. Для выполнения функции загрузите следующие значения в регистры: AL = число строк (00 - для всего экрана) СХ = начальные строка:столбец ВН = атрибут или значение пиксела DX = конечные строка: столбец В следующем примере в текстовом режиме устанавливается атрибут цвета и весь экран прокручивается вверх на одну строку: MOV АХ,601Н /Запросить прокрутку вверх на одну строку (текст) MOV ВН,61Н ;Коричневый фон, синие символы MOV СХ,0000 ;От 00:00 до MOVDX,184FH ; 24:79 (весь экран) INT 10H ;Вызвать обработчик прерывания Операция не возвращает значений. Вот стандартный способ прокрутки одной строки вверх. 1. Для установки строки, в которой находится курсор, определите элемент данных, например, ROW, проинициализировав его значением 0; 2. Выведите строку и переместите курсор на следующую строку; 3. Проверьте, не близка ли ROW к нижнему краю экрана (CMP ROW, 22); 4. Если да, прокрутите одну строку, используйте ROW для установки курсора и очистите ROW в 0; 5. Если нет, увеличьте ROW на 1 (INC ROW). Пара регистров CX:DX позволяет прокручивать любую прямоугольную часть экрана. Будьте внимательны и соразмеряйте значение в AL с размером в CX:DX, особенно когда прокручивается только некоторая часть экрана. В нижеследующем примере создается окно (с отдельными атрибутами) из 7 строк и 30 столбцов, с верхним левым углом в координатах 12:25, верхним правым - в 12:54, нижним левым - в 18:25 и нижним правым - в 18:54: MOV AX,0607H ;Запросить прокрутку вверх на 7 строк (текст) MOV ВН,3 0Н ;Голубой фон, черные символы MOV СХ,0С19Н ;От строки 12, столбца 25 до MOV DX,184FH ; строки 18, столбца 54 (окно) INT 10H ;Вызвать обработчик прерывания В примере прокручиваются 7 строк, то есть все строки с 12 по 18 включительно, так что только эта область очищается. Обычно при создании окна прокручиваются (и очищаются) все его строки, а в дальнейшем - по одной. Поскольку атрибуты окна сохраняют свои значения, пока они не будут изменены, можно устанавливать различные атрибуты для различных окон одновременно. Глава 9. Видеосистема 221
В графическом режиме в ВН вместо атрибутов помещается значение пикселов. Вот пример, устанавливающий строки в красный цвет: MOV AX,060FH /Запросить прокрутку вверх на 15 строк (графика) MOV ВН,0100В /Значение пиксела MOV СХ,0А00Н ;От строки 10, столбца 00 до MOV DX,184FH ; строки 24, столбца 79 INT 10H ;Вызвать обработчик прерывания Функция 07Н прерывания INT ЮН: прокрутка экрана вниз Для текстового и графического режимов прокрутка экрана вниз вызывает сдвиг нижних строк за пределы экрана и появление пустых строк сверху. За исключением того, что экран прокручивается вниз, эта функция работает так же, как и 06Н, рассмотренная ранее. Для выполнения функции загрузите значения в следующие регистры: AL = число строк (00 - для всего экрана) СХ = начальные строка:столбец ВН = атрибут или значение пиксела DX = конечные строка: столбец Функция 08Н прерывания INT ЮН: считывание символа и его атрибутов в положении курсора Функция 08Н считывает как символ, так и его атрибуты из области данных дисплея в текстовом или графическом режимах. Считываемый символ определяется положением курсора. Загрузите номер страницы в ВН, как показано ниже: MOV АН,08Н ;Запросить считывание символа и атрибутов MOV ВН,00 ;Страница 0 (обычная) INT 10H ;Вызвать обработчик прерывания Операция помещает символ в AL, а атрибуты - в АН. В графическом режиме операция возвращает ООН для не-ASCII символов. Поскольку операция считывает только один символ, для чтения последовательности символов придется использовать цикл. Функция 09Н прерывания INT ЮН: вывод символа и атрибутов в положение курсора Эта операция выводит заданное число символов с указанными атрибутами в текстовом или графическом режимах. Положение курсора определяет место вывода символа. Для выполнения функции задайте следующие значения в регистрах: 222 Часть III. Работа с видео и клавиатурой
AL = символ ASCII BL = атрибут или значение пиксела ВН = номер страницы СХ = число символов Число в регистре СХ определяет, сколько раз должна повториться операция вывода символа из AL. В следующем примере в текстовом режиме выводятся 60 "улыбающихся лиц" (ASCII 01H) с установленными атрибутами: MOV АН,09Н ;Запросить вывод (текст) MOV AL,01H ;Выводимый символ MOV ВН,0 ;Страница 0 (обычная) MOV BL,16H ;Синий фон, коричневые символы MOV СХ,60 ;Число выводимых символов INT 10H ;Вызвать обработчик прерывания Операция не перемещает курсор и не реагирует на символы звукового сигнала, перевода строки, возврата каретки и табуляции; вместо этого она попытается вывести их как символы ASCII. Если в текстовом режиме вывод достигает крайнего правого символа, вывод продолжится со столбца 00 в следующей строке. В графическом режиме BL определяет цвет символов. Если бит 7 равен 0, определенный цвет заменяет текущий; если бит 7 равен 1, определенный цвет сочетается (операция XOR) с текущим. В следующем примере выводятся десять сердечек (03Н) в графическом режиме : MOV АН,09Н ;Запросить вывод (графика) MOV AL,03H ;Выводимый символ MOV ВН,00 ;Страница 0 (обычная) MOV BL,04H ;Значение пиксела MOV СХ,60 /Число выводимых символов INT 10H /Вызвать обработчик прерывания Для вывода последовательности различных символов используется функция 13Н. Функция 0AH прерывания INT 10H: вывод символа в положение курсора Единственное различие между этой функцией и функцией 09Н состоит в том, что функция 0AH не устанавливает атрибуты, а использует их текущее значение. Вот пример ее использования: MOV АН,0АН /Запросить вывод MOV AL,символ /Выводимый символ MOV ВВ., № страницы /Страница 0 (обычная) Глава 9. Видеосистема 223
MOV EL,значение ;3начение пиксела (только в графическом ; режиме) MOV СХ,число ;Число выводимых символов INT 10H ;Вызов обработчика прерывания Операция ничего не возвращает. Функция ОВН прерывания INT ЮН: установить цветовую палитру Эта функция используется для установки цветовой палитры в графическом режиме. Значение в регистре ВН (00 или 01) определяет смысл значения в BL: • ВН = 00: выбор цвета фона (BL содержит цвет фона в битах 0—3 (один из 16 возможных)). MOV АН, ОВН MOV ВН,00 MOV BL,04 INT 10H Запрос выбор цвета фона красный цвет (опции - от 00 до 0FH) Вызов обработчика прерывания • ВН = 01: выбор палитры для графики, BL содержит палитру (0 или 1): MOV АН,ОВН ;Запрос MOV ВН,00 ; выбор палитры MOV BL,04 ; номер 0 (зеленый, красный, коричневый) INT 10H ;Вызов обработчика прерывания Операция сохраняет значение цвета в регистре выбора цвета (Color Select) и изменяет значение палитры в области видеоданных BIOS по адресу 40: [66]. Как только палитра выбрана, она сохраняет свое значение, но при изменении палитры весь экран перерисовывается в соответствии с выбранными цветами. Функция 0CH прерывания INT ЮН: нарисовать пиксел Функция 0CH используется для рисования пиксела выбранным цветом (фоновым и палитры) в графическом режиме. Загрузите в регистры значения: AL = цвет пиксела СХ = столбец ВН = номер страницы DX = строка Минимальный номер столбца или строки - 0, максимальный - зависит от выбранного видеорежима. В следующем примере пиксел рисуется на пересечении строки 50 и столбца 200: MOV АН,0CH ;Запросить рисование пиксела MOV AL,03H ;Цвет пиксела 224 Часть III. Работа с видео и клавиатурой
MOV BH,0 ;Страница 0 MOV DX,50 ;Вертикальная координата MOV СХ,200 ;Горизонтальная координата INT 10H /Вызвать обработчик прерывания Функция 0DH прерывания INT ЮН: прочитать пиксел Эта операция, обратная по действию операции ОСН, считывает точку для определения ее цвета. Установите номер страницы в ВН, столбец в СХ и строку в DX. Минимальный номер столбца или строки - 0, максимальный номер зависит от выбранного видеорежима: MOV AH,0DH ;Запросить считывание пиксела MOV ВН,0 ;Страница 0 MOV DX,110 ;Вертикальная координата MOV СХ,80 ;Горизонтальная координата INT 10H ;Вызвать обработчик прерывания Операция помещает прочитанный цвет пиксела в регистр AL. Функция ОЕН прерывания INT ЮН: вывод в режиме телетайпа Эта операция позволяет использовать монитор в качестве простого терми- нала.для вывода текста и графики. Вот пример ее вызова: MOV АН,ОЕН ;Запросить вывод MOV AL,символ ;Выводимый символ. MOV ВЬ,цвет ;Цвет (в графическом режиме) INT 10H /Вызвать обработчик прерывания Символы Backspace (08H), возврата каретки (0DH), перевода строки (ОАН) воспринимаются как команды управления выводом, но табуляция (09Н) не воспринимается. Операция автоматически перемещает курсор, переносит символы на следующую строку, прокручивает экран и сохраняет текущие атрибуты экрана. Операция значений не возвращает. Функция 0FH прерывания INT ЮН: получить текущий видеорежим Эту функцию можно использовать для определения текущего видеорежима. Вот пример: MOV АН,0FH ;Запросить видеорежим INT 10H ;Вызвать обработчик прерывания Глава 9. Видеосистема 225 15 — 5-741
CMP AL,03 ;Если режим 3, то JE ; перейти Программа: вывод на экран таблицы ASCII Программа, приведенная на рис. 8.1, использовала функцию 09Н прерывания INT 21H для вывода набора символов ASCII, но пропускала управляющие символы Backspace, звукового сигнала, возврата каретки и перевода строки. Улучшенная программа, приведенная на рис. 9.2, использует прерывание INT 10H для вывода полной таблицы символов с помощью следующих функций: 0FH: получение и сохранение текущего видеорежима; ООН: установка видеорежима 03 для этой программы и восстановление исходного видеорежима при завершении выполнения; 08Н: считывание атрибута в текущей позиции курсора для использования функцией 06Н; 06Н: прокрутка экрана вверх для очистки всего экрана с использованием только что прочитанных атрибутов, а также создание 16-строчного окна для вывода таблицы с символами коричневого цвета и синим фоном; 02Н: установка курсора в начале вывода и перемещение его при выводе каждого следующего символа; ОАН: вывод каждого символа, включая управляющие, в текущей позиции курсора. TITLE CHAR_CTR COL ROW MODE A10MAIN A20: A09BIOAS (EXE) Вывод .MODEL .STACK .DATA DB DB DB - DB .CODE PROC MOV MOV MOV CALL CALL CALL SMALL 64 00 24 04 • NEAR AX,@DATA DS,AX ES,AX B10MODE C10CLEAR D10CURSOR символов ASCII с помощью INT ЮН Счетчик ASCII-символов Столбцы экрана Строки экрана Видеорежим № Инициализация сегментных - регистров Получение/установка видеорежима Очистка экрана ■Установка курсора Рис. 9.2. Вывод на экран таблицы ASCII с помощью INT 10H 226 Часть III. Работа с видео и клавиатурой
АЗО: AlOMAIN BlOMODE BlOMODE ; C10CLEAR , CALL CMP JE INC ADD CMP JNE INC MOV JMP MOV INT MOV MOV INT MOV INT ENDP E10DISPLY CHAR__CTR,OFFH АЗО Вывод символов на экран Выведен ли последний символ? да, выход CHAR_CTR ;Увеличить значение счетчика символов | COL,02 COL,56 А2 0 ROW COL,24 A2 0 АН, ЮН 16Н АН,ООН AL,MODE ЮН АХ,4С00Н 21Н Получение и сохранение PROC MOV INT MOV MOV MOV INT RET ENDP Увеличить значение столбца Достигнут ли конец столбца? нет, продолжаем да, переходим к новой строке и сбрасываем значение столбца •Запрос на получение символа с клавиатуры ■ Запрос на сброс режима в исходное состояние ■Завершение работы текущего режима, установка нового режима: NEAR AH,0FH ЮН MODE,AL АН,ООН AL, 03 ЮН ;Запрос на получение режима ;Сохранение режима ;Запрос на установку нового режима ;Стандартный цвет Очистка экрана, выделение окна, задание атрибутов: PROC PUSHA MOV INT MOV MOV MOV MOV INT MOV MOV NEAR АН,08Н ЮН ВН,АН АХ,0600Н СХ,0000 DX,184FH ЮН AX,0610H ВНД6Н ;Сохранение значений регистров ;Запрос на получение текущих ; атрибутов в АН ;Перенос их в ВН ;Прокрутка целого экрана ;Верхняя левая ячейка ;Нижняя правая ячейка ;Создание окна с 16 строками ;Синий фон, коричневые символы Рис. 9.2 (Продолжение) Глава 9. Видеосистема 15* 227
C10CLEAR / DIOCURSOR D10CURSOR ; ElODISPLY E10DISPLY MOV MOV INT POPA RET ENDP CX,0418H DX,1336H 10H Координаты верхнего левого угла - 04:24 Координаты нижнего правого угла - 19:54 ■Восстановление значений регистров ' ' Позиционирование курсора в заданные строку и столбец: PROC PUSHA MOV MOV MOV MOV INT РОРА RET ENDP Вывод PROC PUSHA MOV MOV MOV MOV INT POPA RET ENDP END NEAR AH,02H BH,00 DH,ROW DL,COL 10H •Сохранение значений регистров •Запрос на установку курсора •Страница 0 (стандартная) •Новая строка •Новый столбец ;Восстановление значений регистров ASCII-символов по одному за раз: NEAR AH,0AH AL,CHAR_CTR BH,00 CX,01 10H A10MAIN ;Сохранение значений регистров ;Запрос на вывод на экран ;ASCII-символ ;Страница 0 ;Один символ /Восстановление значений регистров Рис. 9.2 (Окончание) Символы выводятся в области размером 16 строк и 16 столбцов. Эта программа, как и другие программы, написана с целью обучения, а не получения максимальной скорости ее выполнения. Можно переделать программу с целью ускорения выполнения, например, используя регистры для хранения символа, номера строки и номера столбца. Кроме того, поскольку INT 10H использует только регистр АХ, не нужно сохранять значения из других регистров. Однако это существенно не ускорит выполнения программы - на современных компьютерах она выполнится мгновенно (с точки зрения человека) - и затруднит ее понимание. 228 Часть III. Работа с видео и клавиатурой
Функция ЮН прерывания INT ЮН: работа с регистрами палитры Эта операция содержит ряд функций, связанных с чтением и изменением значений в регистрах палитры, регистре границы (Overscan) и видео-ЦАП. Загрузите код функции в AL для^запроса необходимого действия. Подфункция ООН: обновить регистр палитры Для изменения цвета, выводимого любым из 16 регистров палитры, загрузите значение цвета в регистр ВН, а номер требуемого регистра (ООН—0FH) - в BL: MOV AX,1000H /Запросить обновление палитрового регистра MOV ВН,02 ;Новый цвет (зеленый) MOV BL,01 ;Регистр палитры INT 10H ;Вызвать обработчик прерывания Подфункция 01Н: обновить цвет рамки По умолчанию рамка вокруг экрана имеет черный цвет. Для изменения этого цвета загрузите новый цвет в ВН и вызовите данную операцию: MOV АХД001Н ; Запросить обновление цвета рамки MOV ВН,02 ;Новый цвет (зеленый) INT ЮН ;Вызвать обработчик прерывания Подфункция ОЗН: установить интенсивность фона Эта операция позволяет вам включать и отключать атрибут мерцания. Загрузите код в BL (ООН - отключить мерцание, 01Н - включить мерцание). Операция обращается к регистру управления режимом в контроллере атрибутов (Attribute Controller's Mode Control Register). Если мерцание отключено, то для фонового цвета доступны все 16 регистров вместо 8: MOV АХД003Н ;Запросить установку интенсивности цвета фона MOV BL,00H ;Отключить мерцание INT 10H ;Вызвать обработчик прерывания Подфункция 07Н: прочитать регистр палитры Эта операция возвращает код цвета, сохраненный в выбранном регистре палитры. Загрузите номер требуемого регистра в BL; MOV AX,1007H MOV Bh,регистр INT 10H Запросить код цвета из регистра палитры Вызвать обработчик прерывания Операция помещает код в регистр ВН. Для чтения всей палитры используется подфункция 09Н. Глава 9. Видеосистема 229
Подфункция 08Н: прочитать регистр рамки (Overscan) Эта операция возвращает код цвета, сохраненный в регистре рамки: MOV AX,1008H ;Прочитать содержимое регистра рамки INT 10H ;Вызвать обработчик прерывания Операция помещает код в регистр ВН. Подфункция 09Н: прочитать таблицу значений регистров палитры Эта операция возвращает коды цветов, сохраненных в регистрах палитры и регистре рамки, записывая их в 17-байтовую таблицу. Вы определяете таблицу и загружаете ее адрес в ES:DX. В приведенном ниже примере подразумевается, что соответствующий адрес сегмента уже хранится в ES: REGTABLE DB 17 DUP(?) ;17-байтовая таблица MOV АХД009Н ;Поместить значения палитры регистров LEA DX,REGTABLE ; в таблицу по адресу (ES:DX) INT ЮН ;Вызвать обработчик прерывания Операция помещает содержимое регистров палитры в первые 16 байт, а регистра рамки - в семнадцатый. Типичное содержимое этой таблицы по умолчанию - 00 01 02 03 04 05 14 07 38 39 ЗА ЗВ ЗС 3D ЗЕ 3F 00. Последнее значение, 00 (черный цвет) - это цвет рамки. Подфункция ЮН: обновить значения регистров цветов ЦАП Видео-ЦАП (цифро-аналоговый преобразователь) содержит 256 трехбайтных регистров цвета: 00—0FH Диапазон стандартных цветов CGA 10—1FH Оттенки серого нарастающей интенсивности 20—67Н Первая из трех групп: синий, красный и зеленый цвета высокой насыщенности 68—AFH Вторая группа: синий, красный и зеленый цвета ВО—F7H Последняя группа: синий, красный и зеленый цвета низкой насыщенности F8—FFH Черный * Три группы регистров с номерами, начинающимися с 20, 68 и ВО, состоят из трех диапазонов цветов уменьшающейся степени насыщенности. Подфункция ЮН позволяет загружать в регистры ЦАП новые значения: 230 Часть III. Работа с видео и клавиатурой
MOV AX,1010H MOV BH,регистр MOV CH,зеленый MOV CL,синий MOV DH, кра сный INT 10H Обновление регистров цветов ЦАП Номер регистра ЦАП Значения для зеленого, синего и красного Вызвать обработчик прерывания Операция может изменять только младшие 6 битов кода цвета. Подфункция 12Н: обновить блок регистров ЦАП Эта операция позволяет изменить содержимое нескольких регистров ЦАП, загрузив в них новые значения. Для этого необходимо определить в памяти таблицу цветов (три байта на регистр дня красного, зеленого и синего) и переслать ее адрес в ES:DX. В следующем примере подразумевается, что ES содержит соответствующий адрес сегмента: DACTABLE DB nn DUP(?) ;Таблица (3 байта на регистр) / ... MOV AX,1012H ;Изменение содержимого блока ; регистров ЦАП MOV ВХ,регистр ;Первый регистр в блоке MOV СХ, количество ;Число регистров, подлежащих изменению LEA DX,DACTABLE ; согласно таблице по адресу (ES:DX) INT ЮН ;Вызвать обработчик прерывания Подфункция 15Н: прочитать значение регистра цвета ЦАП Эта подфункция позволяет прочитать значение из любого регистра цвета ЦАП: MOV АХ,1015Н ;Чтение из регистра цвета ЦАП MOV BX,регистр ;Номер регистра ЦАП INT ЮН / ;Вызвать обработчик прерывания Операция помещает зеленый компонент в СН, синий - в CL, и красный - в DH. Подфункция 17Н: обновить значения регистров цветов ЦАП Эта операция позволяет поместить содержимое нескольких регистров ЦАП (до 256) в таблицу (3 байта на регистр для красного, синего и зеленого). Поместите адрес таблицы в ES:DX. В следующем примере предполагается, что ES уже содержит соответствующий адрес сегмента: DACTABLE DB nn DUP(?) ;Таблица (3 байта на регистр) MOV АХ,1012Н ;Чтение содержимого блока" ; регистров ЦАП Глава 9. Видеосистема 231
MOV BX,регистр MOV CX,количество LEA DX,DACTABLE INT ЮН Первый регистр в блоке Число регистров, из которых читаем в таблицу по адресу (ES:DX) Вызвать обработчик прерывания Возвращенные значения будут выглядеть, например, так: 00 00 00 (регистр 0), 00 00 2А (регистр 1), 00 2А 00 (регистр 2) и т.д. Подфункция 1ВН: перевести регистры ЦАП в режим шкалы серого Эта операция позволяет перевести регистры ЦАП в режим шкалы серого: MOV АХ,101ВН ;Перевести в шкалу серого MOV ВХ, регистр ;Цервый регистр в блоке MOV CX,количество ;Число регистров, подлежащих изменению INT 10H ;Вызвать обработчик прерывания Попробуйте выполнить этот пример, например, с ВХ = 0 и СХ = ЮН, чтобы уяснить эффект данного действия. Функция 11Н прерывания INT 10H: работа со знакогенератором Эта функция содержит несколько подфункций, применяющихся в работе со знакогенератором в текстовом и графическом режимах. Эти подфункции можно использовать для изменения размера и формы выводимых символов. В приведенной ниже таблице показаны 12 взаимосвязанных подфункций: Определенная поль зователем таблица Символы размера 8x14 Символы размера 8x8 Символы размера 8x16 Загрузить текстовые символы Загрузить текстовые символы и запрограммировать видеоконтроллер Загрузить графические символы ООН 01Н 02Н 04Н ЮН 11Н 12Н 14Н 21Н 22Н 23Н 24Н Подфункции ООН, ЮН и 21Н Эти подфункции используются при работе с определенными пользователем таблицами символов и в этой книге не рассматриваются. 232 Часть III. Работа с видео и клавиатурой
Подфункции 01Н, 02Н и 04Н Эти подфункции используются при работе с текстовыми символами трех предопределенных размеров. Подфункции 11Н, 12Н и 14Н (по умолчанию) Эти подфункции аналогичны, соответственно, ООН, 01Н и 04Н, но они, кроме того, перепрограммируют видеоконтроллер для управления высотой матрицы пикселов символа. Для вызова этих подфункций загрузите соответствующий код в BL. В следующем примере знакогенератор загружает текстовые символы с матрицей размером 8x8: MOV АХД112Н ;3агрузить символы 8x8 MOV BL,0 ;Коды таблицы- 0-7 INT 10H ;Вызвать обработчик прерывания Эта операция вызывает переход дисплея в режим 43 строк символов с матрицей 8x8. Подфункции 22Н, 23Н и 24Н Эти операции поддерживают графические символы. Для них значение в регистре BL указывает число строк символов на экране, где: 1-14 строк, 2 - 25 строк, 3-43 строки, и 0 указывает, что в DL - число символов в строке. MOV АХД123Н ; Загрузить графические символы 8><8 MOV BL,код ; 0, 1, 2 или 3 MOV DL,число ;Число символов в строке, если BL = 0 INT 10H ;Вызвать обработчик прерывания Подфункция ОЗН: выбор таблицы определения символов Загрузите в BL байт, биты которого определяют, какая из таблиц будет загружена: • Если бит 3 равен 0, то биты 0, 1 и 4 определяют использование одной из восьми 256-символьных таблиц. • Если бит 3 равен 1, то биты 2, 3 и 5 указывают, какую таблицу использовать. Подфункция ЗОН: чтение данных из знакогенератора Для получения текущих данных о знакогенераторе загрузите в ВН один из следующих кодов: 0 - содержимое вектора INT 1FH; 1 - содержимое вектора INT 43H; 2 - адрес в BIOS таблицы символов 8x14; 3 - адрес в BIOS первой половины таблицы символов 8x8; Глава 9. Видеосистема 233
4 - адрес в BIOS второй половины таблицы символов 8><8; 5 - адрес в BIOS альтернативной таблицы символов 9x14; 6 - адрес в BIOS таблицы символов 8x16; 7 - адрес в BIOS альтернативной таблицы символов 9x16. Операция возвращает следующие значения: СХ - высота пиксельной матрицы символов, DL - число строк - 1 (обычно 18Н), и ES:BP - адрес таблицы определения символов (Character Definition Table). Можно использовать DEBUG в текстовом режиме для просмотра результатов каждой разновидности операции, например, так: MOV АХ,1130Н ;Запросить информацию из знакогенератора MOV ВН,0 ;Код 0, 1, 2, ..., 7 INT 10H ;Вызвать обработчик прерывания INC BH ;Следующий код ЛУ1Р 100 ; повторить Используйте команду D программы DEBUG для просмотра значений, помещенных в регистры. Функция 12Н прерывания INT 10H: дополнительные функции управления видеоадаптером Эта операция также включает набор подфункций, выбираемых согласно коду, содержащемуся в регистре В. • Подфункция ЮН: получение информации о видеоадаптере. Возвращаемая информация: ВН - класс видеоадаптера (0 - цветной, 1 - монохромный); BL - объем видеопамяти адаптера (0 - 64 К, 1 - 128 К, 2 - 192К, 3 - 256 К или больше); СН - биты адаптера; CL - установка переключателя конфигурации. • Подфункция ЗОН: выбор числа строк в текстовом режиме. Эта операция используется для изменения вертикального разрешения (числа строк) в текстовом режиме (режимы 0, 1, 2, 3 и 7). Загрузите в AL число строк: 0 - 200, 1 - 350 и 2 - 400. После использования этой операции для установки режима воспользуйтесь функцией 03Н прерывания INT 10H. MOV АХД202Н ;Задать разрешение в 400 строк по вертикали MOV BL,3 0H 234 Часть III. Работа с видео и клавиатурой
INT ЮН ;Вызвать обработчик прерывания MOV АХ,0003Н ;Установить текстовый режим 03 (720x400) INT 10H ;Вызвать обработчик прерывания • Другие подфункции позволяют работать со шкалой серого, эмулировать курсор и управлять обновлением экрана. Функция 13Н прерывания INT ЮН: вывод строки символов Эта мощная функция выводит в текстовом или графическом режимах строку символов любой длины с возможностями установки атрибутов и перемещения курсора. Загрузите в пару ES:BP адрес строки в форме сегмент:смещение. Операция корректно обрабатывает символы Backspace, звукового сигнала, перевода строки и возврата каретки, но не воспринимает табуляцию. MOV АН,13Н ;Запрос на вывод строки MOV AL, подфункция ;00, 01, 02 или 03 (см. ниже) MOV ВН,^страницы ;Номер страницы в памяти MOV ВЬ,атрибуты ;Атрибуты экрана LEA BP,адрес ;Адрес первого символа в памяти MOV CX,длина ;Длина строки MOV DH,строка ;Номер строки на экране • MOV Db, столбец ;Номер столбца на экране INT 10H ;Вызов обработчика прерывания Четыре подфункции, выбираемые по значению в регистре AL: 00 - вывод строки и атрибутов без перемещения курсора; 01 - вывод строки и атрибутов с перемещением курсора; 02 - вывод строки, а после - атрибутов; курсор неподвижен; 03 - вывод строки, а после - атрибутов; курсор перемещается. Подфункции 02 и 03 требуют, чтобы за каждым символом следовал байт атрибутов. Они производят вывод со смешанными атрибутами прямо из области видеоданных. В графических режимах установка бита 7 регистра BL в 1 вызывает проведение операции XOR над строковым значением и значением в области видеопамяти. Программа: установка атрибутов и прокрутка Программа, приведенная на рис. 9.3, воспринимает вводимые с клавиатуры имена и выводит их на экран. Глава 9. Видеосистема 235
TITLE PARLIST MAX_LEN ACT_LEN KB_NAME LEFT_COL BOTT_SCRN ATTRIB COL ROW PROMPT 7Я6 * • A10MAIN A20: A30: • A10MAIN ; BIOPROMPT A09NMSCR (EXE) Установка атрибутов и прокрутка .MODEL .STACK .DATA LABEL DB DB DB EQU EQU DB DB DB DB .CODE PROC MOV MOV MOV MOV MOV INT MOV CALL MOV CALL CALL CMP JE CALL JMP MOV CALL MOV INT ENDP PROC MOV LEA SMALL 64 BYTE 20 ? 20 DUP(■ ■ ) 51 23 00 05 00 'Name? ' FAR AX,©data DS,AX ES,AX АХД003Н BL,00 *10H AL,00H Q10SCROLL COL,05 B10PROMPT C10INPUT ACT_LEN,00 A3 0 D10NAME A2 0 AL,00H Q10SCROLL AX,4C00H 21H Вывод приглашены NEAR ATTRIB,71H BP,PROMPT Список параметров: максимальная длина имени количество введенных символов имя Крайний слева выводимый столбец Крайняя снизу выводимая строка Атрибуты экрана Столбец и строка экрана Приглашение к вводу Инициализация сегментных регистров •Отключить атрибут мерцания •Запросить очистку всего экрана •Установить столбец 05 •Вывести приглашение •Организовать ввод имени ;Был ли ввод? нет, выход ;Вывести имя ;Выход, ; очистка экрана ; и конец работы я для пользователя: ;Используются ВР и СХ ;Установить атрибуты ;Указать адрес приглашения Рис. 9.3. Прокрутка и инвертирование изображения 236 Часть III. Работа с видео и клавиатурой
BIOPROMPT '' CIOINPUT CIOINPUT D10NAME ' D30: D90: D10NAME / ElODISPLY MOV CALL RET ENDP PROC MOV LEA INT RET ENDP PROC PUSHA MOV SUB ADD MOV MOV LEA MOVZX CALL CMP JAE INC JMP MOV CALL POPA RET ENDP PROC PUSHA MOV MOV MOV MOV MOV MOV CX,06 ElODISPLY и его длину Вывести его на экран Ввод имени с клавиатуры NEAR АН,0АН ; DX,PARLIST 21Н Подготовиться к прокрутку, если NEAR AL,MAX_LEN AL,ACT_LEN AL,LEFT_COL COL,AL ATTRIB,17H BP,KB_NAME CX,ACT_LEN ElODISPLY ROW,BOTT_SCRN D3 0 ROW D90 AL,01H Q10SCROLL Вывод символов и NEAR ;BP, С: ; Coxpaj AH,13H ;3anpo AL,01 ; си: BH,00 ;Номер BL,ATTRIB ;Атриб^ DH,ROW ;Строк DL,COL ;Столб Используются АН и DX Запросить ввод с клавиатуры Ввод выводу имени, выполнить близок нижний край экрана Сохранение значений регистров Вычислить величину отступа, прибавить ее к номеру столбца, и сохранить Инвертировать изображение Проинициализировать регистр и записать имя Вывести имя на экран Близок ли край экрана? да, продолжать выполнение нет, перейти к следующей строке и выйти Прокрутить экран на одну строку Восстановление значений регистров установка атрибутов: К устанавливаются при входе с^ение значений регистров зить вывод на экран УШОЛОВ страницы /ты а экрана эц экрана Рис. 9.3 (Продолжение) Глава 9. Видеосистема 237
РОРА E10DISPLY ; Q10SCROLL Q10SCROLL INT RET ENDP PROC PUSHA MOV MOV- MOV MOV INT POPA RET ENDP END 10H ;Восстановление значений регистров Прокрутка NEAR АН,06Н ВН,86Н СХ,0000 DX,184FH ЮН A10MAIN экрана и установка атрибутов: ;AL устанавливаются при входе ;Сохранение значений регистров /Запрос на прокрутку ;Серый, коричневый ;Весь экран ;Восстановление значений регистров Рис. 9.3 (Окончание) Для повышения выразительности отображаемой информации программа выводит приглашение в инверсном режиме (синие буквы на белом фоне), воспринимает имя и отображает его в нормальном режиме (белые буквы на синем фоне), а затем выводит имя таким образом, что оно выравнивается по правому краю (столбец 70) в той же строке в инверсном режиме. Вот этот формат: Name? Mark Twain ... Mark Twain Столбец 0 Столбец 70 Программа состоит из следующих процедур. A10MAIN содержит основные операции управления для ввода любого числа имен с клавиатуры и использует функцию ЮН прерывания INT 10H для блокирования атрибута мерцания. B10PROMPT выводит приглашение пользователю ввести имя. C10INPUT использует функцию ОАН прерывания INT 21Н для восприятия ввода с клавиатуры. D10NAME вычисляет начальный столбец вьиюда так, что при выводе имя выравнивается по правому краю. После этого вызывается процедура E10DISPLY для вывода в последующих строках вплоть до строки 23, после чего начинается построчная прокрутка экрана при вводе каждого нового имени. E10DISPLY использует функцию 13Н прерывания INT ЮН для установки курсора и вывода введенного имени. 238 Часть III. Работа с видео и клавиатурой
• Q10SCROLL управляет прокруткой экрана и устанавливает фон в серый цвет (1000В), поскольку мерцание отключено. Функция 1АН прерывания INT ЮН: сочетания дисплеев Эта операция содержит ряд подфункций, предназначенных для одновременной работы с различными типами мониторов (например, цветными и монохромными). Здесь эти функции не рассматриваются. Функция 1ВН прерывания INT ЮН: получить информацию о видео-BIOS Для использования этой операции нужно определить 64-байтовый буфер для динамической информации и поместить его адрес в пару ES:DI. В DEBUG воспользуйтесь такой программой: MOV АН,1В ;Запрос информации о видео-BIOS MOV ВХ,0 ;0 для типа реализации MOV DI,110 ; Смещение для динамической информации (ES:DI) INT 10 ;Вызов обработчика прерывания JMP 100 При выполнении этот пример возвращает следующую динамическую информацию (в этом примере - по адресу ES:110): Смещение Размер Описание ООН Двойное Адрес таблицы Static Functionality слово Текущий видеорежим Число столбцов на экране (5000Н - 80 столбцов) Текущий размер видеобуфера (00 ЮН - 4096 байт) Текущий адрес начала видеобуфера 16-байтовый массив для хранения положений курсора на восьми страницах (столбецхтрока) Конечная линия курсора Начальная линия курсора Текущая видеостраница Порт для регистра адреса CRTC 239 04Н 05Н * 07Н 09Н ОВН 1ВН 1СН 1DH 1ЕН Глава 9. Байт Слово Слово Слово Массив Байт Байт Байт Слово Видеосистема
20Н 21Н 22Н 23Н 25Н 27Н 29Н 2АН 2ВН Байт Байт Байт Слово Байты Слово Байт Байт Байты 2DH 31Н 32Н Байт Байт Байт Текущая установка в регистре видеорежима Текущая цветовая палитра Текущее число строк на экране (19Н - 25 строк) Высота символа в линиях (1000Н - 16 линий) Кодовые комбинации активного и неактивного дисплеев Число выводимых в данный момент цветов Число видеостраниц Число линий (0 - 200, 1 - 350, 2 - 400, 3 -480) Таблицы текстовых символов для нормальной и повышенной яркости Биты 0—5 указывают включенные/отключенные состояния Доступная видеопамять (3 - 25 6К и более) Биты 0—5 указывают активные/неактивные состояния Адрес в начале динамической таблицы указывает на 16-байтовую таблицу Static Fuctionality table. Например, значение 8839 00С0 означает смещение 3988Н и адрес сегмента C000H, и в DEBUG можно просмотреть эту таблицу командой D С000:3988. Смещение Размер Описание ООН 01Н 02Н 07Н Байт Байт Байт Байт Бит 0-1 означает поддержку режима ООН и так далее Бит 0-1 означает поддержку режима 08Н и так далее Бит 0-1 означает поддержку режима ЮН и так далее Бит равен 1, если число линий поддерживается в текстовом режиме (бит 0 - для 200 линий, 1 - для 350, 2-для 400) Максимальное количество поддерживаемых наборов текстовых символов Число таблиц определения текстовых символов Биты 0—3 указывают на наличие различных возможностей видеоадаптера 08Н 09Н 0AH Байт Байт Байт 240 Часть III. Работа с видео и клавиатурой
Функция 1СН прерывания INT ЮН: сохранение/восстановление видеосостояния В этой операции содержатся три подфункции, возвращающие размер буфера, сохранение запрошенных состояний и их восстановление. Здесь они не рассматриваются. Использование графических режимов __ В графическом режиме для генерации изображений используются цветные точки - пиксели (от picture element - элемент изображения). Ранее уже говорилось, что атрибут в текстовом режиме состоит из четырех битов, определяющих фон, и четырех, определяющих цвет символов. Видеосистема воспринимает пиксели таким же образом, но, в зависимости от режима, один пиксель может соответствовать одному, двум, четырем или восьми битам. Перевод дисплея в графический режим вызывает исчезновение курсора с экрана, хотя к нему можно продолжать обращаться. В приведенной ниже таблице описаны основные графические режимы. Режим 04Н 05Н ОбН 0DH ОЕН 0FH ЮН 11Н 12Н 13Н Тип Цветной Цветной Цветной Цветной Цветной Монохромный Цветной Цветной Цветной Цветной Область памяти В800 В800 В800 В800 А000 А000 А000 А000 А000 А000 Число страниц 8 8 8 8 4 2 2 1 1 1 Разрешение 320x200 320x200 640x200 320x200 640x200 640x350 640x350 640x480 640x480 320x200 Число цветов 4 4 2 16 16 1 16 2 16 256 • Графические режимы 04Н и 05Н: изначально разработаны для CGA, но в целях сохранения совместимости поддерживаются и VGA-картами. Один байт соответствует четырем пикселям (два бита на пиксель). Два бита могут представить 22, т.е. четыре цвета. • Графический режим ОбН: изначально разработаны для CGA, но в целях сохранения совместимости поддерживаются и VGA-картами. Один байт Глава 9. Видеосистема 241 16 — 5-741
соответствует восьми пикселям (один^бит на пиксель). Один бит может представить 21, т.е. два цвета. • Графические режимы 0DH, ОЕН и ЮН: изначально разработаны для EGA, но в целях сохранения совместимости поддерживаются и VGA-картами. В 16-цветном графическом режиме каждый символ содержится в матрице пикселей 8x8. Область видеопамяти представляет такой символ 32 байтами (4 бита на пиксель х 8 х 8 = 256 бит или 32 байта). • Графический режим 0FH. Монохромный графический режим EGA, также поддерживаемый VGA для сохранения совместимости. • Графические режимы ПН и 12Н: специально разработаны для VGA. Эти режимы в использовании аналогичны режимам 0DH и ОЕН. В режиме ПН используется только один бит для представления одного пикселя. Однако, хотя режим ПН имеет доступ только к двум цветам палитры, режим 12Н имеет доступ к 16 цветам. • Графический режим 13Н: специально разработан для VGA. Этот режим использует один байт (8 битов) для представления одного пикселя в памяти. Это позволяет присваивать пикселю один из 2 , т.е. 256 цветов. Для установки графического режима используется функция ООН прерывания INT 10H, как показано в следующем примере: MOV АН,ООН ;Запрос на установку видеорежима MOV AL,0EH ;Цветная графика INT 10H ;Вызов обработчика прерывания В графическом режиме ПЗУ содержит матрицы пикселей только для первых 128 символов таблицы ASCII. Прерывание INT 1FH предоставляет доступ к 1 Кбайт области памяти, в которой определяются оставшиеся 128 символов - по 8 байт на символ. Программа: использование функций в графическом режиме Программа, приведенная на рис. 9.4, использует в графическом режиме следующие функции: • 0FH - для получения и сохранения графического режима, использовавшегося до начала выполнения программы; • ООН - для установки графического режима 12Н; • ОВН - для запроса цветной палитры; • 06Н - для прокрутки экрана и установки пяти строк в желтый цвет; • 13Н - для установки курсора и атрибутов и для вывода строки символов; 242 Часть III. Работа с видео и клавиатурой
• О АН - для многократного вывода символов. В конце программа ожидает нажатия клавиши, после чего восстанавливает исходный режим дисплея и завершается. TITLE STRING .286 A10MAIN A10MAIN / B10MODE B10MODE / | CI0SCROLL A09GRFX1 (EXE) Графические функции вывода .MODEL .STACK .DATA DB .CODE PROC MOV MOV MOV MOV INT PUSH CALL CALL CALL CALL MOV INT POP MOV INT MOV INT ENDP PROC MOV MOV INT MOV MOV MOV INT RET ENDP PROC SMALL 64 •1234567890' FAR AX,©data DS,AX ES,AX AH,0FH 10H AX B10MODE CIOSCROLL D10STRING E10DISPLY AH,10H 16H AX АН,ООН юн AX,4C00H 21H Установка графич NEAR АН,ООН AL,12H ЮН AH,0BH ВН,00 BL,07H ЮН Прокрутка экрана NEAR • Подготовка адресации Получение исходного видеорежима и его сохранение Установка видеорежима Прокрутка экрана Вывод функцией 13Н •Вывод функцией ОАН Запрос на ввод с клавиатуры •Восстановление исходного видеорежима (в AL) •Конец работы еского режима и запрос палитры: •Использует АХ и ВХ •Запросить графический режим с 640 столбцами и 480 строками •Запросить палитру •Фон • Серый и установка атрибутов: Рис. 9.4. Графические функции вывода на экран Глава 9. Видеосистема 16* 243
PUSHA MOV MOV MOV MOV INT POPA RET CIOSCROLL ENDP \ D10STRING PROC PUSHA MOV MOV LEA MOV MOV INT POPA RET D10STRING / E10DISPLY PUSHA MOV MOV MOV MOV INT POPA RET E10DISPLY END AX,0605H ВНД110В CX,0000H DX,044FH 10H Вывод строки, NEAR АХД301Н BX,00021H BP,STRING CX,10 DX,0815H 10H ENDP Многократный ;Сохранение значений регистров /Запросить прокрутку: 5 строк ;Желтый цвет ;Из строки:столбца ;В строку:столбец ;Восстановление значений регистров установка атрибутов и курсора: ;Сохранение значений регистров ;Запросить вывод на экран ;Страница и атрибут ;Строка символов ;Ее длина ;Строкагстолбец ;Восстановление значений регистров вывод символов PROC NEAR AX,0A01H BH,00 BL,0100B CX,10 10H ENDP A10MAIN ;Сохранение значений регистров ;Запрос на вывод ;Символы ;Красный цвет ;10 повторений ; /Восстановление значений регистров Рис. 9.4 (Окончание) Пример пикселя В качестве простого примера рассмотрим режим 04Н, обеспечивающий отображение 320 пикселей в каждой из 200 линий развертки экрана дисплея. В этом режиме байт соответствует четырем пикселям (по два бита на пиксель). Эти пиксели пронумерованы от 0 до 3, как показано ниже: С1 СО С1 СО С1 СО С1 СО пиксель: 0 1 2 3 244 Часть III. Работа с видео и клавиатурой
Одновременно для отображения доступны до четырех цветов, пронумерованных от 0 до 3. Количество цветов ограничено четырьмя, поскольку два бита могут представлять четыре различных значения -00, 01, 10 и 11. Пиксель 00, соответствующий цвету фона, может быть любого из 16 цветов. Цвет Черный Голубой Зеленый Синий Красный Пурпурный Коричневый Светло-серый 0000 0001 0010 ООП 0100 0101 ОНО 0111 Цвет Серый Светло-голубой Светло-зеленый Светло-синий Светло-красный Светло-пурпурный Желтый Белый 1000 1001 1010 1011 1100 1101 1110 1111 Режим 04Н предоставляет палитру 0 с зеленым, коричневым и красным цветами символов и палитру 1 с голубым, пурпурным и белым цветами символов. Используйте функцию 0BH прерывания INT 10H для выбора цветовой палитры и фона. Если выбрать цветом фона желтый и палитру 0, то доступны будут желтый, зеленый, красный и коричневый цвета. Байт, содержащий двоичное значение 10101010, соответствует четырем красным пикселям. Если выбрать синий цвет фона и палитру 1, будут доступны синий, голубой, пурпурный и белый цвета. Байт, содержащий 00011011, соответствует четырем пикселям, представляющим все эти цвета. Программа: вывод графических пикселей Программа на рисунке 9.5 использует следующие функции прерывания INT 10H для работы с графикой: 0FH - получить исходный видеорежим; ООН - установить графический режим 12Н; 0BH - выбрать зеленый цвет фона; 0CH - записать пиксели в 640 столбцах и 480 строках/ В действительности выводимое окно имеет размер 210 строк и 512 столбцов (столбцы с 64 по 576). Заметьте, что координаты по столбцам и колонкам измеряются в пикселях, а не в символах. Программа увеличивает значение цвета для каждой строки (так, что цвет 0000 становится 0001 и т.д.) и, поскольку используются только младшие 4 бита, цвета повторяются каждые 16 строк. Вывод начинается с 64-го столбца слева и заканчивается на 64-м столбце справа. Глава 9. Видеосистема 245
В конце программа ожидает нажатия клавиши и восстанавливает исходный графический режим. Программу можно модифицировать для работы в других графических режимах. TITLE трс . Z 00 , A10MAIN A10MAIN ' B10MODE B10MODE C10DISPLY A09GRFX2(EXE) Вывод пикселей| .MODEL .STACK .CODE PROC MOV MOV MOV MOV INT PUSH CALL CALL MOV INT POP MOV INT MOV INT ENDP PROC MOV INT MOV MOV INT RET ENDP PROC PUSHA MOV MOV MOV SMALL 64 FAR AX,@data DS,AX ES,AX AH,0FH 10H AX B10MODE C10DISPLY АН,10Н 16H AX АН,ООН ЮН AX,4C00H 21H Установка NEAR AX,0012H 10H AH, OBH BX,0007H 10H Вывод 210 / < графич строк с 512 столбцами, NEAR BX,00 CX,64 DX,70 Подготовка адресации Получение исходного видеорежима и его сохранение Установка видеорежима Вывод цветной графики Запрос ввода с клавиатуры Восстановление исходного видеорежима (в AL) Завершение работы еского режима и палитры: •Используются АХ и ВХ •Установка графического режима 640 столбцов и 480 строк ;Запрос цветной палитры ;Серый фон - точек в графическом режиме, с изменением цвета каждой строки: ;Сохранение значений регистров ;Установка страницы, ; столбца ; и строки Рис. 9.5. Вывод пикселей в графическом режиме 246 Часть III. Работа с видео и клавиатурой
С20: C10DISPLY MOV MOV INT INC CMP JNE MOV INC INC CMP JNE POPA RET ENDP END AH,OCH AL,BL 10H CX CX,576 C20 CX, 64 BL DX DX,280 C20 A10MAIN ;Запросить пиксель ;Цвет ;Значения ВХ, CX, & DX сохраняются /Перейти к следующему столбцу /Достигли столбца 576? ; нет, повторяем ; да, сбрасываем столбец в 0 /Изменяем цвет /Переходим к следующей строке /Достигли строки 2 80? / нет, повторяем /Восстановление значений регистров /Конец Рис. 9.5 (Окончание) Непосредственный вывод на экран Поскольку обмен с дисплеем выполняется с помощью BIOS и операционной системы, для некоторых приложений это может существенно замедлить работу. Ускорить вывод графики и текста можно, записывая их напрямую в видеопамять. Например, адрес страницы 0 видеопамяти для режима 03 (цветной текст) - В800[0]Н. Каждый символ занимает 2 байта памяти: один - собственно, для символа, и один - для его атрибутов. При размере области вывода на экране в 25 строк по 80 символов размер страницы в видеопамяти равен 80*25*2=4000 байтов. Первые 2 байта в области видеопамяти соответствуют символу, расположенному на экране в левом верхнем углу (столбец 0, строка 0), а байты со смещениями F9EH и F9FH соответствуют правому нижнему угловому символу (столбец 79, строка 24). Запись символа и его атрибутов в видеопамять вызывает его немедленное отображение на экране. Для проверки справедливости этого утверждения можно использовать программу DEBUG. Сначала воспользуйтесь командой D В800:00 для просмотра содержимого видеопамяти по адресу В800[0]Н. В памяти будет находиться текст, выведенный на экран в момент, когда вы ввели команду - обычно это набор байт, содержащих 20 07Н (пробелы с черным фоном и белым цветом символов). Заметьте, что и вы, и сам DEBUG обращаетесь к одной и той же области памяти. Попробуйте выводить различные символы (например, сердечки (03Н)) с различными атрибутами (например, 25, 36 или 47) в верхней и нижней строках: Глава 9. Видеосистема 247
Е В800:000 01 25 02 36 03 47 Е B800-.F90 01 25 02 36 03 47 Программа, приведенная на рис. 9.6, - это пример прямой записи данных в видеопамять по адресу В900[0]Н - то есть в страницу 1, а не 0. Программа использует директиву SEGMENT AT для определения области видеоданных VroEO_SEG, фактически как фиктивный сегмент. VEDAREA определяет положение страницы 1 в начале сегмента. Программа выводит символы в строках с 5 по 20 в столбцах с 10 по 69. Первая строка содержит цепочку символов А (41Н) с атрибутом 01Н, вторая - символов В (42Н) с атрибутом 02Н, и т.д. - с последовательным перебором символов и атрибутов в каждой строке. TITLE / VIDEO_SEG VID_AREA VIDEO_SEG .286 A10MAIN A09DRVID (EXE) Непосред зтвенный вывод строк символов (последовательно букв А-Р) 'с разными атрибутами. .MODEL .STACK SMALL 64 SEGMENT AT 0B900H DB ENDS * .CODE PROC MOV MOV ASSUME MOV INT PUSH PUSH MOV INT MOV INT CALL MOV INT MOV POP MOV INT POP MOV INT MOV INT 1000H DUP(?) FAR AX,VIDEO_SEG ES,AX ES:VIDEO_SEG AH,0FH 10H AX BX AX,0003H 10H AX,050lH 10H B10DISPLY АН,10Н 16H AH,05H BX AL,BH 10H AX АН,00Н ЮН AX,4C00H 21H Страница 1 области видеопамяти Адресация для видеопамяти Запросить получение и сохранить текущие видеорежим и страницу Установить режим 03 и очистить экран Установить страницу 01 Обработать область вывода Ожидать нажатия клавиши •Восстановить исходную 'страницу- •Восстановить ^идеорежим (в AL) ;Завершить работу Рис. 9.6. Непосредственный вывод на экран 248 Часть III. Работа с видео и клавиатурой
A10MAIN BlODISPLY B20: B30: BlODISPLY ENDP PROC PUSHA MOV MOV MOV MOV MOV ADD LOOP INC INC ADD CMP JNE POPA RET ENDP END Сохранит ь символ последовательно NEAR AL,41H AH,01H DI,820 CX, 60 ES:WORD DI,2 B3 0 AH AL DI,40 AL,51H B20 A10MAIN PTR[DI], и атрибуты в области видеопамяти, перебирать символы и атрибуты: ' Сохранить значения регистров Выводимый символ Атрибуты Начало области вывода Символов в строке АХ ;Количество выводимых символов Следующие атрибут и символ Повторить 60 раз Следующие атрибут и символ Отступ для следующей стррки Последний из выводимых символов? нет, повторить иначе восстановить регистры и вернуться к точке вызова Рис. 9.6 (Окончание) Программа устанавливает позицию в видеопамяти согласно формуле для определения любого адреса смещения в области: Адрес смещения = [(строка х 80) + столбец] х 2 Согласно этой формуле у символа в строке 5, столбце 10 будет смещение [(5х80)+10]х2 = 410x2 = 820. После вывода одной строки программа перемещает курсор на 40 положений в области видеоданных до начала следующей строки и заканчивается, достигнув символа Q (51H). Сегмент видеоданных для страницы 1 определен как VEDEOSEG, а страница - как Vro_AREA. Программа использует регистр ES в качестве сегментного регистра для VIDEO_SEG. В начале выполнения программа сохраняет текущий видеорежим, устанавливает режим 03 и текущую страницу 01, а при завершении - восстанавливает исходный режим. В процедуре BlODISPLY начальный символ и атрибуты инициализируются в регистре АХ, а начальное смещение в видеопамяти - в DI. Инструкция MOV ES:WORD PTR[DI],AX помещает содержимое регистра AL (символ) в первый байт видеостраницы, а АН (атрибуты) - во второй байт. Инструкция LOOP вызывает 60-кратное повторение этого действия и вывод цепочки символов на Глава 9. Видеосистема 249
экран. После этого значение символа и атрибутов увеличивается на 1, к значению в регистре DI добавляется 40, т.е. 20 до конца строки и 20 как отступ от начала новой (по десять столбцов отступа от краев экрана) - и все повторяется вновь. После завершения вывода программа ожидает нажатия клавиши и, восстановив исходный видеорежим, завершается. Символы ASCII для рамок и меню В составе расширенного набора символов ASCII (с кодами 128—255 или 80Н—FFH) есть символы, называемые псевдографическими и использующиеся для построения в текстовом режиме рамок, таблиц, приглашений, эмблем и других объектов. Эти символы показаны на рис. 9.7. CHARACTER Straight Lines: Horizontal Vertical Corners: Top left Top right Bottom left Bottom right Middle: Left Right Top - Bottom Center Cross SINGLE LINE C4H - B3H | DAH r BFH -i СОН L D9H J i C3H B4H C2H j C1H -L C5H + Blocks: One-quarter dots on One-half dots on Three-quarter dots on Solid shadow DOUBLE LINE CDH = BAH II C9H BBH C8H BCH CCH B9H CBH САН СЕН ВОН B1H B2H DBH MIXED LINES D6H B7H D3H BDH C7H B6H D2H DOH D7H rr 1 и It I *■ D5H B8H D4H BEH C6H B5H D1H CFH D8H F 1 A I т X ф Solid shadow, upper half DFH Solid shadow, left half DDH Solid shadow, right half DEH Solid shadow, lower half DCH Рис. 9.7. ASCII-символы псевдографики В следующем примере функция 09Н прерывания INT 10H используется для рисования сплошной горизонтальной линии длиной в 25 символов: MOV АН,09Н ;Запросить вывод MOV AL,0C4H ;Сплошная одиночная линия MOV ВН,00 ;Страница 0 MOV BL,1EH ;Синий фон, коричневые символы MOV СХ,2 5 ;25 символов INT 10H вызывать обработчик прерывания Помните, что функция 09Н выводит символы, но не перемещает курсор. 250 Часть III. Работа с видео и клавиатурой
Простейший способ нарисовать рамку - определить ее в сегменте данных и вывести на экран всю область. В приведенном ниже примере показано определение меню в сплошной одиночной рамке: MENU DB DB DB DB DB DB DB DB ODAH, 0B3H, 0B3H, 0B3H, 0B3H, 0B3H, 0B3H, OCOH, 17 DUP(0C4H), 'Add records ', 'Delete records ', 'Enter orders ', 'Print report ', 'Update accounts ' 'View records ', 17 DUP(0C4H) , OBFH 0B3H 0B3H 0B3H 0B3H , 0B3H 0B3H 0D9HLF В следующей главе, рисунки 10 1 и 10_2 показывают такое же меню в двойной рамке с тенью вокруг правого и нижнего краев рамки из сплошных блоков. Ключевые моменты Горизонтальные линии (растр) на экране дисплея содержат сотни элементов (пикселей), состоящих из люминофорных точек трех основных аддитивных цветов - красного, синего и зеленого. Данные в видеопамяти хранятся в последовательно расположенных видеостраницах. По умолчанию отображается страница 0, но можно выбрать отображение любой другой страницы. Ее адрес зависит от текущего видео- . режима. Видеоконтроллер генерирует сигналы вертикальной (кадровой) и горизонтальной (строчной) синхронизации, отслеживает текущие данные в видеопамяти, прорисовывает изображение согласно этим данным, управляет курсором и выбором текущей страницы. Возможно отображение рамки вокруг активной области экрана. Байт атрибутов в текстовом режиме управляет мерцанием, инвертированием изображения, его яркостью и содержит биты для выбора цветов в схеме RGB. Прерывание BIOS INT ЮН предоставляет такие функции, как установку видеорежима, установку курсора, прокрутку экрана, установку палитр и вывод на экран символов и пикселей. Программа, выводящая на экран строки, может использовать функцию 06Н прерывания INT 10H для прокрутки экрана и предотвращения выхода текста за его пределы. Глава 9. Видеосистема 251
• Пикселю (элементу изображения) соответствует в памяти определенное количество битов, зависящее от типа адаптера и выбранного режима. • Графические режимы 04 и 05 позволяют использовать до четырех цветов, из которых один может быть любым из 16 доступных, а остальные три - • из палитры. • Наиболее быстрый способ вывода на экран информации - непосредственное обращение к видеопамяти. Вопросы и упражнения 1. (а) Как называются горизонтальные линии на экране? (б) Как называются элементы в этих линиях? (в) эти элементы состоят из трех люминесцирующих точек трех основных аддитивных цветов; каковы эти цвета? 2. Каков адрес области видеоданных для (а) режимов ООН—06Н, (б) монохромного текстового режима, (в) режимов 0DH—13H? 3. Укажите назначение видеоконтроллера. 4. (а) Каковы два местоположения видео-BIOS? (б) что там хранится? 5. Укажите число страниц, разрешение и число цветов для видеорежима 03. 6. Укажите в двоичной форме содержимое байтов атрибутов для (а) коричневых символов с желтым фоном, (б) пурпурных символов на небесно-голубом фоне, (в) зеленых мигающих символов на белом фоне. 7. Объясните, как ограничивается количество доступных цветов структурой байта атрибутов. 8. Напишите программы, выполняющие следующие действия: (а) Установку текстового режима для 80-столбцового цветного монитора, (б) установку курсора, начинающегося на линии растра 4 и заканчивающегося на 10, (в) позиционирование курсора в строку 5, столбец 20. 9. Напишите программы, выполняющие следующие действия: (а) прокрутку, экрана вверх на 12 строк и установку синего фона, (б) вывод 40 мигающих блоков с половинным заполнением точками (ASCII B1H) в режиме с белым фоном и красными символами. 10. Напишите программы, выполняющие следующие действия: (а) Выбор страницы 3 в качестве текущей, (б) получение текущего состояния курсора, (в) получение текущего видеорежима. 252 Часть III. Работа с видео и клавиатурой
11. Укажите инструкции, необходимые для вывода на экран (а) 10 желтых сердечек (ASCII 03H) на синем фоне, (б) 10 белых звездочек (ASCII 2АН) на красном фоне. 12. Напишите программу, использующую функцию ОАН прерывания INT 21H для организации ввода с клавиатуры и функцию 13Н прерывания INT 10H для вывода символов на экран. Очистите экран, установите режим с зелеными символами на сером фоне, установите синюю рамку и поместите курсор в строку 0, столбец 2. Используйте различные цвета, инвертирование изображения и звуковые сигналы. Выводите данные с клавиатуры, начиная с указанной позиции курсора. После этого поместите курсор в столбец 78 и выведите введенные данные в обратном (справа налево) порядке. Перейдите к следующей строке. Выводите введенные данные в виде столбца, до тех пор, пока не достигнете нижнего края экрана. После этого выполните прокрутку. Программа должна воспринимать данные, пока пользователь не нажмет только клавишу <Enter> в ответ на очередной запрос. Организуйте программу с простой основной управляющей процедурой и несколькими служебными, решающими отдельные задачи. 13. Укажите инструкции, (а) изменяющие цвет регистра палитры 2 на белый, (б) изменяющие цвет рамки на синий, (в) отключающие мерцание, (г) помещающие значения из всех 256 регистров ЦАП в таблицу. 14. Укажите инструкции, устанавливающие размер (а) текстовых символов равным 8x16, (б) графических символов равным 8x14 с 25 строками. 15. Укажите инструкции, необходимые, чтобы поместить информацию о видеоBIOS в 64-байтовую таблицу с именем VIDBIOSTBL. 16. Укажите инструкции, устанавливающие графический режим с разрешением (а) 320x200, 10 цветов; (б) 640x200; (в) 640x480, 16 цветов. 17. Укажите инструкции, выбирающие в графическом режиме красный цвет в качестве фонового. 18. Укажите инструкции, считывающие значение пикселя со строки 142, столбца 264 в графическом режиме. 19. Модифицируйте программу, приведенную на рис. 9.5. так, чтобы (а) фон был синим, (б) строка начиналась с позиции 50 и заканчивалась на позиции 400, (в) столбец начинался с позиции 72 и заканчивался на позиции 568. 20. Исходя из изменений, внесенных вами в вопросе 19, перепишите программу так, чтобы последовательно прорисовывались столбцы, а не строки: сначала должны выводиться все пиксели первого столбца, затем - второго и т.д. Глава 9. Видеосистема 253
Глава 10 Работа с клавиатурой Цель: описать функции работы с клавиатурой и расширенные возможности ввода с клавиатуры, включая статус клавиш Shift, буфер клавиатуры и скан-коды. Введение В этой главе описаны основные приемы работы с клавиатурой, некоторые из которых имеют специальное применение. Функция ОАН прерывания INT 21Н (описанная в гл. 8) и функции прерывания INT 16H (описанные в этой главе) предоставляют в ваше распоряжение почти все средства управления и обмена, которые когда-либо могут вам понадобиться. Среди других тем, рассмотренных в данной главе - байты состояния клавиш Shift, скан-коды и буфер клавиатуры. Байты состояния Shift в области клавиатуры BIOS позволяют программе определять, например, нажаты ли клавиши Ctrl, Alt и Shift. Скан-код - это уникальный для каждой клавиши на клавиатуре номер, позволяющий системе распознать нажатие определенной клавиши, а программе - определить, относится ли.нажатая клавиша к дополнительным (например, Home, PageUp или стрелки управления курсором) или к основным (буквам и цифрам). Буфер клавиатуры позволяет вам непрерывно вводить текст с клавиатуры в определенную область памяти, пока программа не в состоянии непосредственно воспринимать ввод. В этой главе рассмотрены следующие функции: Функции INT 16H Функции INT 21H ОЗН - установить параметры 07Н - прямой ввод с клавиатуры без автоповтора отображения на экране Q5H - запись с клавиатуры в буфер 08Н - ввод с клавиатуры без отображения на экране ЮН - прочитать символ с ОАН - ввод с клавиатуры с клавиатуры использованием буфера ПН - определить, присутствует ли ОВН - проверка состояния символ клавиатуры 12Н - прочитать статус клавиш Shift 0CH - очистить буфер, вызвать функции 254 Часть III. Работа с видео и клавиатурой
Существует три основных разновидности клавиш. 1. Стандартные символы: буквы от А до Z, цифры от 0 до 9 и такие символы, как %, $ и #. 2. Расширенные функциональные клавиши, включая: • функциональные клавиши, например, F1 или Shift+Fl; • клавиши цифровой клавиатуры при выключенном Num Lock: Home, End, стрелки управления курсором, Del, Ins, Page Up и Page Down и соответствующие им клавиши на расширенной клавиатуре; • Alt + клавиши букв и Alt+функциональные клавиши. 3. Специальные клавиши Alt, Ctrl и Shift, обычно используемые в сочетании с другими клавишами, и Caps Lock, Scroll Lock и Num Lock, задающие режим работы. BIOS не помещает кодов нажатий этих клавиш в виде символов ASCII в программу. Вместо этого при их нажатии BIOS обновляет их состояние в байтах состояния в области данных клавиатуры в BIOS. Изначально на клавиатуре PC было 83 клавиши, поскольку вследствие недальновидного конструкторского решения клавиши на так называемой цифровой клавиатуре выполняли 2 действия: позволяли вводить цифры или управлять курсором, в зависимости от состояния Num Lock. Для устранения сложностей, вызванных такой раскладкой клавиатуры, была разработана расширенная клавиатура со 101 клавишей, а затем -для Windows - со 104 клавишами. Из 21 добавленных клавиш только Fll, F12 и три клавиши обращения к меню Windows выполняют действительно новые функции, остальные повторяют функции клавиш на исходной клавиатуре. Области данных клавиатуры в BIOS Область данных BIOS по адресу 40[0]Н в нижней памяти содержит несколько полезных элементов данных. Среди них - две области данных клавиатуры, указывающие текущее состояние клавиш управления. Первая область данных клавиатуры содержит два байта. Биты первого байта, расположенного по адресу 40:17Н, установленные в 1, указывают на следующее: Бит 7 б 5 4 Значение Insert включен Caps Lock включен Num Lock включен Scroll Lock включен. Бит 3 2 1 0 Значение Нажат правый Alt Нажат правый Ctrl Нажат левый Shift Нажат правый Shift Глава 10. Работа с клавиатурой 255
Можно использовать функцию 02Н прерывания INT 16H (рассмотренную далее) для проверки этих значений. "Нажат" означает, что клавиша удерживается нажатой в данный момент; при отпускании клавиши BIOS сбрасывает соответствующий бит в 0. Во втором байте, расположенном по адресу 40:18Н, биты, установленные в 1, указывают на следующее: Бит Значение 7 Insert нажат 6 Caps Lock нажат 5 Num Lock нажат 4 Scroll Lock нажат Бит Значение 3 Активен Ctrl/Num Lock (Pause) 2 Нажат SysReq 1 Нажат левый Alt 0- Нажат левый Ctrl Можно, например, проверить, нажаты ли Ctrl или Alt, или оба вместе. Вторая область данных клавиатуры по адресу 40:48Н используется в качестве буфера клавиатуры, описанного далее. Третья область данных клавиатуры расположена по адресу 40:96Н. Если бит 4 установлен в 1, то подключена расширенная клавиатура. Полное описание областей данных клавиатуры приведено в гл. 24. Упражнение: проверка состояния Для проверки воздействия нажатий клавиш Shift, Ctrl и Alt на содержимое байтов состояния можно воспользоваться DEBUG. Введите команду D 40:17 для просмотра содержимого двух байтов состояния. Нажмите Scroll Lock, Caps Lock и Num Lock и опять введите команду D 40:17 для просмотра изменений. Байт по адресу 40:17Н должен содержать 70Н (0111 0000В), а байт по адресу 40:18Н - скорее всего, ООН. Байт по адресу 40:96Н (бит 4) показывает наличие (1) или отсутствие (0) расширенной клавиатуры. Попробуйте изменить содержимое байтов состояния по адресу 40:17Н - введите команду Е 40:17 00. Если индикаторы на вашей клавиатуре светятся, они должны погаснуть. Теперь попробуйте ввести команду Е 40:17 70. Индикаторы должны вновь зажечься. Можете пробовать различные сочетания, хотя довольно затруднительно вводить корректные команды DEBUG при нажатых клавишах Ctrl и Alt. Для выхода из Debug введите команду Q. Буфер клавиатуры Область данных BIOS по адресу 40:1ЕН содержит буфер клавиатуры. Это позволяет вам вводить до 15 символов еще до того, как программа запросит ввод с клавиатуры. Когда вы нажимаете клавишу, процессор клавиатуры автоматически генерирует скан-код клавиши (уникальный назначенный ей номер), и вызывает прерывание BIOS INT 09H. 256 Часть III. Работа с видео и клавиатурой
Обработчик INT 09H получает с клавиатуры скан-код, преобразует его в соответствующий символ ASCII и помещает его в область буфера клавиатуры. После этого прерывание INT 16H (самая низкоуровневая из операций работы с клавиатурой) считывает символ из буфера и передает его выполняющейся программе. Программе нет нужды вызывать непосредственно INT 09H, поскольку процессор делает это автоматически при нажатии клавиши. Ниже прерывание INT 09H и буфер клавиатуры рассмотрены в подробностях. Функции прерывания INT 21Н для ввода с клавиатуры В этом параграфе рассмотрены различные функции обработчика прерывания INT 21H, обслуживающие ввод с клавиатуры. Все эти функции вызываются соответствующими кодами в регистре АН и воспринимают ввод только одного символа. Ниже термин "реагирует на нажатие Ctrl+Break" означает, что система завершает выполнение программы при нажатии сочетаний клавиш Ctrl+C или Ctrl+Break. Функция 01Н прерывания INT21H: ввод с клавиатуры с отображением на экране Эта операция воспринимает символ из буфера или, если он пуст, ожидает ввода с клавиатуры: MOV АН,01Н ;Запросить ввод с клавиатуры INT 21H Эта операция возвращает один из двух кодов состояния в регистр AL. Если в AL ненулевое значение, это значит, что присутствует стандартный символ ASCII (такой, как буква или цифра), и операция отображает его на экране. Если в регистре AL 0, то пользователь нажал функциональную клавишу, например, клавишу управления курсором, и АН все еще содержит код функции. Операция довольно неуклюже обрабатывает нажатия функциональных клавиш, пытаясь отобразить их на экране. Для того чтобы поучить в AL скан-код функциональной клавиши, нужно немедленно повторить вызов INT 21H. Операция также реагирует на нажатие Ctrl+Break. Функция 07Н прерывания INT21H: прямой ввод с клавиатуры без отображения на экране Эта операция работает так же, как и 01Н, но введенный символ не отображается на экране, а операция не реагирует на нажатие Ctrl+Break. Эту функцию можно использовать, например, для ввода пароля, который не должен отображаться на экране. Глава 10. Работа с клавиатурой 257 17 — 5-741
Функция 08Н прерывания INT 21Н: ввод с клавиатуры без отображения на экране Эта операция работает так же, как и 01Н, но введенный символ не отображается на экране. Функция ОАН прерывания INT21H: ввод с клавиатуры с использованием буфера Эта операция детально рассмотрена в гл. 9. Однако ее неспособность воспринимать нажатия функциональных клавиш существенно ограничивает применение данной функции. Функция ОВН прерывания INT21H: проверка состояния клавиатуры Эта операция помещает в регистр AL значение FFH, если в буфере есть символ; помещает в AL ООН, если в буфере символа нет. Заметьте, что функция не ожидает нажатия клавиши, а просто проверяет буфер. Функция ОСН прерывания INT21H: очистить буфер и вызвать функцию Эту функцию можно использовать в сочетании с функциями 01Н, 06Н, 07Н, 08Н или ОАН. Загрузите код требуемой функции в AL: MOV АН, ОСН ;Запросить функцию клавиатуры MOV AL, функция /Требуемая функция INT 21H Операция очищает буфер клавиатуры, выполняет функцию, код которой указан в AL, и воспринимает символ (или ожидает ввода), соответственно запросу функции. Эта операция может использоваться в программах, не допускающих ввода с клавиатуры до запроса. Функции прерывания INT 16H для ввода с клавиатуры Обработчик прерывания INT 16H реализует основные операции, используемые при работе с клавиатурой. Доступен ряд функций, выбираемых согласно коду в регистре АН. Функция ОЗН прерывания INT 16H: установить параметры автоповтора Если удерживать нажатой клавишу в течение небольшого промежутка времени, клавиатура войдет в режим автоповтора и начнет автоматически повторять 258 Часть III. Работа с видео и клавиатурой
ввод символа. Для изменения задержки перед началом автоповтора и его частоты используется функция ОЗН, например, в таком фрагменте программы: MOV АН,ОЗН ;Установка параметров автоповтора MOV AL,05H /Требуемая подфункция MOV BH,задержка /Задержка перед началом MOV BL,, период /Период повторений INT 16H Значения задержки автоповтора в регистре ВН следующие: 0 - 0,25 секунды, 1 - 0,5 секунды (значение по умолчанию), 2 - 0,75 секунды, 3-1 секунда. Допустимые значения в BL - от 0 (максимальная частота повторения) до 31 (минимальная частота повторения). Функция 05Н прерывания INT 16H: запись в буфер клавиатуры Эта операция позволяет программе имитировать нажатия клавиш, записывая символы в буфер клавиатуры. Загрузите требуемый символ в регистр СН, а его скан-код - в CL. Эта операция позволяет вам вводить символы в буфер до тех пор, пока он не заполнится. Если буфер полон, операция устанавливает флаг переноса и регистр AL в 1. Функция ЮН прерывания INT 16H: прочитать символ с клавиатуры Эта стандартная операция работы с клавиатурой проверяет наличие введенного символа в буфере клавиатуры. Если в буфере есть символ, операция помещает его в AL, а его скан-код - в АН. Если нажатая клавиша относится к функциональным, то в регистр AL будет помещен код ООН. На расширенной клавиатуре клавиши F11 и F12 также помещают ООН в AL, но остальные дополнительные (дублирующие) клавиши, такие, как Ноте или PageUp, помещают в регистр AL 0EH. Вот три возможных ситуации: Нажатая клавиша Обычный символ ASCII Расширенная функциональная клавиша Расширенная дублирующая клавиша Регистр АН Скан-код Скан-код Скан-код Регистр AL Символ ASCII ООН ЕОН Программа может проверять значение в регистре AL на равенство ООН или ЕОН, чтобы определить, была ли нажата расширенная функциональная клавиша: MOV AH,10H ;Запросить ввод с клавиатуры через BIOS INT 16H ;Вызвать обработчик прерывания Глава 10. Работа с клавиатурой 17* 259
CMP AL,OOH ;Расширенная функциональная клавиша? JE выход ;Да, выход CMP AL,E0H /Расширенная функциональная клавиша? JE выход ;Да, выход Поскольку операция не отображает вводимый символ на экране, для его вывода потребуется вызвать отдельную функцию. Функция 11Н прерывания INT 16H: определить, присутствует ли символ Если в буфере клавиатуры есть введенный символ, операция устанавливает в 0 флаг ZF и помещает символ в регистр AL, а его скан-код - в АН. Введенный символ сохраняется в буфере. Если буфер не содержит символов, программа устанавливает в 1 флаг ZF и не ожидает ввода. Заметьте, что функция позволяет предварительный ввод, поскольку символ остается в буфере, пока функция ЮН не прочитает его. Функция 12Н прерывания INT 16H: проверка состояния клавиатуры Эта операция помещает байты состояния клавиатуры из области данных BIOS в регистры: байт, хранящийся по адресу 40:17Н, помещается в AL, а хранящийся по адресу 40:18Н - в АН. В следующем примере проверяется, нажаты ли левый или правый Shift, по битам 1 и 0 байта? в AL: MOV АН,12Н /Запросить состояние клавиатуры INT 16H /Вызвать обработчик прерывания AND AL,0000001IB /Нажаты ли левый или правый Shift? JZ выход /Если да, то... Единичные биты в байте в регистре АН указывают на следующее: Бит 7 б 5 '4 Значение SysReq нажат Caps Lock нажат Num Lock нажат Scroll Lock нажат Бит 3 2 1 0 Значение Правый Alt нажат Правый Ctrl нажат Левый Alt нажат Левый Ctrl нажат Упражнение Вот простая программа, использующая DEBUG для ознакомления с функцией 12Н. В DEBUG введите команду А 100, а затем следующие инструкции: MOV АН,12 INT 16 JMP 100 260 Часть III. Работа q.видео и клавиатурой
Убедитесь, что все три индикатора на клавиатуре погашены. Введите команды R, Т и P. AL должен содержать ООН. Теперь включите Caps Lock, Num Lock и Scroll Lock и повторите ввод последних трех команд. На этот раз AL должен содержать 70Н (0111 0000) и отражать состояние клавиатуры. Теперь выключите Caps Lock, Num Lock и Sum Lock и повторите три команды, но перед нажатием <Enter> после команды Р нажмите и удерживайте Caps Lock: АН и AL будут содержать 40Н (0100 0000). Проверьте клавишу Insert, действующую как переключатель. Попробуйте другие комбинации клавиш, хотя некоторые, например, Alt+Enter, не работают. Дополнительные функциональные клавиши и скан-коды Дополнительные функциональные клавиши, например, F1 или Ноте, вызывают выполнение какой-либо операции, а не отображение на экране символа. В аппаратной части системы ничто не связывает отдельные клавиши с определенными действиями. Только программист определяет, что, например, нажатие клавиши Ноте должно перемещать курсор в начало строки, а нажатие End приводит к перемещению курсора в конец строки. Можно запрограммировать выполнение любых других действий при нажатии этих клавиш. Каждой клавише соответствует определенный скан-код. Скан-коды - это номера, начинающиеся с 01 (скан-код клавиши Esc). В приложении F приведен полный список скан-кодов. С помощью скан-кодов программа может определять источник любого сигнала с клавиатуры. Например, программа может использовать функцию ЮН прерывания INT 16H и запросить ввод одного символа. Операция возвращает различные результаты в зависимости от того, была ли нажата функциональная клавиша или клавиша символа. Для символа, например, буквы А, операция помещает в АХ два элемента данных: АН - скан-код для буквы А (1ЕН)' и AL - ASCII-код для буквы А (41Н). Клавиатура содержит по две клавиши, позволяющие вводить такие символы, как +, /,-,*. Нажатие клавиши * (умножить) на цифровой клавиатуре помещает в регистр код символа 2АН и скан-код 29Н, а нажатие клавиш Shift+8 - код символа 2АН и скан-код 09Н. В следующем примере для выяснения, какая клавиша была нажата, проверяется скан-код: CMP AL,2AH ;Звездочка? JNE выход1 ;Нет, переходим к выходу 1 СМР АН,29Н ;Скан-код для клавиши на цифровой клавиатуре? JE выход2 ;Да, выход 2 Глава 10. Работа с клавиатурой 261
Если нажать клавишу дополнительной функции, например, Del, операция поместит в АХ следующие два элемента: АН - скан-код для клавиши Del (53H) и AL - ООН (для клавиши Del на цифровой клавиатуре) или ЕОН (для аналогичной клавиши на расширенной клавиатуре). После выполнения операции INT 16H (и некоторых операций прерывания INT 21H) можно проверить регистр AL. Если он содержит ООН или ЕОН, то была нажата функциональная клавиша, если какой-то другой код, то с клавиатуры был введен символ. В следующем примере проверяется, была ли нажата функциональная клавиша: MOV АН, ЮН ; Запросить ввод с клавиатуры INT 16H /Вызвать обработчик прерывания CMP AL,00H ;Функциональная клавиша? JE выход ;Да, выход CMP AL,0E0H /Функциональная клавиша? JE выход ;Да, выход В следующем примере, если пользователь нажимает клавишу Ноте (ее скан- код - 47Н), курсор устанавливается в столбец 0, строку 0: L30: MOV АН, ЮН INT 16H CMP AL,00H JE L30 CMP AL,0E0H JNE выход CMP AH,47H JNE выход MOV AH,02H MOV BH,00 MOV DX,00 INT 10H Запросить ввод с клавиатуры Вызвать обработчик прерывания Функциональная клавиша? Да, пропустить Функциональная клавиша? Нет, выход Скан-код клавиши Ноте? Нет, выход Запросить установку курсора в строку и столбец 00:00 Вызвать обработчик прерывания Функциональные клавиши F1—F10 генерируют скан-коды ЗВН—44Н, соответственно, а клавиши F11 и F12 - 85Н и 86Н. В следующем примере проверяется, была ли нажата клавиша F10: СМР АН,44Н ;Скан-код клавиши F10? JE выход ;Да, выход Упражнение В следующем упражнении с применением DEBUG изучаются эффекты ввода различных символов. Введите команду DEBUG A 100, а затем инструкции: 262 Часть III. Работа с видео и клавиатурой
MOV АН,10 INT 16 JMP 100 Используйте команду Р (Proceed) для выполнения функции INT. Попробуйте вводить различные алфавитно-цифровые символы в сочетании с клавишами Shift и Ctrl и сравнивайте результаты в АН (скан-код) и в AL (символ) со списком в приложении F. Поэкспериментировав, переходите к приведенному ниже упражнению. Ввод любого символа из таблицы ASCII Таблица ASCII содержит 256 символов, пронумерованных от 0 до 255 (от ООН до FFH). Многие из них - стандартные выводимые на экран символы, начиная от ASCII 20H (пробел), до ASCII 7EH (~ - тильда). Из-за ограничений клавиатуры большая часть символов ASCII не представлена на ней. Однако можно ввести любой символ ASCII с кодом от 001 до 255, удерживая Alt и вводя соответствующий десятичный код на цифровой клавиатуре. Система сохранит введенное значение в двух байтах в буфере клавиатуры: первый - сгенерированный символ ASCII, второй - ноль. Например, Alt+001 поместит в первый байт 01Н, a Alt+255 - FFH. Продолжая работать с DEBUG, используйте приведенную выше программу и понаблюдайте за эффектом ввода различных символов. Просматривайте значения в регистре АХ и сравнивайте их с таблицей кодов ASCII в приложении В. Программа: выбор из меню Следующая программа выводит на экран окно меню, отбрасывающее тень. Вывод меню описан в гл. 9 и показан на рис. 10.1. Само меню определено в сегменте данных как рамка из двойных линий, содержащая несколько строк - 8 текстовых строк и тень из символов ASCII 0DBH. Пользователь выбирает элемент меню, нажимая стрелки вниз и вверх и клавишу Enter для выбора конкретного пункта. Далее описываются процедуры, составляющие программу, приведенную на рис 10.2. Add records Delete records Enter orders Print, report Update accounts View records Рис. 10.1. Меню, отбрасывающее тень Глава 10. Работа с клавиатурой 263
TITLE TOPROW BOTROW LEFCOL ATTRIB ROW SHADOW MENU PROMPT • 386 ; A10MAIN A20: | A10MAIN A10SELMU (EXE) Выбор элемента меню .MODEL .STACK .DATA EQU EQU EQU DB DB DB DB DB DB DB DB DB DB DB DB DB DB .CODE PROC MOV MOV MOV CALL MOV CALL MOV MOV CALL CALL CMP JNE MOV CALL MOV INT ENDP SMALL 64 08 15 26 ? 00 19 DUP(ODBH) ;Верхняя строка меню ;Нижняя строка меню ;Левый столбец меню ;Атрибуты экрана ;Строка экрана ,-Символы "тени" 0C9H, 17 DUP(OCDH), OBBH 1 OBAH, ' Add records ' , OBAH OBAH, ' Delete records ' ' , OBAH OBAH, • Enter orders ' , OBAH OBAH, ' Print report ' , OBAH OBAH, ' Update accounts ' , OBAH | OBAH, ' View rec ords ' , OBAH 0C8H, 17 DUP(OCDH), OBCH 1 'To select an item, use <Up/Down Arrow>' ' and press <En'ter>. ' 13, 10, 'Press <Esc> to exit.' | FAR AX,@data DS,AX ES,AX Q10CLEAR ROW,BOTROW+4 B10MENU ROW,TOPROW+l ATTRIB,16H D10DISPLY CIOINPUT AL,1BH A2 0 AX,0600H Q10CLEAR AX,4C00H 21H Инициализация сегментных регистров Очистка экрана Установка строки Вывод меню Выбор верхнего пункта меню в качестве текущего Переключение в инверсный режим изображения Подсветка текущей строки меню Организация выбора из меню Нажата ли клавиша Esc? нет, продолжать да, завершить работу ■Очистка экрана ■Конец работы Рис. 10.2. Выбор элемента меню 264 Часть III. Работа с видео и клавиатурой
BlOMENU B20: B30: BlOMENU CI0INPUT PROC PUSHA MOV MOV LEA MOV MOV MOV INT INC CMP JNE MOV MOV MOVZX LEA MOV MOV MOV INT ADD INC CMP JNE MOV MOVZX LEA MOV MOV MOV INT POPA RET ENDP PROC PUSHA Вывести рамку и затем NEAR АХД301Н ВХ,0060Н BP, SHADOW. СХД9 DH,TOPROW+l DL,LEFCOL+l 10H DH DH,BOTROW+2 B2 0 ATTRIB,71H АХД300Н BX,ATTRIB BP, MENU CX,19 DH,TOPROW DL,LEFCOL 10H ВРД9 DH DH,BOTROW+l B30 АХД301Н BX,ATTRIB BP,PROMPT CX,79 DH,BOTROW+4 DL,00 10H тень, затем меню поверх них, приглашение ;Сохранение значений регистров ;Запрос на вывод рамки с тенями ;Страница и атрибуты ;Символы "тени" ;19 символов ;Верхняя строка тени ;Левый столбец тени ;Следующая строка ;Выведены ли все строки? ;Нет, повторить ;С)||ние символы, белый фон ,-Запросить вывод меню ;Страница и атрибуты ;Строка меню ;Длина строки ;Строка ;Столбец ; Следующая ^строка меню ;Следующий столбец ;Все ли строки выведены? ;Нет, повторить ;Запросить вывод приглашения /Страница и атрибуты ;Строка приглашения ;Длина строки ;Строка экрана, ; столбец ;Восстановление значений регистров Воспринимать нажатия клавиш: стрелки и Enter для выбора пункта меню, Esc для выхода NEAR ;Сохранение значений регистров Рис. 10.2 (Продолжение) Глава 10. Работа с клавиатурой 265
С20: СЗО: С40: С50: С90: СЮ INPUT D10DISPLY MOV INT CMP JE CMP JE CMP JE CMP JE JMP MOV CALL INC CMP JBE MOV JMP MOV CALL DEC CMP JAE MOV MOV CALL JMP AH,10H 16H AH,50H C30 AH,48H C40 AL,0DH C90 AL,1BH C90 C20 ATTRIB,71H D10DISPLY ROW ROW,BOTROW-l• C50 ROW,TOPROW+l C50 ATTRIB,71H D10DISPLY ROW ROW,TOPROW+l C50 ROW,BOTROW-l ATTRIB,17H D10DISPLY C20 POPA RET ENDP PROC PUSHA MOVZX SUB IMUL LEA ADD Подсветка выделе Запросить один символ с клавиатуры Стрелка вниз? Стрелка вверх? Клавиша Enter? Клавиша Escape? Ни одна из них, повторить Белые символы на синем фоне Установить ранее выбранную строку в обычный режим Перейти к следующей строке Ниже нижней строки? Нет, выполнить Да, перейти вверх Синие символы на белом фоне Установить ранее выбранную строку в обычный режим •Верхняя строка? •Нет, выполнять •Да, сброс •Белые символы на синем фоне •Установить текущую строку в инверсный режим отображения •Восстановить значения регистров иной строки меню и установка невыделенной в обычный режим NEAR АХ, ROW АХ,ТОPROW АХ, 19 SI,MENU+1 SI,AX ;Сохранение значений регистров ;Row указывает, какую ; строку изменять ;Умножаем на длину строки, чтобы ; получить нужную строку Рис. 10.2 (Продолжение) 266 Часть III. Работа с видео и клавиатурой
D10DISPLY '' QIOCLEAR QIOCLEAR MOV MOVZX MOV MOV MOV MOV INT POPA RET ENDP PROC PU8HA MOV MOV MOV MOV INT POPA RET ENDP END АХД300Н BX,ATTRIB BP, SI CX,17 DH,ROW DL,LEFCOL+l 10H Очистка экрана NEAR •AX,0600H BH,61H CX,0000 DX,184FH 10H A10MAIN ;Запросить вывод ;Страница и атрибуты ;Строка символов ;Длина строки ;Строка ;Столбец ;Восстановление значений регистров ;Сохранение значений регистров ;Синие символы, коричневый фон ;Весь экран ;Восстановление значений регистров Рис. 10.2 (Окончание) • A10MAIN вызывает процедуру QIOCLEAR для очистки экрана, вызывает B10MENU для вывода меню и приглашения, вызывает D10DISPLY для установки первого пункта меню в инверсный режим отображения и вызывает C10INPUT для восприятия ввода информации с клавиатуры. • B10MENU выводит на экран меню. Сначала используется функция 09Н прерывания INT 10H для вывода рамки с тенью. Затем используется функция 13Н прерывания INT 10H для вывода строк меню (определенных в сегменте данных как MENU) в пределах рамки окна, но со смещением на один столбец и одну строку. После этого опять используется та же функция для вывода приглашения ниже меню. • C10INPUT использует функцию ЮН прерывания INT 16H для ввода: стрелки вниз и вверх для перемещения вниз и вверх по меню, Enter - для выбора пункта меню, Esc - для выхода. Нажатия любых других клавиш игнорируются. Меню организовано таким образом, что попытка переместить указатель ниже последнего пункта меню установит его на первый, а выше первого - на последний. Эта процедура также вызывает D10DISPLY для установки выбранной ранее строки в обычный (не инверсный) режим отображения, а выбранной в данный момент- в инверсный. Глава 10. Работа с клавиатурой 267
• D10DISPLY использует функции? 13Н прерывания INT ЮН для вывода выбранной в данный момент строки соответственно установленным атрибутам (нормальный или инверсный режим). • Q10CLEAR очищает весь экран и устанавливает его в режим с синими символами и коричневым фоном. Программа только иллюстрирует вывод и работу меню; полностью готовая программа должна выполнять действия сообразно выбранному пункту меню. Лучше разобраться в приведенной программе можно, введя, оттранслировав и отладив ее. < Прерывание BIOS INT 09H и буфер клавиатуры В клавиатуру встроен 8-разрядный микропроцессор Intel 8048, реагирующий- на нажатия и отпускания клавиш. Процессор посылает код, соответствующий нажатым клавишам, другому 8-разрядному микропроцессору - Intel 8042, расположенному на системной (материнской) плате. При нажатии клавиши процессор клавиатуры генерирует скан-код клавиши и запрашивает прерывание INT 09H. Вектор этого прерывания (он расположен по адресу 36 в таблице векторов прерываний) указывает на программу обработчика прерываний в ПЗУ BIOS. Данная программа выдает запрос на ввод с порта 96 (60Н): IN AL,60H. Программа в BIOS считывает скан-код и сравнивает его с записями в таблице скан-кодов, где скан-кодам поставлены в соответствие определенные символы ASCII. После этого программа записывает скан-код и соответствующий символ ASCII в буфер клавиатуры. Вся только что описанная процедура проиллюстрирована рис. 10.3. ' Заметьте, что прерывание INT 09H обращается за состоянием клавиш Shift, Alt и Ctrl к байтам состояния по адресам 40:17Н, 40:18Н и 40:96Н в области данных клавиатуры. Хотя нажатие этих клавиш вызывает обращение к INT 09H, программа обработчика устанавливает в 1 соответствующие биты, но не помещает в буфер клавиатуры каких-либо символов. Обработчик INT 09H также не реагирует на неопределенные сочетания клавиш, например, Ctrl+/. Когда вы нажимаете клавишу, процессор клавиатуры автоматически генерирует соответствующий скан-код и вызывает обработчик INT 09H. Когда вы отпускаете клавишу, продержав ее нажатой меньше, чем необходимо для включения автоповтора, процессор генерирует еще один скан-код (собственно говоря, тот же, что и при нажатии, плюс 1000 0000В, то есть с установленным в 1 старшим битом) и вновь вызывает обработчик INT 09H. Второй скан-код указывает обработчику, что вы отпустили клавишу. Если удерживать клавишу нажатой, 268 Часть III. Работа с видео и клавиатурой
включится режим автоповтора, проще говоря, будет имитироваться многократное нажатие клавиши. If 1 |ч ) /ШШШШШёё: INT 09H —>* ' Скан-код * *» Процедура BIOS INT09H Клавиатура Ь и Ьз Буфер [ i Скан -код Ьй Символ клавиатуры у у 1 1 1 |хх|хх| Т 1 Процедура BIOS INT16H Ьк I I I I I Г-----------1 ! Процедура! ^ ->-! dos . ! INT21H ! Ьл - Скан- !_ код !Симв" Регистр АХ Ь м П — клавиатура запрашивает INT 09H □ — Обработчик INT 09H воспринимает скан-код и находит соответствующий ему символ □ — Обработчик INT 09H помещает символ и его скан-код в буфер клавиатуры □ и □ — Программа запрашивает INT 16H напрямую либо посредством INT 21Н □ — Обработчик INT 16H перемещает из буфера: символ - в AL, скан-код- в АН Рис. 10.3. Буфер клавиатуры Буфер клавиатуры определяется двумя адресами. Первый адрес (голова буфера) необходим, чтобы указать обработчику INT 16H, откуда читать следующий символ. Второй адрес (хвост буфера) необходим, чтобы указать обработчику INT 09H, где сохранять следующий введенный символ. Эти адреса хранятся в ячейках области данных клавиатуры со смещением 41 АН и 41СН, соответственно. Далее описывается содержимое буфера: Адрес Содержимое 1 41 АН Текущая голова буфера, следующая позиция, из которой INT 16H должен будет прочитать символ и поместить его в регистр АХ 41СН Текущий хвост буфера, сюда обработчик прерывания INT 09H должен поместить введенный с клавиатуры символ 41ЕН Начало собственно буфера клавиатуры. Буфер состоит из 16 слов (32 байтов), хотя его размер может быть увеличен. Он содержит введенные с клавиатуры символы и соответствующие им скан-коды. Обработчик INT 16Н будет считывать каждый символ и его скан-код и передавать его программе. Для каждого символа и его скан-кода требуются два байта Когда пользователь вводит символ, обработчик INT 09H перемещает хвост буфера. Когда обработчик INT 16H считывает символ, он перемещает голову Глава 10. Работа с клавиатурой 269
буфера. Процесс можно повторять до бесконечности, так как голову непрерывно преследует хвост. Когда буфер пуст (INT 16H прочитало все сохраненные символы), голова и хвост имеют одинаковый адрес. В приведенном ниже примере и голова и хвост изначально имеют адрес 41ЕН. Затем пользователь вводит символы abc<Enter>, которые обработчик INT 09H сохранит в буфере в следующем виде: • 'а' в буфере по адресу 41ЕН, а скан-код (1ЕН) - по адресу 41FH; • ЧУ в буфере по адресу 420Н, а скан-код (ЗОН) - по адресу 421Н; • 'с' в буфере по адресу 422Н, а скан-код (2ЕН) - по адресу 423Н; • Нажатие клавиши Enter - по адресу 424Н, а ее скан-код ЕОН - по адресу 425Н. В результате обработчик INT 09H переместил хвост буфера до адреса 426Н: а 1ЕН Ъ ЗОН с 2ЕН <Enter> ЕОН адрес 41Е 41F 420 421 422 423 424 425 426 При первом вызове программой обработчика INT 16H операция считывает 'а' и ее скан-код и перемещает голову по адресу 420Н. После четырехкратного вызова INT 16H все символы будут считаны, а голова переместится в позицию 426Н. Поскольку теперь адрес головы совпадает, с адресом хвоста, буфер считается пустым. После того как вы введете 15 символов и не прочитаете из буфера ни одного, буфер будет считаться заполненным; хвост будет расположен непосредственно перед головой (буфер закольцован). Для того чтобы убедиться в этом, предположим, что вы ввели Tghijklmnopqrs<Enter>'. Обработчик INT 09H сохраняет символы, начиная с хвоста с адресом 426Н, и делает круг, сохраняя '<Enter>' по адресу 422Н. Теперь адрес хвоста равен 424Н, т.е. хвост расположен непосредственно перед головой: г S <Enter> • l-h д h i J k 1 m n о P q II I I I I I I I I I I I I • I I 41E 420 422 424 426 428 42A 42C 42E 430 432 434 436 438 43A 43C В данный момент INT 09H не воспринимает дальнейший ввод с клавиатуры. Фактически, обработчик способен принять в буфер только 15 символов, хотя размер буфера равен 16. (Если бы был принят в буфер шестнадцатый символ, адрес хвоста стал бы равен адресу головы, и обработчик ПМТ 16 ошибочно решил бы, что буфер пуст). 270 Часть III. Работа с видео и клавиатурой
Клавиши Shift, Alt u Ctrl INT 09H также воспринимает нажатия клавиш Shift, Alt и Ctrl, управляя байтами состояния по адресам 40:17Н, 40:18Н и 40:96Н в области данных клавиатуры. При нажатии на эти клавиши обработчик устанавливает соответствующие биты в 1, а при их отпускании - очищает в 0. Заметьте, что само по. себе цажатие этих клавиш не будет замечено обработчиком INT 16H: необходимо нажать их в сочетании с какой-либо другой клавишей, получив корректное клавиатурное сочетание, например, Shift+A, Ctrl+Fl и т.п. (список приведен в приложении F). Байты состояния отображают состояние клавиш управления. Программа может проверять, нажаты ли какие-либо из этих клавиш, при помощи функции 12Н прерывания INT 16H. Программа, приведенная на рис. 10.4, демонстрирует чтение и проверку байта состояния по адресу 40:17Н. Как правило, лучше использовать прерывания BIOS для обращения к областям данных BIOS, но в этой программе иллюстрируются несколько интересных приемов программирования. Программа содержит следующие процедуры. • A10MAIN ожидает нажатия клавиши. Нажатие Enter вызывает завершение программы; при нажатии другой клавиши программа вызывает функцию 12Н прерывания INT 16H для проверки состояния клавиатуры. Если вместе с нажатой клавишей были нажаты Ctrl, Alt или Shift, программа вызывает процедуру B10DISPLY. • B10DISPLY показывает сообщение о нажатии соответствующей клавиши управления. Эту программу можно изменить так, чтобы она проверяла заодно и байт состояния по адресу 40:18Н. TITLE BIODATA KBSTATE BIODATA CR LF ALTKEY CTRLKEY A10KBSTA (EXE) Проверка .MODEL .STACK .DATA SMALL 64 SEGMENT AT 4 OH ORG DB ENDS EQU • EQU DB DB 17H 7 ODH OAH 'Alt key pressed 'Ctrl key presse состояния клавиш Alt, Shift и Ctrl Получение доступа к области данных BIOS и байту состояния •Возврат каретки •Перевод строки ', CR, LF d ', CR, LF Рис. 10.4. Проверка байта состояния клавиатуры Глава 10. Работа с клавиатурой 271
SHIFTKEY ?fifi • .ZOO / A10MAIN A20: A30: A40: A90: A10MAIN / / D10DISPLY D10DISPLY DB .CODE PROC MOV- MOV MOV INT CMP JE MOV TEST JZ LEA CALL TEST JZ LEA CALL TEST JZ LEA CALL JMP MOV INT ENDP PROC PUSHA PUSH MOV MOV MOV MOV MOV MOV INT POP POPA RET ENDP 'Shift key press FAR AX,BIODATA ES,AX AH,10H 16H AL,ODH A90 BL,ES:KBSTATE BL,00000011B A3 0 BP,SHIFTKEY D10DISPLY BL,00000100B A40 BP/yCTRLKEY D10DISPLY BL,00001000B A20 BP,ALTKEY D10DISPLY A2 0 AX,4C00H 21H Вывод сообщения при нажатии клав NEAR ES AX,@data ES,AX АХД301Н BX,0016H CX,17 DX,1008H 10H ES END A10MAIN ed', CR, LF1 Инициализация адреса сегмента BIODATA в регистре ES Запрос на ввод с клавиатуры Запрос на завершение работы? да, завершить работу Получить байт состояния Нажаты Shift и клавиша символа? нет, пропустить Запросить вывод сообщения о нажатии Shift Нажаты Ctrl и клавиша символа? нет, пропустить ' Запросить вывод сообщения о нажатии CTRL •Нажаты Alt и клавиша символа? нет, пропустить ■Запросить вывод сообщения о нажатии ALT •Повторить •Завершение работы иш Shift» Ctrl или Alt •BP устанавливается перед вызовом ;Сохранение значений регистров ;Инициализация пары ES:BP ; адресом сегмента данных ;Запрос вывода на экран ;Страница и атрибуты ;Длина строки ;Строка и столбец ;Восстановление значений регистров Рис. 10.4 (Окончание) 272 Часть III. Работа с видео и клавиатурой
Ключевые моменты • Байты состояния по адресам 40:17Н и 40:18Н, относящиеся к области данных клавиатуры BIOS, указывают текущее состояние таких клавиш, как Shift, Alt, Ctrl, Caps Lock, Num Lock и Scroll Lock. • Обращение к клавиатуре при помощи прерывания INT 21H позволяет использовать ряд функций, отображающих или не отображающих на экране вводимые символы, реагирующих или не реагирующих на нажатие Ctrl+Break, и считывающих скан-коды. • Функция ЮН прерывания INT 16H обеспечивает доступ к базовым средствам BIOS для чтения символов из буфера клавиатуры. При нажатии символьной клавиши операция помещает символ в регистр AL, а его скан-код - в АН. Для функциональных клавиш в AL записывается ООН или БОН, а в АН - скан-код клавиши. • Скан-код - уникальный номер, назначенный каждой клавише, позволяющий BIOS определять, какая клавиша нажата, а программе - проверять нажатия функциональных клавиш, которым не соответствуют никакие символы - например, клавиш Home, PageUp или стрелок управления курсором. • Область данных клавиатуры BIOS по адресу 40:1ЕН содержит буфер клавиатуры, позволяющий вводить до 15 символов и хранить их до того, как они начнут восприниматься программой. • Нажатие клавиши вызывает генерацию микропроцессором клавиатуры соответствующего скан-кода и обращение к обработчику INT 09H. Отпускание клавиши вызывает генерацию того же скан-кода, но со старшим битом, установленным в 1. • BIOS INT 09H получает от клавиатуры скан-коды. Обработчик использует скан-код для выбора определенного символа из таблицы ASCII. После этого скан-код и выбранный символ помещаются в буфер клавиатуры. Вопросы и упражнения 1. (а) Какой адрес у.первого байта состояния в области данных BIOS? (б) Что означает его содержимое 00000010? (в) Что означает его содержимое 00001100? 2. Опишите следующие функции прерывания INT 21Н: (а) 01Н, (б) 07Н, (в) 08Н, (г) 0AH. 3. Объясните различия между функциями ЮН и 11Н прерывания INT 16H. Глава 10. Работа с клавиатурой 273 18 — 5-741
4. Укажите скан-коды для нажатий следующих клавиш: (a) End, (б) PageUp, (в) стрелка вверх, (г) F6. 5. Используйте DEBUG для проверки результатов ввода различных сочетаний клавиш. В DEBUG введите команду А 100, а затем следующие инструкции: MOV АН,12 INT 16 JMP 100 Используйте команду U 100,104 для дизассемблирования программы, и воспользуйтесь командой Р для вызова обработчика INT 16H. Выполнение приостановится в ожидании ввода с клавиатуры. Нажмите какую-нибудь клавишу и просмотрите содержимое АН и AL. Попробуйте вводить различные сочетания клавиш. Воспользуйтесь командой Q для выхода из DEBUG. 6. Укажите инструкции, которые с помощью функции ЮН прерывания INT 16H воспринимают нажатие клавиши; если нажата клавиша PageDown, переместите курсор в столбец 0, строку 24. 7. Внесите в программу, приведенную на рис. 10.2, следующие изменения, (а) Замените сплошную тень тенью из символов с ASCII-кодом 0В2Н. (б) После начальной очистки экрана организуйте вывод сообщения, требующего нажатия F1 для вывода меню, (в) При нажатии F1 выведите меню, (г) Организуйте возможность выбора пункта меню нажатием первой буквы пункта (строчной или заглавной), (д) При выборе пункта меню организуйте вывод описания пункта, например, 'Procedure to Delete Records', (e) Добавьте в меню пункт 'Exit from program', при выборе которого завершается выполнение программы. При этом потребуется модифицировать и процедуру B10MENU для вывода дополнительной строки в рамке. 8. Когда вызывается обработчик прерывания INT 09H? 9. Объясните, чем отличается реакция обработчика INT 09H на нажатие клавиш Shift и Ctrl от реакции на нажатие обычных'символьных клавиш. 10. (а) Где расположен буфер клавиатуры в памяти? (б) Каков размер буфера в байтах? (в) Сколько символов с клавиатуры он может вместить? 11. Объясните эффект следующих положений указателей в буфере: (а) Адрес хвоста непосредственно следует за адресом головы; (б) Адрес головы и хвоста одинаков. 12. Модифицируйте программу, приведенную на рис. 10.4, так, чтобы (а) второй байт состояния клавиатуры передавался в регистр ВН, (б) проверялись нажатия левых Alt и Ctrl и выводились соответствующие сообщения. 274 Часть III. Работа с видео и клавиатурой
Часть IV Обработка данных Глава 11. Обработка строковых данных Глава 12. Арифметика: обработка двоичных чисел Глава 13. Арифметика: работа с данными форматов ASCII и BCD Глава 14. Определение и обработка таблиц
Глава 11 Обработка строковых данных Цель: рассмотреть специальные инструкции, предназначенные для обработки строковых данных. Введение Инструкции, использовавшиеся в программах вплоть до этой главы, работ тали с данными, определенными как единственный байт, слово или двойное слово. Однако часто бывает необходимо перемещать или сравнивать данные, длина которых намного больше. Например, может возникнуть необходимость отсортировать имена или названия и выстроить их в алфавитном порядке. Элементы данных такого типа называются строковыми данными и могут содержать алфавитно-цифровые символы или числа. В языке ассемблера для работы со строковыми данными существуют следующие инструкции: MOVS - переместить один байт, слово или двойное слово из одной ячейки памяти в другую;. LODS - загрузить из памяти байт в AL, слово - в АХ, или двойное слово - вЕАХ; STOS - сохранить значение из AL, АХ или ЕАХ в памяти; CMPS - сравнить байты, слова или двойные слова в памяти; SCAS - сравнить содержимое AL, АХ или ЕАХ с содержимым элемента данных в памяти. С этими инструкциями можно применять префикс REP. При наличии префикса REP строковая инструкция выполняется многократно и может обработать любое (указанное в регистре СХ) количество байт, слов или двойных слов. Еще две инструкции, INS или OUTS, рассмотрены в гл. 24. Свойства строковых операций У каждой строковой инструкции есть несколько реализаций, предназна-^ ченных для последовательной обработки строк данных порциями размером в байт, слово или двойное слово. Эти инструкции используют пары регистров ES*:DI или DS:SI. Для строк с нечетным количеством байтов (нечетной длиной) можно выбрать операции, обрабатывающие строки по одному байту, а для 276 Часть IV. Обработка данных
строк с четной или делящейся нацело на 4 длиной - инструкции, обрабатывающие строки порциями в целое слово или двойное слово - их применение обеспечит более быстрое исполнение. Строковые инструкции подразумевают, что регистры SI и DI содержат корректные величины смещений, по которым расположены в памяти данные. SI обычно связывается с DS (для обращений к сегменту данных) в виде DS:SI, a DI всегда связывается с ES (для обращений к дополнительному сегменту) как ES:DI. Поэтому обычно (но не всегда) программы типа .ЕХЕ, использующие инструкции MOVS, LODS, CMPS и SCAS, инициализируют ES тем же адресом, что и DS: MOV AX,@data MOV DS,AX MOV ES,AX Получить адрес сегмента данных Занести этот адрес в DS и ES На рис. 11.1 показаны два основных способа кодирования строковых инструкций: 1. Второй столбец показывает основной формат каждой инструкции, использующей операнды, приведенные в третьем столбце. Если указать инструкцию как MOVS, указанные операнды (например, BYTE1 и BYTE2 в команде MOVS BYTE1,BYTE2) определят своим типом длину перемещаемого фрагмента. В параграфе "Альтернативное кодирование строковых инструкций" далее в этой главе формат инструкций описан более подробно. 2. Второй способ кодирования строковых инструкций применяется чаще и показан в четвертой, пятой и шестой колонках. Вы загружаете адреса операндов в регистры DI и SI и вызываете инструкции без операндов, например, так: LEA DI,BYTE2 ;Адрес BYTE2 загружается в ES:DI LEA SI,BYTE2 ;Адрес BYTE1 загружается в DS;SI MOVSB ;BYTE1 переносится в BYTE2 Операция Перемещение Загрузка Сохранение Сравнение Просмотр Исходная инструкция MOVS LODS STOS CMPS SCAS Операнды, используемые по умолчанию ES:DI,DS:SI AX,DS:SI ES:DI,AX DS:SI,ES:DI ES:DI,AX Операция с байтами MOVSB LODSB STQSB CMPSB SCASB' Операция со словами MOVSW LODSW STOSW CMPSW SCASW Операция с двойными словами MOVSD LODSD * STOSD CMPSD SCASD Рис. 11.1. Форматы строковых инструкций Глава 11. Обработка строковых данных 277
REP: префикс повторения строки Префикс REP непосредственно перед строковой инструкцией, например, REP MOVSB, приводит к многократному выполнению инструкции, для чего число повторений указывается в регистре СХ. REP выполняет строковую инструкцию, уменьшает СХ на 1, а затем повторяет эти действия до тех пор, пока в СХ не окажется 0. С помощью этого префикса вы можете обрабатывать строки значительной длины. Флаг направления. (Direction Flag, DF) определяет, в каком направлении будут выполняться последовательно повторяющиеся операции. • Для обработки строки слева направо используйте инструкцию CLD для установки DF в 0. • Для обработки справа налево используйте инструкцию STD для установки DFbI. В следующем примере предполагается, что регистры DS и ES инициализированы адресом сегмента данных, как показано выше. Инструкция REP MOVSB копирует 20 байтов из SEND_STR в RECV_STR: ;Поле-источник ;Поле-приемник ;Установить флаг направления (DF) в 0 ;Инициализация перемещения 20 байтов ;Загрузка адреса строки-источника ;Загрузка адреса строки-приемника ;Копирование Инструкции MOVS, LODS и STOS всегда полностью выполняются указанное количество раз. Инструкции CMPS и SCAS выполняют сравнение, устанавливая в результате своего выполнения определенные флаги, и могут завершиться при выполнении заданного условия. Для реализации такого рода операций предусмотрено несколько модификаций префикса REP. • REP: Повторять инструкцию до тех пор, пока СХ не будет содержать 0. • REPE или REPZ: Повторять инструкцию, пока флаг нуля (ZF) указывает, что имеет место равенство или ноль. Остановиться, если ZF указывает, что имеет место неравенство или не ноль, или когда СХ содержит 0. • REPNE или REPNZ: Повторять инструкцию, пока флаг нуля (ZF) указывает,1 что имеет место неравенство или не ноль. Остановиться, если ZF указывает, что имеет место равенство или ноль, или СХ содержит 0. ч Ниже каждая из инструкций описана в подробностях. SEND_STR RECV_STR DB DB CLD MOV LEA LEA REP 20 DUP ('*') 20 DUP (' ') CX,20 SI/SEND_STR DI,RECV_STR MOVSB 278 Часть IV. Обработка данных
Инструкция MOVS: перемещение строковых данных Инструкции MOVSB, MOVSW и MOVSD при наличии префикса REP и заданного в регистре СХ числа могут перемещать указанное количество символов. Адрес строки назначения в регистрах ES:DI и адрес строки источника в регистрах DS:SI задаются в форме сегментхмещение. Поэтому в начале программы типа .ЕХЕ необходимо корректно инициализировать как DS, так и ES, а непосредственно перед использованием инструкций MOVS необходимо инициализировать DI и SI. В зависимости от того, установлен или сброшен флаг направления DF, MOVS уменьшает или увеличивает значения в регистрах DI и SI на 1 при перемещений байта, на 2 - при перемещении слова, и на 4 - при перемещении двойного слова. В следующем примере демонстрируется перемещение 12 слов: MOV LEA LEA REP СХ,12 ' DI,RECV_STR SI,SEND_STR MOVSW ;Число слов ;Адрес RECV_STR (в ES:DI) ;Адрес SEND_STR (в DS:SI) ;Перемещаем 12 слов Инструкцию REP MOVSW в этом примере можно заменить следующими инструкциями: JCXZ 'L30: L40 MOV MOV ADD ADD LOOP АХ,[SI] [DI],AX DI,2 SI,2 L3 0 Пропустить, если СХ содержит 0 изначально Получить слово из SEND_STR Поместить слово в RECV_STR Увеличить значения в регистрах DI и SI Уменьшить значение в СХ и повторить процедуру L40: Ранее, в гл. 6, программа, приведенная на рис. 6.2, выполняла перемещение 9-байтового поля. Для тех же целей можно было бы использовать инструкцию MOVSB. В следующем фрагменте программы (рис. 11.2) регистр ES инициализируется, поскольку это необходимо для использования инструкций MOVS. Программа использует инструкцию MOVSB для перемещения содержимого 12-байтового поля, STRING1, в другое такое же поле, STRING2, порциями по одному байту. Инструкция CLD устанавливает в 0 флаг направления, и инструкция MOVSB двигается по строке слева направо. Флаг DF обычно равен О в начале исполнения программы, но в качестве предосторожности лучше применить инструкцию CLD. Глава 11. Обработка строковых данных 279
STRING1 STRING2 STRING3 DB DB DB MOV MOV MOV CLD MOV LEA LEA REP CLD MOV LEA LEA REP 'Interstellar' , 12 DUP(' ') 12 DUP(' ') AX,@data DS,AX ES,AX CX,12 DI,STRING2 SI,STRING1 MOVSB CX,06 DI,STRING3 SI/STRING2 MOVSW Элементы данных Инициализация сегментных регистров Использование MOVSB: слева направо Перемещение 12 байтов из STRING1 в STRING2 •Использование MOVSW: * •Слева направо •Перемещение б слов из STRING2 в STRING3 Рис. 11.2. Использование инструкций MOVS Инструкция MOV инициализирует регистр СХ значением 12 (длиной строк STRING1 и STRING2). Две инструкции LEA загружают в регистры SI и DI адреса смещений STRING1 и STRING2, соответственно. После этого инструкция REP MOVSB выполняет следующие действия: • перемещает первый слева байт из STRING 1 (указанный адресом в DS:SI) в первый слева байт STRING2, по адресу, указанному в ES:DI; • увеличивает SI и DI на 1 для обращения к следующим справа байтам; • уменьшает СХ на 1; • повторяет вышеуказанные операции 12 раз, пока значение в СХ не уменьшается до 0. Поскольку DF установлен в 0 и инструкция MOVSB увеличивает DI и SI на 1, каждая итерация;обращается к байту, следующему справа за только что обработанным, т.е. обращается к STRING1 и STRING2, STRING1+1 и STRING2+1 и т.д. В конце исполнения операции СХ содержит 0, DI содержит адрес STRING2+12, a SI - STRING1+12, регистры DI и SI указывают на байты, находящиеся непосредственно за концом соответствующих строк. Для обработки строк справа налево установите флаг DF в 1. При этом MOVSB будет последовательно уменьшать DI и SI на 1 при каждой итерации, но для правильного переноса данных необходимо инициализировать регистр SI адресом STRING1+11, a DI - адресом STRING2+11. 280 Часть IV. Обработка данных
Далее программа использует инструкцию MOVSW для перемещения 6 слов из STRING2 в STRING3. В конце выполнения инструкции СХ содержит О, DI содержит адрес STRING3+12, a SI - STRING2+12. Поскольку MOVSW перемещает байты порциями по два, операция повторяется только 6 раз. Для выполнения перемещения справа налево установите DF в 1, и инициализируйте SI адресом STRING2+10, a DI - STRING3+10. Инструкция LODS: загрузить строку LODS просто помещает из памяти в регистр AL байт, в АХ - слово, а в ЕАХ - двойное слово. Адрес, из которого производится загрузка, хранится в паре DS:SI. В зависимости от состояния флага DF, операция также уменьшает или увеличивает значение в SI на 1 при перемещении байта, 2 - слова, и 4 - двойного слова. •Поскольку одна инструкция LODS заполняет регистр, нет необходимости использовать ее с префиксом REP. В большинстве случаев ее можно заменить инструкцией MOV. Однако MOV генерирует три байта машинногр кода, а LODS - только один, хотя необходимо предварительно проинициализировать регистр SI. Можно использовать инструкцию LODS для последовательного просмотра строки по байту, слову или двойному слову за раз, например, в поисках определенного значения. Инструкция LODSB эквивалентна следующим инструкциям: MOV AL,[SI] INC SI Поместить байт в AL Увеличить SI на 1 для обращения к следующему байту В следующем примере STRING 1 - это последовательность из 12 байтов, содержащих слово "Interstellar", a STRING2 - пустая 12-байтовая последовательность. Цель данного примера - переместить содержимое STRING 1 в STRING2 в обратном порядке так, чтобы STRING2 содержала "ralletsretnl". LODSB поочередно считывает байты из строки STRING1 в AL, а инструкция MOV [DI],AL перемещает их из AL в STRING2 справа налево. ;Элементы данных ;Слева направо Адрес STRING1 (в DS:SI) Адрес конца STRING2 (в ES:SI) STRING1 STRING2 DB DB CLD MOV LEA LEA 1 Interstellar 12 DUP (20H) CX, 12 SI,STRING1 DI/STRING2+11 Глава 11. Обработка строковых данных 281
L20: LODS MOV [DI],AL DEC DI . LOOP L20 Поместить символ в регистр AL Сохранить его в STRING2 Перейти к предыдущему символу в STRING2 Повторить процедуру 12 раз Инструкция STOS: сохранение строки Инструкция STOS сохраняет содержимое AL, АХ или ЕАХ соответственно в байте, слове или двойном слове в памяти. Адрес в памяти всегда содержится в паре ES:DI. В зависимости от состояния флага DF инструкция STOS также увеличивает или уменьшает значение в DI на 1 - для байта, на 2 - для слова, и на 4 - для двойного слова. Практически, STOS в сочетании с префиксом REP используется для заполнения области памяти определенным значением, например, при очистке строки от содержимого. Количество байтов, слов или двойных слов определяется значением в СХ. Инструкции, эквивалентные REP STOSB (для случая DF=0), следующие: L20: JCXZ MOV INC L30 [DI],AL DI LOOP L20 L30: Пропустить, если СХ изначально содержит О Поместить байт в RECV_STR Увеличить значение в DI Примените DEC DI, если нужно заполнять ячейки в обратном порядке Уменьшить значение в СХ и повторить процедуру, если СХ не содержит О Операция завершена Инструкция STOSW в следующем фрагменте помещает последовательно шесть слов, содержащих 2020Н (2 пробела) в строку STRING1 из 12 символов. Операция сохраняет в первом байте содержимое AL, а во втрром - АН (т.е. переставляет их). В конце строка будет содержать 12 пробелов, СХ - 0, a DI - адрес STRING1+12. CLD MOV АХ,2020Н MOV СХ,06 LEA DI,STRING1 REP STOSW Слева направо Поместить 6 слов по 2 пробела в строку STRING1 (адрес в .ES:DI) 282 Часть IV. Обработка данных
Программа: использование LODS и STOS для редактирования данных Программа, приведенная на рис. 11.3, демонстрирует применение инструкций LODS и STOS. Эта программа позволяет пользователю редактировать строку символов. Для уменьшения размера и сложности в этой программе реализованы только минимальные возможности редактора. Программа просто выводит строку из 30 символов. Основные процедуры выполняют следующие действия: • A10MAIN инициализирует адреса и вызывает процедуры Q30DISPLY (для вывода строки) и B10KEYBRD (для запроса символа с клавиатуры). Программа завершает работу при нажатии клавиши <Esc>; • B10KEYBRD воспринимает нажатия клавиш: если нажата стрелка вправо, вызывается процедура C10RTARRW, если стрелка влево - D10LFARROW, если клавиша <Del> - вызывается E10DELETE, если <Ноте> - вызывается процедура F10HOME, если <End> - G10END; при нажатии любой другой клавиши вызывается процедура HI0CHARS; • C10RTARRW организует перемещение, курсора вправо; если достигнут правый край, вызывается процедура F10HOME; • D10LFARRW организует перемещение курсора влево; если достигнут левый край, вызывается процедура G10END; • E10DELETE заменяет текущий символ следующим за ним справа, и пере-ч мещает все символы на один влево; крайний справа символ заменяется пробелом; • F10HOME устанавливает курсор в крайнее слева положение; • G10END устанавливает курсор в крайнее справа положение; • H10CHARS не реагирует на символы с кодом до 20Н или свыше 7ЕН; если код символа - от 20Н до 7ЕН, текущий символ заменяется введенным в строке и на экране. В программу можно внести, например, следующие улучшения: • выводить строку в рамке из символов псевдографики; • создать два режима ввода, переключаемых нажатиями клавиши <Ins>, с непосредственной заменой символов и с перемещением ранее введенных символов вправо. Можно также поэкспериментировать с выводом данных на экран в процессе редактирования, например, записывая их напрямую в видеопамять. Глава 11. Обработка строковых данных 283
TITLE INDENT LEFTLIM RIGHTLIM NOCHARS COL ROW DATASTR .386 ; A10MAIN \a30: A10MAIN B10KEYBRD A11EDIT (EXE) Редактирование строки .MODEL .STACK .DATA EQU EQU EQU EQU DB DB DB DB .CODE PROC MOV MOV MOV CALL CALL CALL CALL CALL CMP JNE MOV CALL MOV INT ENDP Получа как PROC MOV INT CMP JE CMP JE CALL JMP SMALL 64 24 00 29 30 00 10 'abcdefghi jklmno 'pqrstuvwxyzABCD FAR AX,©data DS,AX ES,AX Q10CLEAR Q20CURSOR Q30DISPLY Q20CURSOR B10KEYBRD AH,01H A3 0 AX,0600H Q10CLEAR AX,4C00H 21H эм символ с клави на него реагиров NEAR АН, ЮН 16Н AL,ООН В20 AL,0E0H В2 0 H10CHARS В90 ;Отступ на экране * ;Левая граница' данных ;Правая граница данных ;Длина данных /Столбец экрана ;Строка экрана ' ;Область для редактирования ' , 2 ОН ; данных Инициализация сегментных регистров Очистка экрана Установка курсора Вывод строки на экран Установка курсора Получение символа с клавиатуры Клавиша ESC? нет, продолжаем да, начинаем завершать работу Очищаем экран Завершаем работу атуры и определяем, ать: Используется только АХ Получить символ •Функциональная клавиша/стрелка? да •Функциональная клавиша/стрелка? да • Другой символ • Выход Рис. 11.3. Простой редактор 284 Часть IV. Обработка данных
В20: ВЗО: В40: В50: ВбО: В90: B10KEYBRD C10RTARRW С20: С90: C10RTARRW D10LFARRW CMP JNE CALL JMP CMP JNE/ CALL JMP CMP JNE CALL JMP CMP JNE CALL JMP CMP JNE CALL RET AH,4DH ВЗО C10RTARRW В90 АН,4ВН В40 D10LFARRW В90 АН,53Н ВБО E10DELETE В90 АН,47Н В60 F10HOME В90 AH,4FH В90 G10END ENDP Стрелка вправо. Если те Стрелка вправо? нет да, обработать Стрелка влево? нет да, обработать Клавиша Del? нет да, обработать Клавиша Ноте? нет да, обработать Клавиша End? нет да, обработать кущий символ - крайний справа, перейти к левому краю, иначе перейти к следующему столбцу: PROC СМР JAE INC JMP CALL RET ENDP NEAR COL,RIGHTLIM C20 COL C90 F10HOME Стрелка влево. Если тек перейти к правому краю, PROC СМР JBE DEC JMP NEAR COL,LEFTLIM D20 COL D90 •На правом краю? да нет, переход к следующему ; столбцу ;Выход ,-Вереместить курсор на пг%1вый край ущий символ - крайний слева, иначе к предыдущему столбцу: ;На левом краю? да ; нет, переход к предыдущему ; столбцу ;Выход Рис. 11.3 (Продолжение) Глава 11. Обработка строковых данных 285
D20: D90: D10LFARRW ElODELETE E20: ElODELETE ' FIOHOME FIOHOME / G10END GIOEND CALL RET ENDP GIOEND Переместить курсор на правый край Клавиша Del. Заменить текущий символ соседним справа, сдвинуть все символы, расположенные справа, на 1 влево: PROC MOVZX PUSH LEA LEA LODSB STOSB CALL INC CALL CMP JBE POP MOV RET ENDP NEAR BX,COL BX DI,[DATASTR+BX] Использует BX, DI, SI Получить номер столбца Сохранить его в стеке Загрузить текущий столбец SI,[DATASTR+BX+1] ; и соседний в регистры 1 Q40DISCHR COL Q20CURSOR COL,RIGHTLIM E20 BX COL,BL Клавиша Home. Установит PROC MOV CALL RET ENDP NEAR COL,LEFTLIM Q20CURSOR Клавиша End. Установить PROC MOV CALL RET ENDP Прочие NEAR COL,RIGHTLIM Q20CURSOR символы. Игнорир 2ОН или выше 7EH, иначе Сохранить следующий символ в текущем столбце Вывести символ на экран Перейти к следующему столбцу Установить курсор На правом краю? нет, повторить Восстановить исходное значение столбца ь курсор в первый слева столбец: Установить курсор на левый край строки * курсор в последний столбец: ■Установить курсор ■ на правый край строки овать символы с кодами меньше вставлять их в позиции курсора: Рис. 11.3 (Продолжение) 286 Часть IV. Обработка данных
H10CHARS H90: HIOCHARS / QIOCLEAR QIOCLEAR ; Q20CURSOR > Q2 0CURSOR # Q30DISPLY PROC CMP JB CMP JA MOVZX LEA MOV CALL CMP JAE INC RET ENDP NEAR AL,20H H90 AL,7EH H90 BX,COL DI,DATASTR [DI+BX],AL Q40DISCHR COL,RIGHTLIM H90 COL Использует BX, DI ASCII-код меньше 20Н? да, пропустить Больше 7ЕН? да, пропустить Используем COL как индекс Поместить символ в строку данных Вывести символ На правом краю? да, выход нет, перейти к следующему столбцу Очистка экрана и установка атрибутов: PROC MOV MOV MOV MOV INT RET ENDP NEAR AX,0600H BH,61H CX,0000 DX,184FH 10H . Запросить прокрутку Синие символы, коричневый фон Весь экран Установка курсора в строку:столбец: PROC MOV MOV MOV MOV ADD INT RET Вывод PROC MOV MOV NEAR AH,02H BH, 00 DH,ROW DL,COL DL,INDENT 10H ENDP строки данных: NEAR AX,1301H BX,0016H •Запросить установку курсора •Страница 0 •Строка Столбец •Отступ на экране ч ;Запросить вывод ;Страница, атрибуты Рис. 11.3 (Продолжение) Глава 11. Обработка строковых данных 287
LEA MOV MOV MOV ADD INT RET Q30DISPLY ; Вывод Q40DISCHR PROC MOV MOV MOV INT RET Q40DISCHR END BP,DATASTR CX,NOCHARS+l DH,ROW DL,COL DL,INDENT 10H ENDP одного символа: NEAR AH, OAH BH,00 CX,01 10H ENDP A10MAIN Строка данных Длина строки данных Отступ на экране Символ находится в AL при вызове процедуры Страница Один символ Рис. 11.3 (Окончание) Инструкция CMPS: сравнение строк Инструкция CMPS сравнивает содержимое одного элемента данных (по адресу DS:SI) с содержимым другого (с адресом ES:DI). В зависимости от состояния флага DF, CMPS увеличивает или уменьшает значения в SI и DI на 1 - для байта, на 2 - для слова, и на 4 - для двойного слова. Операция завершается при ожидаемом результате сравнения или в случае, когда в СХ оказывается нулевое значение. Операция может изменять состояния флагов AF, CF, OF, PF, SF и ZF в зависимости от результатов сравнения. В сочетании с префиксами REPnn и значением в СХ эта инструкция может обрабатывать строки солидной длины. Есть три разновидности инструкции CMPS: CMPSB - для сравнения байтов, CMPSW - для слов, и CMPSD - для двойных слов. Заметьте, что СМР сравнивает операнд 2 с операндом 1, a CMPS - операнд 1 с операндом 2. Кроме того, CMPS проводит алфавитно-цифровое сравнение, то есть сравнивает номера символов в таблице ASCII. Эта операция непригодна для сравнения знаковых числовых величин. Рассмотрим сравнение двух строк, содержащих 'Jean' и 'Joan'. Сравнение в данном случае проводится слева направо, побайтно, а результаты следующие: J:J равно е:о не равно (е меньше) а:а равно п:п равно 288 Часть IV. Обработка данных
Сравнение всех четырех байтов заканчивается сравнением 'п' и 'п' (равных). Поскольку два имени не одинаковы, операцию следует завершить, как только она нашла несовпадающие символы. Для этого используется разновидность REPE (повторять пока равно) префикса REP. При использовании REPE операция продолжается до тех пор, пока не будут найдены несовпадающие символы или пока СХ не будет содержать 0. Для побайтового сравнения используется инструкция REPE CMPSB. Ниже приведены два примера использования CMPSB. В первом примере сравниваются STRING 1 и STRING2, две строки, содержащие одинаковые 12- байтовые значения. Поэтому CMPSB продолжает сравнение, пока не дойдет до конца строки. В конце ее выполнения СХ содержит 0, DI содержит адрес STRING2+12, SI - STRING1+12, флаг знака SF положителен, а флаг нуля ZF указывает на нулевое/равное значение. ;Элементы данных ;Сравнение слева направо ;Инициализация для 12 байтов ;ES:DI ;DS:SI /Сравниваем STRING1 со STRING2 ;Не равно, пропускаем ... ;Равно Второй пример сравнивает STRING2 и STRING3, содержащие разные значения. Выполнение CMPSB завершается после сравнения первых байтов с условием больше/неравно: СХ будет содержать 11, DI - адрес STRING3+1, SI - адрес STRING2+1, флаг SF будет указывать на положительное значение, а флаг ZF - не ноль. STRING1 STRING2 STRING3 DB DB DB CLD MOV LEA LEA REPE JNE 'Interstellar 'Interstellar 12 DUP (' ') CX,12 DI,STRING2 SI,STRINGl CMPSB выход MOV LEA LEA REPE JE CX,12 DI,STRING3 SI,STRING2 CMPSB выход Инициализация для 12 байтов ES:DI DS:SI Сравниваем STRING2 с STRING3 Равйо, пропускаем Не равно Внимание! В этих примерах используются инструкции CMPSB, чтобы сравнивать строки побайтово. Если вы попытаетесь использовать инструкцию CMPSW, чтобы обрабатывать сразу целые слова, нужно будет проинициализи- ровать регистр СХ значением 5. Но основная проблема не в этом. При сравнении слов инструкция CMPSW переставляет байты местами. Например, попробуем сравнить имена SAMUEL и ARNOLD. При сравнении первых двух слов вместо Глава 11. Обработка строковых данных 19 — 5-741 289
сравнения SA и AR будут сравниваться AS и RA. Поэтому большим значением будет сочтено имя ARNOLD, а не SAMUEL, как должно было бы быть. CMPSW корректно обращается только с полями, содержащими беззнаковые числа и определенными как DW, DD или DQ (то есть с данными, хранящимися в последовательности с переставленными байтами). Кроме того, более короткая строка должна быть дополнена справа пробелами. Инструкция SCAS: просмотр строки SCAS отличается от CMPS тем, что SCAS просматривает строку в поисках указанного значения. Для этого SCAS последовательно сравнивает ячейки, адреса которых определяются парой ES:DI, с содержимым AL, АХ или ЕАХ. В зависимости от состояния флага направления SCAS также увеличивает или уменьшает DI на 1 - для байтов, на 2 - для слов, и на 4 - для двойных слов. Операция завершается при совпадении значений в ходе очередного сравнения или, когда REP уменьшит значение СХ до 0. В зависимости от результатов сравнений могут изменяться состояния флагов AF, CF, OF, PF, SF или ZF. В сочетании с префиксами REPnn при задании необходимой длины в СХ инструкция SCAS может работать со строками солидной длины. Есть три разновидности инструкции SCAS: SCASB - для сравнения байтов, SCASW - для слов, и SCASD - для двойных слов. Инструкции SCAS особенно полезны в приложениях вроде текстовых редакторов, в которых программа должна просматривать текст в поисках каких-либо фрагментов, например, знаков препинания или пробелов. В следующем примере в строке STRING1 ищется символ "г". Поскольку инструкция SCASB должна продолжать просмотр до тех пор, пока не будет достигнуто равенства в ходе очередного сравнения (искомый символ найден) или пока СХ не будет содержать 0, то в этом случае используется инструкция REPNE SCASB. STRINGl DB CLD MOV MOV } LEA REPE JE . . . 'Interstellar' AL,'r1 СХД2 . DI,STRINGl SCASB выход ;Элементы данных /Просмотр слева направо ;Поиск 'г' в строке ;Инициализация для 12 байто: ;ES:DI ;Нашли ;Не нашли При просмотре строки 'Interstellar' в элементе данных STRINGl SCASB найдет символ V при пятом сравнении. Используя отладчик, можно убедиться, 290 Часть IV. Обработка данных
что после выполнения инструкции REPNE SCASE флаг ZF будет установлен в О, значение в СХ уменьшится до 7, а в DI - увеличится на 5 (значение в DI в конце содержит величину на 1 больше, чем адрес найденногй совпадения). При работе со словами SCASW просматривает строку в памяти в поисках значения, совпадающего со значением в регистре АХ. Если вы использовали инструкции LODSW или MOV для записи в регистр АХ, первый байт будет помещен в AL, а второй - в АН. Поскольку SCASW сравнивает байты в обратном порядке, операция работает корректно. Пример: использование просмотра и замены Может возникнуть потребность заменить определенный символ другим символом, например, для того, чтобы очистить документ от служебных символов, таких, как конец абзаца или табуляция. В следующем примере в строке с именем TESTDATA ищутся символы '*' (звездочки) и заменяются пробелами. Если инструкция SCASB находит звездочку, она завершается. В TESTDATA звездочка содержится по адресу TESTDATA+5. По этому адресу должен быть вставлен пробел, хотя SCASB увеличило значение в DI до TESTDATA+6. Уменьшение DI на 1 с помощью адресации [DI-1] дает правильный адрес для вставки пробела: DATALEN EQU 13 /Длина TESTDATA TESTDATA DB 'Extra*innings' CLD MOV AL,'*' MOV CX,DATALEN LEA DI,TESTDATA REPNE SCASB JNE выход MOV BYTE PTR [DI-1],20H Альтернативное кодирование строковых инструкций Как говорилось выше, если строковая инструкция указывается явно с суффиксом В, W или D, например, MOVSB, MOVSW или MOVSD, ассемблер определяет размер операндов и не требует его явного указания. Можно использовать и другой формат этих инструкций. Например, для инструкции MOVS без суффикса указания типа длину должны указывать операнды. Например, если CHAR1 и CHAR2 определены с помощью DB, инструкция Глава 11. Обработка строковых данных 291 19* ;Поиск слева направо ;Искомый символ ;Длина TESTDATA ;Адрес TESTDATA (EStDI) ;Просмотр строки ;Не найден символ ;Заменяем пробелом
REP MOVS CHAR1/CHAR2 подразумевает перемещение цепочки байтов, начиная с BYTE2, в байты, начиная cBYTEl. Еще один способ использования этих инструкций - с помощью директивы PTR. Если вы загрузите в регистры DI и SI адреса CHAR1 и CHAR2, можно использовать инструкции MOVS следующим образом: LEA DI,CHAR2 LEA SI,CHAR1 REP MOVS ES:BYTE PTR[DI],DS:[SI] Эти форматы используются редко и упомянуты здесь только для полноты изложения. Повторение фрагмента Инструкция STOS полезна для заполнения области памяти определенными байтами, словами или двойными словами. Однако для повторения элемента большей длины определенное количество раз используется инструкция MOVS с небольшой модификацией. Допустим, необходимо вывести строку следующего вида: 1****1|****||****||****| |****| Вместо того чтобы определять всю строку, можно определить только первые 6 байт в качестве повторяющегося эталона, расположив их в памяти непосредственно перед заполняемой областью. Вот инструкции: PATTERN DB •|****|• ;Фрагмент, подлежащий повторению DISPAREA DB 42 DUP (?) CLD ;Слева направо MOV СХ,21 ;21 слово LEA DI,DISPAREA ;Приемник LEA SI,PATTERN ;Источник REP MOVSW ;Переместить символы При выполнении программы инструкция MOVSW перемещает первое слово из PATTERN (|*) в первое слово DISPAREA, а затем перемещает второе (**) и третье (*|) слова: |****| |****| т т PATTERN DISPAREA 292 Часть IV. Обработка данных
В этот момент DI содержит адрес смещения DISPAREA+6, a SI содержит адрес PATTERN+6, такой же, как DISPAREA. Теперь операция автоматически повторит фрагмент, перемещая первое слово в DISPAREA+6, DISPAREA+2 - в DISPAREA+8, DISPAREA+4 - в DISPAREA+10, и так далее. Постепенно будет заполнена вся строка DISPAREA: |****||****||****||****||****| |****| т т т т PATTERN DISPAREA+6 DISPAREA+12 DISPAREA+42 Этот метод можно использовать для повторения фрагмента любое количество раз. Фрагмент может иметь любую длину, но должен быть определен непосредственно перед полем назначения. Ключевые моменты • Для использования строковых инструкций MOVS, STOS, CMPS, SCAS программа типа .ЕХЕ должна инициализировать сегментный регистр ES. • Строковые инструкции с суффиксами В, W и D обрабатывают, соответственно, байты, слова или двойные слова. • Флаг направления DF указывает направление обработки строки: инструкция CLD устанавливает DF в 0, и обработка выполняется слева направо; инструкция STD устанавливает DF в 1, и обработка выполняется справа налево. • Префикс REP задает выполнение инструкции указанное в регистре СХ число раз. • Обычно префикс REP используется с инструкциями MOVS и STOS, a условные префиксы REP (REPE или REPNE) - с CMPS или SCAS. • Инструкции CMPSW и SCASW переставляют байты в словах, над которыми проводятся операции. • При работе инструкций справа налево адресация начинается с крайнего справа (с наибольшим адресом) элемента данных. Например, если поле COTITLE имеет длину 10 байтов, то для обработки справа налево побайтно необходимо загрузить адрес COTITLE+9 инструкцией LEA. Для обработки слов адрес должен быть СОТПЪЕ+8, поскольку будут запрошены байты с адресами COTITLE+8 и COTITLE+9. Глава 11. Обработка строковых данных 293
Вопросы и упражнения 1. Строковые инструкции подразумевают, что операнды связаны с парами регистров ES:DI и DS:SI. Укажите, какие регистры связаны с (а) операндами 1 и 2 инструкции MOVS, (б) операндом 1 инструкции LODS, (в) операндами 1 и 2 инструкции CMPS, (г) операндом 2 инструкции STOS. 2. Для строковых инструкций, например, REP MOVSB, (а) как устанавливается число повторений, (б) как устанавливается направление обработки строки - слева направо и справа налево? 3. В этой главе приведены последовательности инструкций, которыми можно заменить инструкции MOVSB, STOSB и LODSB с префиксом REP. Предложите эквивалентный код для инструкций, работающих со словами. 4. Модифицируйте программу, приведенную на рис. 11.2, следующим образом: инструкции MOVSB и MOVSW должны обрабатывать данные справа налево. Воспользуйтесь отладчиком и отследите выполнение программы. Обратите внимание на содержимое сегмента данных и регистров. 5. Какие условия определяют ход выполнения инструкций (a) REPNE CMPSW, (б) REPE SCASW. 6. Используя определенные ниже строки в качестве данных, укажите инструкции, необходимые для: (а) Перемещения BUSTITLE в WORKSPACE, слева направо; (б) Перемещения BUSTITLE в WORKSPACE, справа налево; (в) Загрузки 4-го и 5-го байта из BUSTITLE в регистр АХ; (г) Сохранения АХ в WORK_SPACE, начиная с адреса W0RKSPACE+12; (д) Сравнения BUS_TITLE с WORK_SPACE; (е) Просмотра BUSTITLE в поисках первого пробела и, после обнаружения, перемещения его в СН. BUS_TITLE DB 'Computer Wizards' WORK_SPACE DB 16 DUP (2OH) 7. Модифицируйте фрагмент программы, рассмотренной в параграфе "Инструкция SCAS: просмотр строки" этой главы, так, чтобы операция просматривала STRING1 в поисках пары символов st. Обратите внимание, что st не появляется в строке STRING1 как единое слово, так как строка делится на слова следующим образом: In/te/rs/te/11/ar/. Есть два возможных решения: (а) использовать SCASW дважды, первый раз - начиная с адреса STRING, a второй - начиная с STRING1+1, (б) использовать SCASB и при обнаружении s сравнить следующий байт с t. 294 Часть IV. Обработка данных
8. Определите 5-байтовый элемент данных, содержащий шестнадцатеричное значение C9CDCDCDBB. Используйте инструкцию MOVSB для дублирования содержимого этого элемента 12 раз в 60-байтовую строку. Выведите результат на экран. 9. Напишите программу, реализующую следующую процедуру. Следует определить строку NAME1, содержащую 'Computer Tech', и NAME2, содержащую 13 пробелов. Необходимо использовать LODSB для последовательного чтения символов первой строки слева направо и STOSB - для сохранения каждого прочитанного символа в NAME2 справа налево, чтобы NAME2 * содержала ту же строку в обратном порядку. Для реализации этой процедуры необходимо изменять состояние флага направления (DF). Оттранслируйте программу и проверьте выполнение. 10. Модифицируйте программу, приведенную на рис. 11.3, так, чтобы она проверяла состояние клавиши Ins. Если она находится в выключенном состоянии, то введенный символ должен замещать текущий символ; если - во включенном, то символы, расположенные правее, перемещаются на 1 вправо, а крайний справа,символ удаляется. Оттранслируйте программу и проверьте выполнение. Глава 11. Обработка строковых данных 295
Глава 12 Арифметика: обработка двоичных чисел Цель: рассмотреть способы и возможности сложения, вычитания, умножения и деления двоичных чисел. Введение В этой главе рассматриваются сложение, вычитание, умножение и деление знаковых и беззнаковых двоичных чисел. В тексте приведено большое количество примеров, иллюстрирующих излагаемый материал и предупреждающих о различных трудностях, с которыми можно столкнуться при работе с двоичной арифметикой. В главе 13 рассмотрены специальные механизмы преобразования чисел из двоичной формы в двоично-десятичную и в формат ASCII. Хотя мы привыкли работать с числами, представленными в десятичной системе счисления (с основанием 10), микрокомпьютеры обрабатывают только двоичные числа (в системе с основанием 2). В этой главе рассматриваются следующие инструкции: ADC - сложение с переносом; ADD - сложение; CBW - преобразование байта в слово; CDQ - преобразование двойного слова в счетверенное; CWD - преобразование слова в двойное слово; CWDE - преобразование слова в расширенное двойное слово; DIV - беззнаковое деление; IDIV - знаковое деление; IMUL - знаковое умножение; MUL - беззнаковое умножение; NEG - изменение знака; SBB - вычитание с переносом; SUB - вычитание. 296 Часть IV. Обработка данных
Обработка знаковых и беззнаковых двоичных чисел Некоторые числовые элементы данных - например, порядковые номера объектов и номера дней в году - беззнаковые. Другие - например, температура или баланс на счету - знаковые и могут содержать как положительные, так и отрицательные величины. Некоторые знаковые числовые величины у например, цена товара или число пи - считаются всегда положительными. В следующей таблице приведены диапазоны знаковых и беззнаковых допустимых значений для целочисленных величин разного типа: Формат Беззнаковый Знаковый Байт (8 разрядов) 0 . . . 255 -128 ... 127 Слово (16 разрядов) 0 ... 65535 -32768 ... 32767 Двойное слово (32 разряда) 0 . . . 4294967295 -2147483648 ... 2147483647 В беззнаковых числовых элементах данных все биты считаются битами данных. В знаковых - старший (первый слева) бит считается знаковым. Заметьте, что инструкции ADD и SUB не различают знаковых и беззнаковых величин - операции сложения и вычитания в любом случае выполняются корректно. В следующем примере иллюстрируется сложение двух двоичных чисел, содержащих значения, представленные в знаковом и беззнаковом видах. Верхнее число в старшем (знаковом) бите содержит 1, то есть будет интерпретироваться как отрицательное, если учитывать знак. В беззнаковом виде оно будет эквивалентно десятичному числу 249, в знаковом - -7. Эта операция сложения не меняет состояния флагов переноса (CF) или переполнения (OF): Двоичная форма 11111001 +00000010 11111011 Беззнаковая десятичная 249 + 2 251 Знаковая десятичная -7 +2 -5 Флаг переполнения (OF) 0 Флаг переноса (CF) 0 Как видно, результат операции сложения корректен как при интерпретации его в беззнаковой форме, так и в знаковой. Однако в беззнаковой форме биты результата представляют десятичное число 251, а в знаковой - -5. Вообще, биты элемента данных означают то, что они должны означать, по мнению программиста, и должны обрабатываться соответствующим образом. Глава 12. Арифметика: обработка двоичных чисел 297
Арифметический перенос Арифметическая операция передает результирующий бит знака (0 или 1) во флаг переноса (CF). Соответственно, флаг устанавливается в 1, если бит знака равен 1. Однако если перенос производится при работе с беззнаковыми данными, полученный результат будет неправильным. В следующем примере сложение приводит к переносу: Двоичная форма 11111100 +00000101 (1)00000001 Беззнаковая десятичная 252 + 5 I1 (неправильно) Знаковая десятичная -4 + 5 1 (правильно) Флаг переполнения (OF) 0 Флаг переноса (CF) 1 Действия с беззнаковыми данными привели к неправильному результату из-за переноса старшего разряда, но операции с учетом знака выполнены правильно. Арифметическое переполнение Арифметическая операция устанавливает в единицу флаг переполнения (OF), когда перенос в знаковый бит не создает переноса из знакового бита, или перенос из знакового бита возникает без переноса в знаковый бит. Если переполнение возникает при работе со знаковыми числами, получается некорректный результат (из-за переполнения в знаковом бите). Этот эффект иллюстрируется в следующем примере: Двоичная форма 01111001 +00001011 10000100 Беззнаковая десятичная 121 + 11 132 (правильно) Знаковая десятичная + 121 + 11 -124 (неправильно) Флаг переполнения . (OF) 1 Флаг переноса (CF) 0 В ходе одной операции могут быть установлены в единицу и флаг переноса, и флаг переполнения. В следующем примере перенос делает неправильным результат при суммировании беззнаковых данных, а переполнение - результат суммирования знаковых: Двоичная форма 11110110 +10001001 (1)01111111 Беззнаковая десятичная 246 + 137 127 (неправильно) Знаковая десятичная -10 -119 + 127 (неправильно) Флаг переполнения (OF) 1 Флаг переноса (CF) 1 298 Часть IV. Обработка данных
Из только что рассмотренных примеров видно, что следует четко представлять себе величину подлежащих обработке чисел и выбирать числовые элементы данных подходящих типов. Сложение и вычитание двоичных чисел Инструкции ADD и SUB выполняют сложение и вычитание двоичных чисел в форматах байтов, слов и двойных слов. Вот формат их использования: [метка:] [метка:] [метка:] [метка:] ч [метка: ] ADD/SUB ADD/SUB ADD/SUB ADD/SUB' ADD/SUB регистр, регистр память, регистр регистр, память регистр, непосредственное значение память, непосредственное значение Инструкции ADD и SUB устанавливают флаги CF и OF так, как описано выше. Как и большинство других инструкций, они не могут выполнять напрямую операции память-память. Как говорилось в главе 1, отрицательное двоичное число представляется в форме двоичного дополнения - с инвертированием битов и прибавлением 1. В следующем простом примере инструкции ADD и SUB используются для обработки чисел в форматах байта и слова. BYTEl DB 24H ; Элементы данных WORD1 DW 4000H MOV CL,BYTE1 MOV DL,40H ADD CL,DL SUB CL,2 0H ADD BYTEl,BL MOV CX,WORDl MOV DX,2000H SUB CX,DX SUB CX,124H ADD WORDl,DX Переполнения Переполнения связаны с арифметическими операциями, особенно с обработкой знаковых данных. Поскольку байт предоставляет только семь битов для разрядов числа и один - для знака, то диапазон допустимых значений - всего от -128 до +127, и арифметическая операция может легко вызвать переполнение ;Обработка байтов ;Регистр-регистр ;Непосредственное значение-регистр ;Регистр-память /Обработка слов ;Регистр-регистр ;Непосредственное значение-регистр ;Регистр-память Глава 12. Арифметика: обработка двоичных чисел 299
байтового регистра. Если сумма в регистре AL выйдет за допустимые пределы, это может вызвать неожиданные эффекты. Например, если AL содержит 60Н, то инструкция ADD AL,20H запишет в AL число 80Н в качестве результата. Сложив два положительных значения, вы ожидаете положительного результата, но операция устанавливает флаг знака (SF) в 1, указывая на отрицательный результат, и флаг переполнения (OF). Причина? Значение 80Н, или 10000000, является отрицательным числом; вместо 128 оно интерпретируется как -127. Проблема в том, что AL имеет недостаточную емкость для хранения такого числа - нужно использовать регистр АХ, как показано ниже. Преобразование байтов в слова В предыдущем разделе вы видели, как сложение значений 20Н и 60Н в регистре AL привело к неправильному результату. Лучшее решение - провести эту операцию с использованием регистра АХ. Инструкция, преобразующая байтовую величину в соответствующее ей слово - CBW (Convert Byte into Word - преобразовать байт в слово). Она автоматически распространяет знаковый бит байта AL на старший байт АН слова АХ. Заметьте, что инструкция CBW не имеет операндов и привязана к регистру АХ. В следующем примере инструкция CBW распространяет знак (0) из регистра AL на регистр АН, создавая в регистре АХ число 0060Н. После этого к содержимому регистра добавляется число 20Н, и в регистре АХ будет содержаться корректный результат 0080Н (или +128): АН AL хх 60Н CBW /Распространяем знак из AL в АН 00 60 ADD AX,2ОН /Прибавляем к АХ 00 80 В этом примере получается такой же результат, как и в предыдущем параграфе, но операция суммирования в регистре АХ не приводит к результату, расцениваемому как некорректный или отрицательный. Поскольку АХ предоставляет 15 битов для данных и 1 для знака, диапазон доступных значений в нем составляет от -32768 до 32767. Преобразовывать байты в слова могут также инструкции MOVZX (для беззнаковых величин) и MOVSX (для знаковых), используемые следующим образом: * MOVZX CX,BYTEVAL ; Байт - в CL, нули - в СН MOVSX WORDVAL,DL ;Байт - в слово, заполнение разрядов ; значением знакового бита Эти инструкции описаны в главе 6. 300 Часть IV. Обработка данных
Преобразование слов в двойные слова Инструкция CWD (Convert Word to Doubleword - преобразовать слово в двойное слово) используется для преобразования знакового числа в формате слова в двойное слово путем заполнения знаковым битом из регистра АХ в регистр DX. Заметьте, что эта инструкция не использует операнды и привязана к паре регистров DX:AX. Вот пример ее использования: MOV АХ, WORD 1 ;Поместить слово в АХ CWD ;Расширить до двойного слова в DX:AX Инструкция CWDE (Convert Word to Extended Doubleword - Преобразовать слово в расширенное двойное слово) используется для преобразования знакового числа в формате слова в двойное слово при помощи распространения знакового бита регистра АХ на регистр ЕАХ. Вот пример: MOV АХ,WORD1 ;Поместить слово в АХ CWDE ;Расширить до двойного слова в ЕАХ Инструкции MOVSX и MOVZX также можно использовать для преобразования слов в двойные слова: MOVZX ECX,WORDVAL ;Слово - в СХ, нули в старших разрядах ЕСХ MOVSX DBLWORD,DX ;Слово - в DX, заполнение разрядов ; значением знакового бита Инструкция CDQ (Convert Doubleword to Quadword - преобразовать двойное слово в счетверенное) используется для преобразования знакового числа в формате двойного слова в счетверенное слово. Цель достигается заполнением регистра EDX знаковым битом из регистра ЕАХ. Эта инструкция также не использует операнды и привязана к паре регистров EDX:EAX. Вот пример: MOV EAX,DBLWORD /Поместить двойное слово в ЕАХ CDQ ;Расширить до счетверенного слова в EDX:EAX Арифметические действия с двойными словами Как показано выше, большие числа могут не помещаться в элементы данных, имеющие размерность слов. Для них могут потребоваться двойные слова или элементы данных еще большей разрядности. При выполнении арифметических действий над такими величинами следует принять во внимание, что в памяти компьютера они хранятся с переставленными в обратном порядке байтами (и словами). Вспомним, что ассемблер автоматически преобразует определенные в тексте программы числовые слова в последовательность с переставленными байтами. Так, например, число 0134Н в тексте программы будет сохранено в памяти в виде 3401Н. В следующем примере демонстрируется процедура суммирования и перемещения двойных слов: Глава 12. Арифметика: обработка двоичных чисел 301
DBLW0RD1 DD DBLWORD2 DD DBLWORD3 DD MOV *ADD MOV 0123ВС62Ц 0012553AH 0 EAX,DBLWORDl EAX,DBLWORD3 DBLWORD3,EAX Определение дво Перемещение и сложение двойных слов Ассемблер автоматически располагает определенные в тексте программы числовые элементы данных в последовательности с переставленными байтами (и словами). Однако в некоторых приложениях данные могут быть определены в виде слов..Например, для DBLWORD1 из предыдущего примера эквивалентом будет определение двух слов в обратном порядке: DW 0BC62H DW 0123H Ассемблер сохранит эти значения в памяти в виде |62ВС|2301|, и эти байты можно воспринимать как значение в формате двойного слова. Рассмотрим два способа выполнения арифметических операций над числами в этом формате. Первый способ прост и имеет довольно ограниченное применение, второй - более замысловат и обладает большими возможностями. Вот данные, используемые примерами: ;Элементы данных WORD1A WORDIB WORD2A WORD2B WORD3A W0RD3B DW DW DW DW DW DW 0BC62H Q123H 553AH 0012H ? ? Рассматриваемая программа должна суммировать значения: WORDlB:WORDlA 0123 ВС62Н WORD2B:WORD2A + 0012 553АН WORD3B:WORD3A 013 6 119CH Из-за перестановки байтов в памяти программа определяет свои данные в следующих друг за другом в обратной последовательности словах: ВС62 0123 и 553А 0012, соответственно. Ассемблер сохранит их в памяти как двойные слова с обратной последовательностью байт: WORD1A и WORD1B: 62ВС 2301 ч WORD2A и WORD2B: 3A55 1200 В первом примере одна пара слов (WORD 1А и WORD IB) прибавляется к другой паре (WORD2A и WORD2B), а результат сохраняется в третьей паре (WORD3A и WORD3B): * 302 Часть IV. Обработка данных
MOV ADD MOV MOV ADC MOV AX,W0RD1A AX,WORD2A WORD3A,AX AX,WORDlB AX,WORD2B WORD3B,AX •Суммирование младших разрядов ;Суммирование старших разрядов с переносом В первых строках примера WORD2A прибавляется к WORD 1А в регистре АХ (это - младшие разряды), после чего сумма сохраняется в WORD3A. После этого с учетом сформированного в предыдущем сложении переноса суммируются WORD2B и WORD IB, тоже с использованием регистра АХ. После этого сумма сохраняется в WORD3B. Рассмотрим выполнение примера подробнее. Первые операции MOV и ADD переставляют байты в АХ и суммируют слова: W0RD1A: W0RD2A: Результат (W0RD3A): ВС62Н +553АН (D119CH (в слове W0RD3A сохраняется 9С11Н) Поскольку сумма значений в WORD1A и WORD2A превышает емкость регистра АХ, флаг CF будет установлен в.1, указывая на наличие переноса из старшего разряда. После этого в примере суммируются слова старших разрядов, но на этот раз используется инструкция ADC (Add with Carry - суммировать с переносом) вместо ADD. ADC суммирует две величины и флаг CF (поскольку он установлен в 1, то к результату добавляется 1): WORD1В: W0RD2B: Перенос: Результат: 0123Н +0012Н + _ 1Н 0136Н (в слове W0RD3B сохраняется 3601Н) Если воспользоваться отладчиком, можно увидеть, что после выполнения примера в АХ будет содержаться сумма в виде 0136Н, в WORD3B - 3601Н (0136Н с переставленными байтами), а в WORD3A - значение 9С11Н. Второй пример демонстрирует более замысловатый подход к сложению чисел, позволяющий суммировать числа произвольной длины, хотя в данном примере этот подход используется для суммирования тех же пар слов, что и раньше - WORD1А и WORD IB, WORD2A и WORD2B: /Установка в 0 флага CF /Установка количества итераций /Младшие разряды первого слагаемого ;Младшие разряды второго слагаемого ;Младшие разряды суммы L20: MOV AX, [SI] ;Поместить слово в АХ CLC MOV LEA LEA LEA ex, si, DI, вх, ,02 ,WORD1A ,WORD2A fWORD3A Глава 12. Арифметика: обработка двоичных чисел 303
ADC MOV INC INC INC INC INC INC LOOP AX,[DI] [BX],AX SI SI DI DI BX BX L20 /Сложение с переносом в АХ ;Сохранить слово-результат ;Перейти к адресам следующих слов /Повторить операцию со следующими разрядами В примере регистры SI, DI и ВХ используются в качестве индексных для обращений к WORD1, WORD2 и WORD3, соответственно. Инструкции в цикле повторяются столько раз, сколько необходимо для суммирования всех разрядов обрабатываемых чисел. В примере суммируются двойные слова (32 разряда), и цикл повторяется 2 раза. При первом проходе суммируются младшие разряды, при втором - следующие по старшинству, и т.д. При каждом проходе цикл обеспечивает приращение адресов в индексных регистрах на 2 для обращений к следующим разрядам. При этом используются инструкции INC, а не ADD: инструкции ADD изменили бы флаг переноса CF, приведя к получению неправильного результата, a INC не оказывает влияния на флаг переноса. Поскольку используется цикл, в примере присутствует только одна инструкция ADC. В начале программы инструкция CLC (Clear Carry - очистить перенос) используется для обнуления флага CF. Результаты в WORD3A и WORD3B и регистре АХ такие же, как и в предыдущем примере. Для обеспечения корректной работы этого метода: (1) слова, содержащие разряды слагаемых, должны располагаться в памяти друг за другом; (2) регистр СХ должен содержать длину слагаемого в словах; и (3) слова должны обрабатываться от младших к старшим. Для вычитания величин размерностью в несколько слов используется эквивалент инструкции ADC в вычитании - SBB (Subtract with Borrow - вычитание с переносом). Ниже приведены форматы использования инструкций ADC и SBB: [метка: ] [метка:] [метка:] [метка: ] [метка:] ADC/SBB ADC/SBB ADC/SBB ADC/SBB ADC/SBB регистр, регистр память, регистр регистр, память регистр, непосредственное значение память, непосредственное значение Для суммирования счетверенных слов можно использовать модификацию описанного способа с использованием двойных слов - для хранения данных, и регистра ЕАХ - для получения результата. 304 Часть IV. Обработка данных
Умножение двоичных чисел Инструкция MUL (Multiply - умножить) используется для умножения беззнаковых величин, а инструкция IMUL (Integer Multiply - умножить целые) - для умножения знаковых. Обе инструкции могут изменять состояние флагов переноса CF и переполнения OF. Как программист, вы можете задавать формат данных, подлежащих обработке, и на вас возлагается функция выбора подходящей инструкции умножения. Формат MUL и основной формы IMUL следующий: [метка:] MUL/IMUL регистр/память Можно умножать байт на байт, слово на слово и двойное слово на двойное слово. IMUL предоставляет дополнительные возможности для перемножения двойных слов и непосредственных значений, рассмотренные ниже. Умножение байта на байт Для перемножения двух байтовых величин множимое помещается в регистр , AL, а множитель - в байт в памяти или в другой регистр. Например, инструкция MUL DL умножает содержимое AL на содержимое DL. Произведение помещается в регистр АХ. Операция игнорирует и уничтожает данные, ранее хранившиеся в АН. Перед умножением: После умножения: АН (игнорируется) AL (множимое) АХ (произведение) Умножение слова на слово Для перемножения двух слов множимое помещается в регистр АХ, а множитель - в слово в памяти или в другой регистр. Например, инструкция MUL ВХ умножает содержимое АХ на содержимое ВХ. Младшие разряды произведения помещаются в регистр АХ, а старшие - в регистр DX. Операция уничтожает данные, ранее хранившиеся в DX. Перед умножением: После умножения: DX (игнорируется) DX (старшие разряды) АХ (множимое) АХ (младшие разряды) Глава 12. Арифметика: обработка двоичных чисел 20 —5-741 305
Умножение двойных слов Для перемножения двух двойных слов множимое помещается в регистр ЕАХ, а множитель - в двойное слово в памяти или в другой регистр. Например, инструкция MUL ЕВХ умножает содержимое ЕАХ на содержимое ЕВХ. Младшие разряды произведения помещаются в регистр ЕАХ, а старшие - в регистр EDX. Операция уничтожает данные, ранее хранившиеся в EDX. Перед умножением: После умножения: EDX (игнорируется) EDX (старшие разряды) ЕАХ (множимое) ЕАХ (младшие разряды) Размеры полей Операнд инструкций MUL и IMUL указывает только на множитель, определяя размер элементов. Инструкция подразумевает, что множимое хранится в регистрах AL, АХ или ЕАХ, в зависимости от размера (байт, слово или двойное слово) операнда. В последующих примерах множитель в регистре представляет собой байт, слово или двойное слово. Инструкция MUL CL MUL BX MUL ЕВХ г Множитель байт слово - двойное слово В следующих примерах множители BYTE1 WORD1 DWORD1 Инструкция MUL BYTE1 MUL WORD1 MUL DWORD1 DB DW DD ? ? ? Множитель BYTE1 WORD1 DWORD1 Множимое AL АХ ЕАХ ' Произведение АХ DX:AX EDX:ЕАХ определены в памяти: ;Значение - ;Значение - ;Значение - байт слово двойное Множимое AL АХ ЕАХ СЛОВО Произведение АХ DX:AX EDX: ЕАХ Беззнаковое умножение: инструкция MUL Инструкция MUL реализует перемножение беззнаковых чисел. В следующих фрагментах программ приведены четыре примера использования MUL: для байтов, слов и двойных слов. Данные определяются следующим образом: BYTEl DB 8 ОН BYTE2 DB 4 ОН WORDl DW 80ООН WORD2 DW 200ОН 306 Часть IV. Обработка данных
DW0RD1 DD 00018402H DWORD2 DD 00012501H В первом примере 80Н (128) умножается на 40Н (64). Результат, помещенный вАХ,равен2000Н(8192): MOV AL,BYTE1 ;Байт умножается на байт MUL BYTE2 /Результат - в АХ Во втором примере результат 1000 0000Н получается в паре регистров DX:AX: MOV AX,WORDl ;Слово умножается на слово MUL WORD2 ; Результат - в DX:AX В третьем примере слово умножается на байт с предварительным преобразования BYTE1 в слово: MOVZX AX,BYTE1 ;Байт умножается на слово MUL WORD1 /Результат - в DX:AX Поскольку значения считаются беззнаковыми, в примере полагается, что биты регистра АН равны 0. (При, использовании инструкции CBW может возникнуть проблема, если старший бит AL равен 1 - при распространении единичных битов на регистр АН значение беззнаковой величины изменится). Произведение в DX:AX равно 0040 0000Н. В четвертом примере ЕАХ используется для перемножения двойных слов: MOV EAX/DWORD1 /Двойное слово умножается на двойное слово MUL DWORD2 Произведение в паре регистров EDX:ЕАХ равно 0000 0001 ВС 17 СЕ02Н. Умножение с учетом знака: инструкция IMUL Инструкция IMUL реализует перемножение знаковых чисел. В следующих фрагментах программ приведены четыре примера использования IMUL для умножения величин из предыдущего раздела с учетом знака. В первом примере 80Н (отрицательное число) умножается на 40Н (положительное число): MOV AL/BYTE1 ;Умножение байта на байт IMUL BYTE2 /Результат - в АХ Результат будет равен E000H. С теми же исходными величинами MUL возвращает величину 2000Н. Здесь явно демонстрируется разница между инструкциями MUL и MUL. MUL расценивает число 80Н как +128, a MUL - как -128. Произведение -128 и 64 равно -8192, или E000H. (Попробуйте преобразовать E000H в двоичную форму, инвертируйте биты, добавьте 1 и просуммируйте веса единичных разрядов.) Глава 12. Арифметика: обработка двоичных чисел 307 20*
Во втором примере 8000Н (отрицательная величина) умножается на 2000Н (положительную величину). Результат в паре регистров DX:AX равен F000 0000Н. Такой же результат, но с обратным знаком, от применения инструкции MUL. MOV АХ,WORD1 ;Умножение слова на слово IMUL WORD /Результат - в DX:AX В третьем примере BYTE1 преобразуется в слово в регистре АХ. Поскольку числа полагаются знаковыми, в примере для преобразования байта в слово используется инструкция MOVSX. После этого значение 80Н в AL преобразуется в FF80H в АХ. Поскольку множитель WORD1 также представляет собой отрицательное число, результат должен быть положителен. И действительно результат 0040 0000Н в паре регистров DX:AX такой же, как и при использовании инструкции MUL для беззнакового умножения. MOVSX AX/BYTE1 ;Байт умножается на слово IMUL WORD1 ; Произведение - в паре DX:AX В четвертом примере регистр ЕАХ используется для перемножения двойных слов. Произведение в паре EDX:EAX равно 0000 0001 ВС17 СЕ02Н: MOV EAX,DWORDl ;Двойное слово на двойное слово IMUL DWORD2 /Результат - в паре EDX:EAX Вообще говоря, если знаковые биты множимого и множителя совпадают, применение инструкций MUL и IMUL дает одинаковый результат. Но если знаки не совпадают, MUL дает положительный результат, a IMUL - отрицательный. Поэтому при написании программ необходимо учитывать формат данных и использовать соответствующие инструкции. Попробуйте использовать отладчик для исполнения приведенных выше примеров. Другие форматы инструкции IMUL В процессорах 80286 и 80386 появились три дополнительных формата использование инструкции IMUL, допускающие использование в операндах непосредственных значений и сохранение результатов в регистрах, отличных от АХ. Эти инструкции можно использовать для умножения знаковых и беззнаковых чисел, поскольку результаты будут теми же. Операнды должны иметь одинаковую длину: 16 (слово) или 32 (двойное слово) разряда. Вот эти форматы: 16-разрядное непосредственное значение 32-разрядное непосредственное значение 16/32-разрядное (метка: ] [метка: ] [метка: ] IMUL регистр, непосредственное значение IMUL регистр, память, непосредственное значение IMUL регистр, регистр/память 308 Часть IV. Обработка данных
16-разрядная операция IMUL с непосредственными значениями Первый операнд (регистр) содержит множимое, второй операнд (непосредственное значение) - множитель. Произведение помещается в регистр, указанный в первом операнде. Если разрядность произведения превышает разрядность регистра, устанавливаются в 1 флаги переноса (CF) и переполнения (OF). 32-разрядная операция IMUL с непосредственными значениями Инструкция в этом формате имеет три операнда в формате двойных слов. Второй операнд (память) содержит множимое, а третий (непосредственное значение) - множитель. Произведение помещается в регистр, указанный в первом операнде. 16/32-разрядная операция IMUL Первый операнд (регистр) содержит множимое, второй операнд (регистр/память) - множитель. Произведение помещается в регистр, указанный в первом операнде. Вот примеры применения инструкции IMUL в этих форматах: Размер Инструкция Множимое Множитель Произведение 16-разрядное IMUL DX,25 DX 25 DX непосредственное значение 32-разрядное IMUL MULTCAND 25 ЕСХ непосредственное ЕСХ, MULTCAND, 25 значение 16/32-разрядное IMUL ВХ,СХ ВХ СХ ВХ 16/32-разрядное IMUL EBX,EDX EBX EDX ЕВХ 16/32-разрядное IMUL EBX DWORDVAL ЕВХ EBX,DWORDVAL Перемножение двойных слов Обычно операции умножения производятся над данными в формате байтов, слов или двойных слов. Как мы уже видели, максимальное значение знакового положительного числа в формате слова равно +32 767. Перемножение больших величин на процессорах до 80386 требует дополнительных ухищрений. Обычно оно реализуется путем раздельного умножения слов, а затем - суммирования произведений. Примеры, рассмотренные далее, стоит изучить, поскольку их можно адаптировать к 32-разрядным регистрам, например, ЕАХ, и использовать Глава 12. Арифметика: обработка двоичных чисел 309
для умножения еще больших чисел. В первом примере четырехразрядное десятичное число умножается на двухразрядное число: 1365 х 12 16380 Что, если бы мы могли перемножать только двухразрядные числа? Тогда можно было бы перемножить по отдельности 13и12и65и12, например, так: 13 65 х 12 х 12 156 780 После этого просуммируем два произведения, помня, что поскольку 13 - число, старшее на 2 разряда, первое произведение, в самом деле, представляет собой 15600: 15600 (13 х 12 х 100) + 780 (65 х 12) 16380 Программа на языке ассемблера может использовать тот же подход, за исключением того, что данные состоят из слов в шестнадцатеричном формате. В следующем разделе рассмотрены приемы перемножения двойного слова на слово и двойного слова на двойное слово. Двойное слово на слово В следующем фрагменте программы двойное слово умножается на слово. Множимое MULTCAN состоит из двух слов, содержащих 3206Н и 2521Н, соответственно. Причина определения двух элементов данных в формате DW вместо одного в формате DD состоит в том, что необходимо с помощью инструкций MOV перемещать слова в регистр АХ. Значения определяются в формате с переставленными байтами, и ассемблер сохраняет значения, еще раз переставив байты. Поэтому MULTCAN, определенный^ виде 3206 2521Н, сохранится в виде 2125 0632Н. MULTCAN DW 2521H ;Элементы-данных DW ' 3206Н MULTPLR DW 6400H PRODUCT DW 0, 0, 0 ;Три слова для результата Множитель MULTPLR содержит 6400Н. Результат будет сохраняться в трех словах с идентификатором PRODUCT. Первая инструкция MUL умножает первое (левое) слово MULTCAN на MULTPLR; произведение, 0Е80 Е400Н, сохраняется в словах с адресом PRODUCT и PRODUCT+2: 310 Часть IV. Обработка данных
MOV MUL MOV MOV AX,MULTCAN MULTPLR PRODUCT,AX PRODUCT+2,DX Умножить первое слово множимого Сохранить результат Вторая инструкция MUL умножает на множитель MULTPLR второе слово MULTCAN; результат равен 138А 5800Н. MOV MUL ADD ADC AX,MULTCAN+2 MULTPLR PRODUCT+2,AX PRODUCT+4,DX Умножить второе слово множимого Прибавить результат ко второму и третьему словам результата В конце примера суммируются два результата, примерно так: Произведение 1: Произведение 2: Сумма 0000 0Е80 Е400 + 138А5800 138А6680Е400 Поскольку первая инструкция ADD может сгенерировать перенос, вторая операция суммирования выполняется инструкцией ADC (суммирование с учетом переноса). Числовые данные сохраняются в формате с переставленными данными, и PRODUCT будет в действительности содержать 00Е4 8066 8А13. Программа требует, чтобы в третьем слове PRODUCT изначально содержался ноль. Умножение двойных слов Умножение двух двойных слов с помощью пары регистров DX:AX требует выполнения четырех операций умножения: Множимое слово 2 слово 2 слово 1 слово 1 X X X X Множитель слово 2 слово 1 слово 2 слово 1 Каждый результат перемножения в паре регистров DX:AX прибавляется к соответствующим словам результата. Пример приведен в следующем фрагменте программы. MULTCAND содержит 3206 2521Н, MULTPLER - 6400 0А26Н, а PRODUCT состоит из четырех слов: MULTCAND DW DW MULTPLER DW DW PRODUCT DW 2521H 3206H 0A26H 6400H 0, 0, 0, 0 ;Элементы данных ;Четыре слова Глава 12. Арифметика: обработка двоичных чисел 311
Хотя общая логика такая же, как и при умножении двойного слова на слово, есть одна особенность: после пары инструкций ADD/ADC используется еще одна инструкция ADC, добавляющая к произведению 0. Первая инструкция ADC может сама по себе сгенерировать перенос, который последующие инструкции могут очистить. Поэтому вторая инструкция ADC добавляет к произведению 0, если переноса нет, и 1, если перенос есть. Последняя пара ADD/ADC не требует дополнительной инструкции ADC - поскольку PRODUCT имеет достаточный размер для сохранения произведения, то в переносе нет необходимости. Множимое 1 х множитель 1 Сохранить произведение ,-Множимое 1 х ; множитель 2 PRODUCT+2/AX ;Прибавить к сохраненному произведению /Прибавить перенос /•Множимое 2 х ; множитель 1 ,-Прибавить к сохраненному произведению ,-Прибавить перенос /•Множимое 2 х ; множитель 2 ,-Прибавить к сохраненному произведению Окончательный результат, равный 138А 687С 8Е5С ССЕ6Н, сохраняется в PRODUCT в формате с переставленными в обратном порядке байтами. Воспользуйтесь отладчиком для наблюдения за выполнением примера. MOV MUL MOV MOV MOV MUL ADD ADC ADC MOV MUL ADD ADC ADC MOV MUL ADD ADC AX/MULTCAND MULTPLER PRODUCT+0/AX PRODUCT+2/DX AX/MULTCAND MULTPLER+2 PRODUCT+2/AX PRODUCT+4,DX PRODUCT+6,00 AX,MULTCAND+2 MULTPLER PRODUCT+2,AX PRODUCT+4,DX PRODUCT+6,00 AX,MULTCAND+2 MULTPLER+2 PRODUCT+4,AX PRODUCT+6/DX Умножение сдвигами Для умножения на степени двойки (2, 4, 8 и так далее) можно воспользоваться операцией сдвига на необходимое количество разрядов влево. Эта операция может дать результат быстрее, чем обычное умножение. В последующих не связанных между собой примерах множимое хранится в первом операнде, а регистр CL содержит 2: Умножение на 2 (сдвиг на 1 бит влево): SHL АХ,01 Умножение на 4 (сдвиг на 2 бита влево): SHL DX/CL Умножение на 8 (сдвиг на 3 бита влево): SHL WORDVAL,03 312 Часть IV. Обработка данных
В следующем примере показывается сдвиг влево двойного слова в регистрах DX:AX. Хотя в примере выполнен сдвиг на 4 бита, можно адаптировать фрагмент для других величин сдвигов: ;Сдвиг DX влево на 4 бита ;Сохранить АН в BL ;Сдвиг АХ влево на 4 бита ;Сдвиг BL вправо на 4 бита ;Вставка 4 битов из BL в DL Обязательно убедитесь, что при сдвиге не могут быть потеряны значащие разряды. SHL MOV SHL SHR OR DX, BL, АХ, BL, DL, 04 АН 04 04 BL Деление двоичных чисел Инструкция деления двоичных чисел, DIV (Divide), обрабатывает беззнаковые числа, а инструкция IDIV (Integer Divide) выполняет деление знаковых величин. Вы должны выбирать подходящую инструкцию в каждом конкретном случае. Формат этих инструкций таков: [метка: ] DIV/IDIV регистр/память Основные форматы операций деления делят слово на байт, двойное слово на слово и счетверенное слово на двойное слово. Слово на байт Для деления слова на байт делимое помещается в регистр АХ, а делитель - это байт в памяти или в другом регистре. Операция помещает остаток в регистр АН, а частное - в AL. Обратите внимание, что байтовое частное имеет очень маленький размер: максимум +255 (FFH) - для беззнаковых, и +127 (7FH) - для знаковых чисел. Перед делением После деления АХ -^ Делимое :► АН Остаток AL Частное Двойное слово на слово Для деления двойного слова на слово делимое помещается в пару регистров DX:AX, а делимое - в виде слова в память или другой регистр. Операция помещает остаток в DX, а частное - в АХ. Частное в формате слова имеет максимальный размер +65535 (FFFFH) - для беззнаковых, и +32767 (7FFFH) - для знаковых чисел. Глава 12. Арифметика: обработка двоичных чисел 313
Перед делением После деления DX Старшие разряды делимого Остаток АХ Младшие разряды делимого Частное Счетверенное слово на двойное слово Для деления счетверенного слова на двойное слово делимое помещается в пару регистров EDX:EAX, а делимое - в память или другой регистр (в виде двойного слова). Операция помещает остаток в EDX, а частное - в ЕАХ. Перед делением После деления Размеры операнда Операнд инструкций DIV и IDIV указывает на размер делителя, и, соответственно, делимого и результатов. В последующих примерах инструкции DIV делители в регистрах имеют форматы байта, слова и двойного слова: EDX Старшие разряды делимого Остаток ЕАХ Младшие разряды делимого Частное Инструкция DIV CL^ DIV CX DIV EBX Делитель байт слово двойное слово Делимое АХ DX:AX EDX:ЕАХ Частное AL 'АХ ЕАХ Остаток АН DX EDX В последующих примерах инструкций DIV делители определены в памяти следующим образом: BYTE1 WORD1 DWORD1 Инструкция DIV BYTE1 DIV W0RD1 DIV DWORD1 DB ? DW ? DD ? Делитель BYTE1 W0RD1 DWORD1 ,;Байт ;Слово /Двойное слово Делимое АХ DX:AX EDX:ЕАХ Частное AL АХ ЕАХ Остаток АН DX EDX Остаток Результат деления 13 на 4 равен 4 1/3, частное равно 4, а остаток - 1. Заметьте, что калькулятор (и языки программирования высокого уровня) дадут результат 4,333, состоящий из целой (4) и дробной (0,333) частей. Значения 1/3 и 0,333 - дроби, а 1 - остаток. 314 Часть IV. Обработка данных
Использование DIV для деления беззнаковых чисел Инструкция DIV предназначена для деления беззнаковых чисел. В следующих фрагментах программ приведены четыре примера инструкции DIV: деление слова на байт, байта на байт, двойного слова на слово и слова на слово. Вот элементы данных, используемые в примерах: ;Элементы данных BYTE1 BYTE2 W0RD1 W0RD2 W0RD3 DB DB DW DW DW 80Н 16Н 2000Н 0010Н 1000Н В первом примере 2000Н (8192) делится на 80Н (128). Остаток в АН равен ООН, а частное в AL - 40Н (64): MOV AX,WORDl ;Слово/байт DIV BYTE1 ; Частное - в регистре AL., остаток - в АН Второй пример требует преобразования BYTE1 в формат слова. Поскольку значение считается беззнаковым, в примере предполагается, что биты регистра АН следует установить в 0. Остаток в АН равен 12Н, а частное в AL - 05Н. MOVZX AX,BYTE1 ;Байт/байт DIV BYTE2 ;Частное - в регистре AL, остаток - в АН В третьем примере остаток в DX равен 1000Н, а частное в АХ - 0080Н. MOV, DX,WORD2 ;Двойное слово/слово MOV AX,WORD3 /Делимое в DX:AX DIV WORD1 /Остаток - в регистре DX, частное - в АХ В четвертом примере слово преобразуется в двойное слово в регистре DX. После деления, остаток в DX равен 0000Н, а частное в АХ равно 0002Н: MOV SUB DIV АХ,WORD1 DX,PX WORD3 /Слово/слово /Делимое - в DX:AX /Остаток - в регистре DX, частное В АХ Использование IDIV для деления знаковых чисел Инструкция IDIV предназначена для деления знаковых чисел. В следующих фрагментах программ используются те же элементы данных, что и в только что рассмотренных фрагментах, но инструкция DIV заменена инструкцией IDIV. Глава 12. Арифметика: обработка двоичных чисел 315
В первом примере положительное число 2000Н делится на отрицательное 80Н. Остаток в АН равен ООН, а частное в AL - СОН (-64) (вместо +64, получаемое при использовании DIV): MOV АХ, WORD 1 ;Слово/байт IDIV BYTE1 ;Частное - в регистре AL, остаток - в АН Во втором примере частное равно FBH (-5), а остаток - ЕЕН (-18): MOVZX АХ, BYTE1 ;Байт/байт IDIV BYTE2 ; Частное - в регистре AL, остаток - в АН В третьем примере частное равно 0080Н (128), а остаток - 1000Н (4096): MOV DX,WORD2 ;Двойное слово/слово MOV AX,WORD3 ; Делимое - в DX:AX IDIV WORD1 ;Остаток - в регистре DX, частное - в АХ В четвертом примере частное равно 0002Н, а остаток - 0000Н: MOV AX,WORDl ;Слово/слово CWD ;Делимое - в DX:AX IDIV WORD3 /Остаток - в регистре DX, частное - в АХ Только четвертый пример Дает одинаковые результаты вне зависимости от того, какая инструкция применяется. Практически, если делимое и делитель имеют одинаковые значения битов знака, DIV и IDIV дают одинаковый результат. Однако если знаковые биты делимого и делителя имеют разные значения, DIV генерирует положительное частное, a IDIV - отрицательное. В следующем примере демонстрируется деление счетверенного слова на двойное слово. Частное равно DC5CH (56412): DBLWD1 DBLWD2 DBLWD3 DD DD DD MOV MOV IDIV 0 225648 4 EDX,DBLWDl EAX,DBLWD2 DBLWD3 Счетверенное слово/двойное слово делимое - в EDX:ЕАХ остаток - в EDX, частное - в ЕАХ i Попробуйте понаблюдать за исполнением этих примеров в отладчике. Переполнения и прерывания Инструкции DIV и IDIV подразумевают, что частное значительно меньше делимого. Как следствие, при выполнении этих операций может легко возникнуть переполнение; когда оно возникает, вычисления прерываются, что приводит к непредсказуемым результатам. Например, деление на ноль всегда вызывает 316 Часть IV. Обработка данных
прерывание. Но деление на 1 генерирует частное, равное делимому, и также может вызвать прерывание. Существует общее правило: если делитель - байт, его значение должно быть больше, чем значение старшего байта (АН) делимого; если делитель - слово, его значение должно быть больше, чем старшего слова (DX) делимого; если делитель - двойное слово, его значение должно быть больше, чем старшего двойного слова (EDX) делимого. В следующих инструкциях в качестве делителя используется 1, хотя могли бы подойти и другие значения: Операция деления Делимое Делитель Частное Слова на байт 012АН 01Н (1)2АН Двойного слова на 0001402ВН 0001Н (D402BH слово Счетверенного слова на 0000000210542ЕВ4Н 00000001Н (2)10542ЕВ4Н двойное слово г- В каждом случае генерируемое частное превышает по размеру регистр, предназначенный для его хранения. Значит, перед операциями деления рекомендуется добавлять проверку исходных данных. В первом примере DIVRBYTE - 1-байтовый делитель, а делимое хранится в АХ: СМР АН,DIVRBYTE ;Сравнить АН с делителем (байтом) JNB L20 ;Пропустить, если не меньше DIV DIVRBYTE ;Делить слово на байт Во втором примере DIVRDWD - делитель в формате двойного слова, а делимое хранится в паре регистров EDX:EAX: CMP EDX,DIVRDWD ;Сравнить EDX с делителем (двойным словом) JNB L20 ;Пропустить, если не меньше DIV DIVRDWD ;Делить счетверенное слово на двойное слово Для инструкции IDIV логика должна учитывать, что делимое или делитель могут быть отрицательными. Поскольку абсолютное значение (модуль) делителя должен быть меньшим из двух, можно использовать инструкцию NEG для изменения знака делителя и восстановления знака частного после деления. Деление при помощи вычитания Если частное слишком велико, можно попытаться выполнить деление с помощью последовательных вычитаний делителя из делимого. Вычтите делимое из делителя, увеличьте значение частного на 1 и продолжайте так делать до тех Глава 12. Арифметика: обработка двоичных чисел 317
пор, пока делимое не станет меньше делителя. В следующем примере делимое помещено в АХ, делитель - в ВХ, а частное помещается в СХ: ;Очистка регистра частного L20: СМР АХ,ВХ ;Если делимое меньше делителя, ; завершить ;Вьгчесть делитель из делимого ;Увеличить частное на 1 /•Повторить L30: ... ;Частное - в СХ, остаток - в АХ В конце процедуры СХ содержит частное, АХ - остаток. Если делимое должно помещаться в паре DX:AX, то процедура имеет следующий вид: SUB СМР JB SUB INC JMP ex, ex АХ,ВХ L3 0 АХ,ВХ СХ L2 0 L20: L30: L40: SUB СМР JNE СМР JB SUB SBB INC JMP ex, ex DX,0 L3 0 АХ,ВХ L40 АХ,ВХ DX,0 СХ L20 Очистка регистра Если DX = О, пропустить Если делимое меньше делителя, выйти Вычесть делитель из делимого Вычесть флаг переноса Прибавить 1 к частному Повторить Частное - в СХ, остаток - в АХ Обратите внимание, что очень большое делимое и малый делитель могут заставить программу выполняться очень долго. Деление сдвигами Для деления на степени двойки (2, 4, 8 и так далее) можно использовать сдвиг вправо на требуемое количество битов. Эти операции могут выполняться быстрее, чем, собственно, деление. В следующих невзаимосвязанных примерах предполагается, что регистр CL содержит 2, а ВХ - делимое: Сдвиг вправо на 1 Сдвиг вправо на 2 Сдвиг вправо на 3 SHK ВХ,01 ;Деление на 2 SHR BX,CL ;Деление на 4 SHR ВХ,03' ;Деление на 8 Следующая программа может быть полезна для сдвигов вправо двойного слова в паре DX:AX. Хотя в ней задан сдвиг на 4 разряда, она может быть адаптирована к другим величинам: ;Сдвиг АХ вправо на 4 бита ;Сохранение DL в BL ;Сдвиг DX на 4 бита вправо ; Сдвиг BL на 4 бита, влево ;Вставка 4 битов из BL в АХ SHR MOV SHR SHL OR АХ, BL, DX, BL, АХ, 04 DL 04 04 BL 318 Часть IV. Обработка данных
Обращение знака числа Инструкция NEG (Negate - изменить знак) изменяет знак двоичного числа на противоположный. Фактически NEG инвертирует биты, как инструкция NOT, и добавляет 1 к результату для получения двоичного дополнения исходного числа. Формат этой инструкции таков: [метка: ] NEG регистр/память Вот несколько невзаимосвязанных примеров: NEG CL ; Байт NEG BX ; Слово NEG EDX ; Двойное слово NEG BINVAL ;Элемент данных в памяти Изменение знака двойного слова, хранящегося в паре регистров DX:AX, требует дополнительных операций. Инструкция NEG не может обработать связанные значения в DX: АХ одновременно, а использование NEG на DX и АХ по отдельности даст неправильный результат. Вместо этого необходимо использовать NOT для инвертирования битов, а затем использовать инструкции ADD и ADC для добавления 1 к результату: NOT NOT ADD ADC DX AX AX,1 DX,0 Инвертируем биты в DX и АХ Добавляем 1 к АХ Добавляем перенос к DX Остается одна маленькая проблема: арифметические операции над двоичными данными выполнять, как мы видели, несложно. Но вводимые/выводимые данные, как правило, представлены в ASCII-формате. Задача арифметической обработки ASCII-данных рассматривается в следующей главе. Математический сопроцессор В этом параграфе кратко рассматривается математический сопроцессор; подробно рассматривать в этой книге мы его не будем. Математический сопроцессор имеет собственный набор инструкций и схемы для обработки чисел в формате с плавающей запятой, для выполнения, например, таких операций, как возведение в степень, вычисление логарифмсгЕГи тригонометрических функций. Восемь 80-разрядных регистров для чисел с плавающей запятой могут хранить числа вплоть до 10 в 400-й степени и обрабатывать их значительно быстрее, чем обычный процессор. Сопроцессор содержит восемь 80-разрядных регистров, обозначающихся Rl—R8, имеющих следующую внутреннюю структуру: Глава 12. Арифметика: обработка двоичных чисел 319
Знак 79 показатель степени 78 64 мантисса 63 0 С каждым регистром связан двухбитовый тег, индицирующий его состояние: 00 - содержит корректное число 10 - содержит некорректное число 01 - содержит нулевое значение 11 - пуст Сопроцессор различает семь типов данных: 1. Целочисленное слово: 16 бит Знак 15 Число 14-0 2. Короткое целое: 32 бита Знак 31 Число 30-0 3. Длинное целое: 64 бита Знак 63 Число 62-0 4. Короткое действительное: 32 бита данных в формате с плавающей запятой Знак 31 Экспонента 30-23 мантисса 22-0 5. Длинное действительное: 64 бита данных в формате с плавающей запятой Знак 63 Экспонента 62-52 мантисса 51-0 6. Временное действительное: 80 битов данных в формате с плавающей запятой Знак 79 Экспонента 78-64 мантисса 63-0 7. Упакованное десятичное: 18 значащих десятичных разрядов Знак 79 Нули 78-72 мантисса 71-0 Типы 1, 2 и 3 - это обычные двоичные числа (отрицательные числа представляются в виде двоичных дополнений). Типы 4, 5 и 6 представляют числа с 320 Часть IV. Обработка данных
плавающей запятой. Тип 7 содержит 18 4-разрядных двоичных чисел, представляющих десятичные цифры. Числа в любом из этих форматов можно записывать в память из регистров сопроцессора и наоборот. Однако при вычислениях сопроцессор приводит данные всех типов к типу временных действительных (6). Данные сохраняются в памяти с переставленными в обратном порядке байтами. Процессор запрашивает определенную операцию и передает сопроцессору числовые данные, сопроцессор выполняет операцию и возвращает процессору результат. Ключевые моменты • Диапазон допустимых значений байтового элемента данных в знаковом формате - от -128 до +127, элемента в формате слова - от -32768 до +32767, двойного слова - от -2147483648 до +2147483647. • Для проведения арифметических действий над величинами большой разрядности обрабатывайте их по частям, используя следующие операции: ADD и ADC - для сложения, SUB и SBB - для вычитания, и CLC - для установки в 0 флага переноса CF. • Для умножения беззнаковых чисел используйте инструкцию MUL, знаковых - IMUL. • Если операнд инструкций MUL и IMUL определен как байт, множимое располагается в регистре AL; если как слово, то в АХ; если как двойное слово, то в ЕАХ. • Инструкции сдвига влево (SHL и SAL) можно использовать для умножения, соответственно, беззнаковых и знаковых чисел на степени двойки. • Для деления беззнаковых чисел используйте инструкцию DIV, знаковых - IDIV. • Если при делении делитель определен как байт, делимое располагается в регистре АХ; если как слово, то делимое - в паре DX:AX; если как двойное слово - в EDX:EAX. • Делитель должен быть больше, чем содержимое АН, если делитель - байт, чем содержимое DX, если делитель - слово, и чем EDX, если делитель - двойное слово. • Инструкции сдвига вправо (SHR и SAR) можно использовать для деления, соответственно, беззнаковых и знаковых чисел на степени двойки. Глава 12. Арифметика: обработка двоичных чисел 321 21—5-74I
Вопросы и упражнения 1. Для знаковых и для беззнаковых чисел укажите диапазоны допустимых значений в форматах (а) байта, (б) слова, (в) двойного слова. 2. Объясните разницу между переносом и переполнением при выполнении арифметических операций. 3. Выполните сложение двоичных чисел, покажите в двоичном виде суммы, представьте их как знаковые и беззнаковые числа, покажите состояния флагов переноса (CF) и переполнения (OF): (а) оооюои (б) 010101Ю (в) noioioi (г) попои +00111000 +00111001 +01011010 +11010110 4. В параграфе "Арифметические действия с двойными словами" приведены два фрагмента программ, прибавляющие значения пар слов (первое слово - WORD1A). Модифицируйте оба фрагмента так, чтобы складывались три пары слов вместо двух. Определите дополнительные слова как WORD3А и WORD3B и замените старые WORD3A и WORD3B на WORD4A и WORD4B. 5. В вопросах 5-8 используйте следующие элементы данных со словами, правильно определенными в обратном порядке: BIN_AMT1 DW 0147H DW 139AH BIN_AMT2 DW 02B3H DW 2D41H CAL_AMT DW 0 DW 0 DW 0 6. Укажите инструкции, прибавляющие (а) слово BIN_AMT1 к BIN_AMT2, (б) двойное слово, начинающееся с ВГМ_АМТ1, к двойному слову, начинающемуся с BIN_AMT2. 7. Объясните результат выполнения следующей последовательности инструкций: STC MOV BX,BIN_AMTl ADC BX,BIN_AMT2 8. Укажите инструкции, необходимые для (а) умножения слова BIN_AMT1 на слово BIN_AMT2, (б) умножения двойного слова, начинающегося с BIN_AMT1, на двойное слово, начинающееся с BIN_AMT2. Сохраните произведение в CAL_AMT. 322 ; Часть IV. Обработка данных
9. Укажите инструкции, необходимые для (а) деления слова BIN_AMT1 на 24Н, (б) деления двойного слова, начинающегося с BIN_AMT1, на слово, начинающееся с BIN_AMT2. Сохраните частное в CAL_AMT. 10. Какие делители, кроме нуля, вызывают ошибки переполнения? 11. Обратившись к параграфу "Умножение сдвигами", найдите программу, выполняющую сдвиг двойного слова на 4 бита влево. Модифицируйте ее для выполнения сдвига на 2 бита. 12. Обратившись к параграфу "Деление сдвигами", найдите программу, выполняющую сдвиг двойного слова на 4 бита вправо. Модифицируйте ее для выполнения сдвига на 2 бита. Глава 12. Арифметика: обработка двоичных чисел 323 21*
Глава 13 Арифметика: работа сданными форматов ASCII и BCD Цель: рассмотреть форматы данных ASCII и BCD, выполнение арифметических операций над данными в этих форматах и преобразования данных между этими форматами и двоичными числами. Введение Все операции в компьютере проводятся над данными, представленными в двоичном формате. Как мы видели в главе 12, двоичный формат не создает сложностей до тех пор, пока данные определяются непосредственно в теле программы или в файлах данных в том же формате. Однако значительное количество данных, вводимых в компьютер, представляется в других форматах. Например, числовые величины, вводимые с клавиатуры, представляются в ASCII-формате в десятичной системе счисления. В том же формате обычно должен производиться и вывод результатов на экран. С этим форматом связан формат двоично-десятичных чисел (binary-coded decimal, BCD). Он имеет несколько областей применения и представлен двумя подвидами - упакованным и неупакованным. Процессоры поддерживают несколько инструкций, производящих арифметические операции над данными в этих форматах и преобразования форматов. В этой главе также рассмотрены методы преобразования данных из формата ASCII в двоичный формат для выполнения арифметических операций и преобразование двоичных чисел в формат ASCII для вывода результатов. Программа в конце главы объединяет в себе большую часть приемов и возможностей, рассмотренных в главах с первой по двенадцатую. В языках высокого уровня, например в С, компилятор учитывает основание системы счисления и формат числа (целое или дробное). Однако ни компьютер сам по себе, ни транслятор с языка ассемблера не воспринимают дробных чисел, поэтому эта задача ложится на программиста. В этой главе рассмотрены следующие инструкции: AAA ASCII-коррекция после AAD ASCII-коррекция перед сложения деления AAS ASCII-коррекция после DAA Десятичная коррекция после вычитания сложения ААМ ASCII-коррекция после DAS Десятичная коррекция после умножения вычитания 324 Часть IV. Обработка данных
Данные в десятичном формате До этой главы все данные в программах были представлены в двоичном или ASCII-форматах. Процессор также может обрабатывать данные в двоично- десятичном формате, выполняя над ними некоторые арифметические операции. Двоично-десятичный формат используется в двух случаях. 1. Если нужно округлять числа без потери точности, например, при финансовых расчетах (округление данных, содержащих, например, суммы в долларах и центах, может вызвать потерю точности). 2. Часто этот формат эффективен при обработке небольших чисел, вводимых с клавиатуры и выводимых на экран или принтер. Разряд числа в формате BCD состоит из 4-х битов, представляющих десятичные числа от 0 до 9: Двоичное.число 0000 0001 0010 ООН 0100 Цифра 0 1- 2 3 4 Двоичное число 0101 ОНО 0111 1000 1001 Цифра 5 6 7 8 9 Числа формата BCD могут храниться в упакованном и неупакованном видах. 1. Неупакованные BCD-числа содержат один десятичный разряд в младших четырех битах байта, а старшие четыре бита содержат нули. Заметьте, что хотя числа в формате ASCII тоже в определенном смысле "неупакованные", их так не называют. 2. Упакованные BCD-числа содержат две цифры в одном байте - одну в старших четырех битах, вторую - в младших. Этот формат часто используется при выполнении арифметических операций с использованием ариф- . метического сопроцессора, а элементы данных этого формата определяются директивой DT в виде полей размером 10 байтов. Вот представление десятичного числа 1527 в трех форматах: Формат ASCII Неупакованный BCD Упакованный BCD Длина Четыре байта Четыре байта Два байта Содержимое 31 35 32 37 01 05 02 07 15 27 Глава 13. Арифметика: работа с данными форматов ASCII и BCD 325
Процессор выполняет операции над данными в форматах ASCII и BCD по одному байту за раз. Для преобразования между форматами необходимо использовать специальные инструкции. Обработка ASCII-данных Поскольку данные вводятся с клавиатуры в ASCII-формате, число, например, 1234 будет представлено в памяти как 31323334Н. При выполнении арифметических операций над данными в ASCII-формате применяются инструкции AAA и AAS, имеющие следующих формат: [метка:] [метка:] AAA AAS ASCII-коррекция после сложения ASCII-коррекция после вычитания Эти инструкции не используют операндов и автоматически обрабатывают значение в регистре АХ. Преобразование необходимо, поскольку ASCII-значение представляет собой неупакованное десятичное число, а процессор работает в двоичной системе счисления. Сложение ASCII-чисел Рассмотрим три примера сложения ASCII-чисел: Пример 1: Пример 2 : Пример 3 : 35Н 38Н 39Н +32Н +34Н +39Н 67Н 6СН 72Н В примере 1 младший разряд результата, 7, содержит правильную величину, хотя старший разряд требует исправления. В примере 2 результат некорректен из-за переноса в шестнадцйтеричной форме. В примере 3 результат некорректен из-за переноса из единиц в десятки. Для получения корректного результата инструкция AAA проверяет младший шестнадцатеричный разряд регистра AL. Если он содержит значение от А до F или флаг AF (вспомогательный перенос) установлен в 1, операция прибавляет 6 к содержимому AL (это эквивалент отнимания 9, если не учитывать переносов, так как 6 (двоичное 0110) - дополнение к 9 (двоичному 1001)), добавляет 1 к содержимому АН и устанавливает в 1 флаги CF (перенос) и AF (вспомогательный перенос). В любом случае AAA сбрасывает старший шестнадцатеричный разряд AL в 0. 326 Часть IV. Обработка данных
Посмотрим, как команда AAA будет действовать в трех вышеприведенных примерах. Предполагая, что ASCII-числа хранятся в регистрах AL и BL, инструкции имеют следующий вид: ADD AL,BL ;Сложить ASCII-числа AAA /Скорректировать ASCII-сумму Пример 1 Сумма 35Н и 32Н равна 67Н. AAA проверяет младший разряд (7) и, поскольку его значение не лежит в переделах от А до F, и флаг AF не установлен в 1, AAA просто очищает старший разряд, содержащий 6, устанавливая его в 0. Пример 2 Сумма 38Н и 34Н равна 6СН. AAA проверяет младший разряд (С) и выполняет следующие действия: АН AL 00 6С Прибавляется б к AL 00 72 Прибавляется 1 к АН 01 72 Устанавливаются в 0 старшие 4 бита AL 01 02 (сумма = 12) Пример 3 Сумма 39Н и 39Н равна 72Н. Хотя младший десятичный разряд (2) не принадлежит к диапазону A-F, но установлен флаг AF, и инструкция AAA выполняет следующие действия: АН AL 00 72 Прибавляется б к AL 00 7 8 Прибавляется 1 к АН 01 78 Устанавливаются в 0 старшие 4 бита AL 01 08 (сумма = 18) Суммы 0006, 0012 и 0018 представляют собой двоично-десятичные (BCD) числа. Для восстановления их в формат ASCII нужно установить старшие 4 бита каждого их байта равными 3: OR AX,3030H преобразование неупакованных BCD-чисел в ASCII-формат Все выглядит очень просто, пока нам нужно суммировать только однобайтовые ASCII-числа. Однако суммирование многобайтовых чисел формата ASCII требует применения цикла, обрабатывающего данные справа налево (от младших разрядов к старшим) и учитывающего переносы. На фрагменте программы, приведенном на рис. 13.1, показано сложение двух трехбайтовых ASCII-чисел Глава 13. Арифметика: работа с данными форматов ASCII и BCD 327
ASCVALUE1 и ASCVALUE2. Сумма сохраняется в четырехбайтовом поле ASCTOTAL. ASCVAIAJE1 ASCVALUE2 ASCTOTAb .386 ; А20: АЗО: DB DB DB . . . CLC LEA LEA LEA MOV MOV ADC AAA MOV , DEC DEC DEC LOOP MOV LEA MOV OR DEC LOOP MOV MOV LEA MOV MOV INT '548' •724' •0000' SI,ASCVALUE1+ DI,ASCVALUE2+ BX,ASCTOTAL+3 CX, 03 ZX AX,[SI] AL, [DI] [BX],AL SI DI BX A20 [BX],AH BX,ASCTOTAL+3 CX, 04 BYTE PTR[BX], BX A3 0 AX,1300H BX,0031H ВР,ASCTOTAL CX, 04 DX,0824H 10H ASCII элементы | 2 ; 2 ; / / ЗОН Сложение ASCII-величин: Инициализация адреса ASCII-чисел Установка числа циклов = 3 Загрузка ASCII-байта в АХ Сложение (с переносом) ASCII-коррекция Сохранение суммы •Повторение цикла 3 раза • В конце прибавляется ; перенос Преобразование ASCTOTAL в ASCII-формат ;Повторение цикла 4 раза ;Запрос вывода ;Страница и атрибуты ; ASCII-строка ;Длина строки /Строка и столбец Рис.13.1. Сложение ASCII-чисел Отметим следующие моменты. • Инструкция CLC в начале программы устанавливает в 0 флаг переноса CF. • После метки А20 инструкция MOVZX последовательно загружает символы ASCII в регистр AL. Эта инструкция также очищает АН, поскольку последующая инструкция AAA может прибавлять к АН 1, и эта единица может остаться в АН в следующем цикле. Заметьте, что использование инструкций XOR или SUB для очистки АН могло бы изменить состояние флагов. 328 Часть IV. Обработка данных
• Инструкция ADC используется для сложения потому, что автоматически прибавляет к сумме перенос из AL в АН. • Инструкция MOV сохраняет очередной байт суммы ASCII в очередном байте ASCTOTAL. При завершении цикла программа перемещает значение из АН (00 или 01) в старший (первый слева) байт ASCTOTAL. В конце выполнения ASCTOTAL будет содержать 01020702Н. Для преобразования этого числа в формат ASCII программа последовательно записывает значение 3 в старшие 4 бита каждого байта инструкцией OR. Результат равен 31323732Н, и программа выводит его на экран перед тем, как завершить работу. Программа не использует инструкцию OR после AAA, чтобы немедленно сформировать ASCII-цифру. Это могло бы привести к изменению состояния флага CF и некорректному результату исполнения инструкции ADC. Можно было бы поместить флаги в стек (инструкцией PUSHF), выполнить операцию OR и извлечь флаги из стека инструкцией POPF: ;Сложение с переносом ;ASCII-коррекция ;Сохранить флаги ;Преобразовать в ASCII-цифру /Восстановить флаги ;Сохранить сумму Вычитание ASCII-чисел Инструкция AAS работает почти так же, как и AAA. Она проверяет младшие 4 бита регистра AL. Если в них содержится число от А до F или флаг AF установлен в 1, операция вычитает 6 из значения в AL, вычитает 1 из содержимого АН и устанавливает в 1 флаги AF и CF. В любом случае* AAS очищает старший шестнадцатеричный разряд AL. Следующие два примера подразумевают, что ASCVALUE1 содержит 39Н, a ASCVALUE2 - 35Н. Первый пример показывает вычитание ASCVALUE2 (35Н) из ASCVALUE1 (39Н). Команда AAS не вносит поправки, поскольку младший шестнадцатеричный разряд содержит значение (меньшее, чем ОАН), не требующее коррекции: ADC AAA PUSHF OR POPF MOV AL,[DI] AL,30H [BX] ,AL Инструкция MOV SUB AAS OR Операнды AL,ASCVALUE1 AL/ASCVALUE2 al', зон Регистр 0039 •0004 0004 0034 AX Флаг AF 0 0 Флаг CF 0 0 Глава 13. Арифметика: работа сданными форматов ASCII и BCD 329
Во втором примере ASCVALUE1 (39Н) вычитается из ASCVALUE2 (35Н). Поскольку младший шестнадцатеричный разряд содержит ОСН, AAS вычитает 6 из AL, вычитает 1 из АН и устанавливает флаги AF и CF в 1: Инструкция MOV SUB AAS Операнды AL,ASCVALUE2 AL,ASCVALUE1 Регистр АХ 0035 00FC FF06 Флаг AF 1 1 Флаг CF 1 1 Результат, который должен быть равен -4, равен FF06H - десятичному дополнению. То есть, -10+6=-4. Умножение ASCII-чисел Умножение и деление чисел в формате ASCII требует их предварительного преобразования в формат BCD. После этого можно применять инструкции ААМ и AAD для выполнения арифметических операций непосредственно над неупакованными двоично-десятичными числами: [метка:] [метка:] ААМ AAD ;ASCII-коррекция после умножения ;ASCII-коррекция перед деления Инструкция ААМ исправляет результат умножения ASCII-данных в регистре АХ. Однако предварительно нужно очистить старшие 4 бита каждого байта (установить их в 0), преобразуя число из ASCII- в BCD-формат. Т.е. физически необходимо перемножать числа в неупакованном BCD-формате, а инструкция ААМ корректирует результат этого перемножения в тот же BCD-формат, а не ASCII. Например, число ASCII 31323334H преобразуется в неупакованное BCD-число 01020304Н. Кроме того, поскольку операция может проводиться только с однобайтовыми числами, для обработки многобайтовых величин необходимо использовать цикл. Используйте только инструкцию MUL, выполняющую умножение без учета знака. Инструкция ААМ делит содержимое AL на 10 (ОАН) и сохраняет частное в АН, а остаток - в AL. Например, предположим, что AL содержит ASCII-число 35Н, a CL - 39Н. В следующем фрагменте программы значение в AL умножается на значение из CL, а результат преобразуется в ASCII-формат. Инструкция Комментарий Регистр АХ Регистр CL Начальные значения 0035 3 9 AND CL,0FH Преобразуем значение в CL в 09 0035 09 AND AL,0FH Преобразуем значение в AL в 05 0005 09 MUL CL Умножаем AL на CL 002D 09 330 Часть IV. Обработка данных
ААМ Преобразуем значение в АХ в 0405 неупакованный BCD-формат OR АХ,3030Н Преобразуем BCD-число в ASCII-число 3435 Инструкция MUL помещает произведение (002DH) в регистр АХ. Инструкция ААМ делит это значение на ОАН, помещая частное 04Н в АН и остаток 05 в AL. После этого инструкция OR преобразует результат в формат ASCII. Во фрагменте программы, приведенном на рис. 13.2, продемонстрировано умножение 4-байтового ASCII-числа на 1-байтовое. MULTCAND MULTPLER ASCPROD А20: АЗО: DB MOV LEA LEA AND MOV AND MUL AAM ADD AAA MOV DEC MOV DEC LOOP LEA MOV OR DEC LOOP MOV MOV LEA MOV MOV INT '3783' DB ' 5 ' DB 5 DUP(O) CX, 04 SI,MULTCAND+3 DI/ASCPROD+4 MULTPLER,OFH AL,[SI] AL,OFH MULTPLER « AL, [DI] [DI],Ab DI [DI],AH SI A2 0 BX,ASCPROD+4 CX, 05 BYTE PTR[BX],3 0H BX A3 0 AX,1300H BX,0031H BP,ASCPROD CX,05 DX,0824H 10H ASCII-элементы Число циклов = 4 •Очистка старшего полубайта •Загрузка ASCII-символа •Очистка старшего полубайта • Умножение •ASCII-коррекция •Добавление к сохраненному произведению ч ;Сохранение переноса произведения ;Цикл (4 раза) ;Преобразование ; произведения в ASCII: •Справа налево, 5 байт ;Цикл (5 раз) ;Запрос вывода ;Страница и атрибуты ;ASCII-строка ;Длина строки ;Строка Рис. 13.2. Умножение ASCII-чисел Глава 13. Арифметика: работа с данными форматов ASCII и BCD 331
Поскольку ААМ проводит операцию только над одним байтом за раз, программа с помощью цикла обрабатывает по одному байту за раз, справа налево. В конце цикла неупакованное BCD-число, содержащее произведение, равно 0108090105, и цикл, начинающийся с метки А30, преобразует его в формат ASCII. В формате ASCII произведение равно 3138393135, что соответствует числу 18915. Программа выводит результат на экран перед завершением работы. Если длина множителя больше одного байта, придется использовать дополнительный цикл для последовательной обработки байтов множителя, но в таком случае проще будет преобразовать множимое и множитель в двоичный формат, выполнить умножение, после чего преобразовать полученное произведение обратно в ASCII-формат. Такие преобразования рассмотрены в следующих разделах. Деление ASCII-чисел Инструкция AAD выполняет корректировку неупакованного BCD-делимого перед делением. Так же как и при использовании инструкции ААМ, предварительно необходимо удалить цифру 3 из 4-х старших битов каждого байта для преобразования числа в BCD-формат. AAD позволяет обрабатывать двухбайтовое делимое в регистре АХ. Делитель может быть только однобайтовым и содержать число от 1 до 9. Предположим, что АХ содержит ASCII-число 28 (3238Н), a CL содержит делитель, ASCII-число 7 (37Н). Для выполнения коррекции и деления используется следующая последовательность инструкций: Инструкция Комментарий Содержимое АХ Содержимое CL Начальные значения 3238 37 AND CL,0FH Преобразуем значение CL 3238 07 в 07 AND AX,0F0FH Преобразуем значение АХ 0208 в 28 AAD Коррекция делимого 001С (преобразование в двоичную форму) DIV CL Деление на 7 0004 Инструкция AAD умножает значение в АН на 10 (0AH), прибавляет произведение (20, или 14Н) к AL, и очищает АН. Результат, 001СН, есть шестнадца- теричная форма числа 28. В фрагменте Ьрограммы, приведенном на рис. 13.3, выполняется деление четырехбайтового делимого на однобайтовый делитель. Программа побайтно обрабатывает делимое слева направо. Инструкция LODSB помещает байт из 332 Часть IV. Обработка данных
DIVIDEND в регистр AL (с помощью SI), a STOSB сохраняет байты из AL в ASCQUOT (с помощью DI). Остаток сохраняется в АН, чтобы команда AAD смогла учесть его в AL. В конце цикла ASCQUOT содержит неупакованное двоично-десятичное число 00090204, а остаток В АН равен 02. Программа преобразует частное в ASCII-число 30393234 (слева направо) и выводит его на экран в виде 0924. DIVIDEND DIVISOR ASCQUOT А20: АЗО: DB DB DB MOV SUB AND LEA LEA LODSB AND AAD DIV STOSB LOOP LEA MQV OR INC LOOP MOV LEA INT '3698' ;ASCII- .4. 4 DUP(O), ' $' CX,04 AH,AH DIVISOR,OFH SI,DIVIDEND DI,ASCQUOT AL,0FH DIVISOR A20 BX,ASCQUOT CX, 04 BYTE PTR[BX],30H BX A30 AH,09H DX,ASCQUOT 21H элементы ASCII-деление Число циклов = 4 Очистка левого байта делимого Очистка старшего полубайта делителя Загрузка ASCII-байта Очистка старшего полубайта Коррекция для деления •Деление Сохранение частного Цикл 4 раза •Преобразование результата в ASCII: •Слева направо, 4 байта •Установка старшего полубайта • Следующий байт • Цикл 4 раза • Отображение ; частного Рис. 13.3. Деление ASCII-чисел Если размер делителя больше одного байта, необходимо будет организовывать в программе еще один цикл для последовательной обработки делителя. Однако лучше преобразовать числа в двоичную форму, как показано в параграфе "Преобразование ASCII-чисел в двоичную форму". Глава 13. Арифметика: работа с данными форматов ASCII и BCD 333
Обработка упакованных двоично-десятичных чисел В предыдущем примере деления ASCII-числа частное было равно 00090204. Если сжать это число, отбросив нули в старших четырех битах каждого байта, получим число 0924, представленное в упакованном двоично-десятичном формате. Числа в этом формате можно складывать и вычитать, используя инструкции DAA (Decimal adjust after addition - десятичная коррекция после сложения) и DAS (Decimal adjust after subtraction - десятичная коррекция после вычитания). Формат этих инструкций таков: [метка:] [метка:] DAA DAS ;Десятичная коррекция после сложения /Десятичная коррекция после вычитания Инструкция DAA корректирует результат суммирования двух упакованных двоично-десятичных чисел в AL, a DAS - результат вычитания.. У пакованные BCD-числа, как и ASCII-числа, обрабатываются по одному байту за шаг. Двоично-десятичная сумма в AL состоит из двух четырехбитовых цифр. Если значение младшего разряда превышает 9 или установлен в 1 флаг AF, DAA прибавляет 6 к содержимому AL и устанавливает в 1 флаг AF. Если после этого AL содержит число более 99Н или флаг CF установлен в 1, DAA прибавляет к значению в AL 60H и устанавливает флаг CF в 1. В противном случае флаги AF и CF убтанавливаются в 0. Эта процедура демонстрируется в следующем примере. Рассмотрим сложение двоично-десятичных чисел 057836 и 069427. Очистив флаг CF в 0, начнем сложение с младших пар цифр, 36 и 27, в регистре AL: Объяснения Первая инструкция ADC; CF устанавливается в 0 Инструкция DAA прибавляет 06Н, устанавливает AF в 1 Вторая инструкция ADC; CF устанавливается в 1 Инструкция DAA прибавляет 06Н, устанавливает AF в 1 BCD- число Шестнадца- теричное число Двоичное число Сохраненное BCD- число 36 27 36 27 ООН ОНО 0010 0111 63 78 94 (1)72 5D 78 94 (D0C 0101 1101 0000 ОНО ОНО ООН 0111 1000 1001 0100 (1)0000 1100 oopo__Qiio 63 0001 0010 334 Часть IV. Обработка данных
Инструкция DAA прибавляет 60Н (поскольку в CF 1) и устанавливает CF в 1 Третья инструкция ADC; прибавляет также 1 (поскольку в CF 1); CF устанавливается в О Инструкция DAA прибавляет 06Н, устанавливает AF в 1 05 06 1 12 05 Об 1 ОС ОНО 0000 0111 0010 72 0000 0101 0000 ОНО 0000 0001 12 0000 0000 0001 1100 оно 0010 Результат в формате BCD равен 127263. Во фрагменте программы, приведенном на рис. 13.4, показана реализация этого примера на ассемблере. ASCVALUE1 ASCVALUE2 BCDVAbUEl BCDVALUE2 BCDSUM .386 ; А20: DB DB DB DB DB . . . LEA LEA CALL LEA LEA CALL XOR LEA LEA LEA MOV CLC MOV ADC DAA MOV DEC DEC DEC LOOP •057836' •069427' '000' •000' 4 DUP(0) SI,ASCVALUE1 DI,BCDVALUE1 B10CONVRT SI,ASCVALUE2 ;ASCII- данные ;BCD-данные | +4 +2 +4 DI,BCDVALUE2 +2 B10CONVRT AH,AH SI,BCDVALUE1 DI,BCDVALUE2 BX,BCDSUM+3 CX,03 AL, [SI] AL, [DI] [BX] ,AL SI DI BX A20 +2 +2 Инициализация ASCII- и BCD-данных Вызов программы преобразования Инициализация ASCII- и BCD-данных Вызов программы преобразования Сложение BCD-чисел: •Очистка АН ■Инициализация BCD- адресов •3-байтное поле ■Взять BCDVALUE1 ;Добавить BCDVALUE2 ;Десятичная коррекция •Сохранить в BCDSUM ?Цикл 3 раза Рис. 13.4. Конвертирование и сложение BCD-чисел Глава 13. Арифметика: работа с данными форматов ASCII и BCD 335
/ BiOCONVRT B20: BIOCONVRT Преобразование ASCII в BCD: PROC MOV MOV XCHG SHL SHL MOV DEC DEC DEC LOOP RET ENDP CX, 03 AX,[SI] AH,AL AL,04 AX, 04 [DI],AH SI SI DI B20 Слова для конвертирования Взять ASCII-пару Сдвинуть для очистки старшего полубайта •Сохранить BCD-цифры ;Цикл 3 раза Рис. 13.4 (Окончание) Процедура BIOCONVRT преобразует ASCII-числа ASCVALUE1 и ASCVALUE2 в упакованные BCD-числа BCDVALUE1 и BCDVALUE2, соответственно. Обработка ведется справа налево, хотя можно было бы проводить ее и слева направо. Кроме того, отметим, что обрабатывать слова легче, чем байты, поскольку для получения однобайтового упакованного BCD-числа необходимо обработать ASCII-число двухбайтового размера. Однако использование слов требует четного размера ASCII-числа. Программа суммирует числа, трижды циклически выполняя одни и те же операции. Сумма получается в элементе BCDSUM. Результат равен 00127263Н, в чем можно убедиться, использовав DEBUG. Можно попытаться добавить в программу фрагмент, преобразующий BCD-число в ASCII-формат и выводящий его на экран. Преобразование ASCII-чисел в двоичную форму Выполнение арифметических операций над числами в ASCII- и BCD- форматах удобно только для элементов данных небольшого размера. Для многоразрядных чисел удобнее предварительно выполнить преобразования чисел в двоичный формат. Практически удобнее выполнять преобразования ASCII-чисел в двоичный формат напрямую, чем через BCD-формат. Преобразование ASCII-числа в двоичный формат основывается на том факте, что ASCII-числа - десятичные, а компьютер обрабатывает двоичные числа. Вот процедура преобразования: 336 Часть IV. Обработка данных
Множители 9x1 6x10 5x100 3x1000 Сумма Десятичное Произведение 9 60 500 3000 3569 1. Начав с младшего байта ASCII-числа, продвигаемся справа налево. 2. Удаляем тройки из четырех старших битов каждого ASCII-байта и преобразовываем числа в BCD-формат. 3. Умножаем значение младшего (крайнего справа) BCD-разряда на 1, второго справа - на 10, третьего - на 100, и т.д., а затем суммируем произведения. В следующем примере десятичное число 3569 преобразуется в двоичный формат: Шестнадцатеричное Множители Произведение 9х01Н 9Н бхОАН ЗСН 5х64Н 1F4H ЗхЗЕ8Н 668Н 0DF1H Убедитесь, что 0DF1H действительно равно 3569. Во фрагменте программы, приведенном ниже, выполняется преобразование ASCII-числа 3569 в его двоичный эквивалент: Длина ASCII-числа ASCII-число Двоичный результат 1, 10, 100, 1000, ... ;Число повторений цикла ;Адрес ASCVALUE ; Получить очередной ASCII-символ ;Удалить тройку из старших ; . 4-х битов Умножить на степень 10 Прибавить произведение к результату Вычислить следующую степень 10 Перейти к следующему ASCII-символу Вернуться к началу цикла ASCLENTH ASCVALUE BINVALUE MULTFACT L10: EQU DB DW DW MOV LEA MOV AND MUL ADD MOV IMUL MOV DEC 4 '3569' 0 1 CX,ASCLENTH SI,ASCVALUE+3 AL,[SI] AL,0FH MULTFACT BINVALUE,AX AX,MULTFACT AX,10 MULTFACT,AX SI LOOP L10 Инструкция LEA инициализирует регистр SI адресом младшего байта ASCII- числа, ASCVALUE+3. Инструкция MOV AL,[SI] помещает байт в AL. Каждый проход по циклу уменьшает значение в SI на 1 и копирует очередной байт ASCVALUE в AL. Цикл повторяется 4 раза, и значение каждого байта умножается на соответствующую ему степень числа 10 (1, 10, 100, 1000). В конце Глава 13. Арифметика: работа с данными форматов ASCII и BCD 22 —5-741 337
цикла BINVALUE содержит корректный результат, F10DH, в форме с переставленными байтами. Программа, приведенная в следующем параграфе, преобразует двоичное значение обратно в ASCII-формат через десятичный. Преобразование двоичных данных в ASCII-формат Для вывода на экран или принтер результата арифметических операций над двоичными числами необходимо преобразовать его в ASCII-формат. Эта операция в обратном порядке повторяет шаги, описанные выше: вместо умножения последовательно делит разряды двоичного числа на 10 (ОАН) до тех пор, пока частное не будет меньше 10. Каждый остаток, лежащий в диапазоне от 0 до 9, поочередно преобразуется в цифру в ASCII-формате. Например, попробуем преобразовать число 0DF1H обратно в десятичный формат: Остаток 9 6 5 Последнее частное (т.е. 3) - старший разряд результата. Это частное вместе с остатками (снизу вверх по таблице) формируют результирующее BCD-число: 3569. Для получения ASCII-формата осталось вставить между разрядами цифру 3. Результат равен 33353639Н. В следующем примере число 0DF1H преобразуется в ASCII-формат: Делим на DF1 / А 164 / А 23 / А ОАН Частное 164 23 3 ASCVALUE BINVALUE L20: L30: DB DW MOV LEA MOV CMP JB XOR DIV OR MOV DEC JMP OR MOV 4 DUP(■ ') 0DF1H CX,0010 SI,ASCVALUE+3 AX,BINVALUE AX,CX L30 DX,DX CX DL,30H [SI],DL SI L2 0 AL,3 0H [SI],AL ;Элементы данных ;Делитель ;Адрес ASCVALUE ;Поместить двоичное число в АХ ;Меньше ли частное десяти? ;Если да, выходим ;Очистить частное ;Делим на 10 ;Вставляем 3 в старшие 4 бита ;Сохраняем ASCII-символ ;Перейти к следующему разряду ;Вернуться к началу цикла ;Вставляем 3 в старшие 4 бита ;Сохраняем частное как ; ASCII-символ 338 Часть IV. Обработка данных
Программа делит двоичное число на 10 (ОАН) до тех пор, пока частное не будет меньше 10, и сохраняет полученные разряды в виде ASCII-байтов. Вы можете попробовать понаблюдать за исполнением этой программы в отладчике. Сдвиги и округление результатов Предположим, что результат неких операций содержит три разряда после запятой. Вам нужно округлить его до двух десятичных разрядов. Например, пусть результат равен 17,385. Для округления к младшему разряду (тысячным) необходимо прибавить 5 и отбросить последний разряд результата: Число 17,3 85 Добавляем 5 + 0, QQ5 -Округленный результат 17,390 = 17,39 Если число равно 17,3855, прибавьте 0,0050 и отбросьте два разряда, а если число равно 17,38555, прибавьте 0,00500 и отбросьте три разряда. Поскольку компьютер обычно обрабатывает двоичные данные, он представит число 17,385 как 43Е9Н. Прибавив 5 к 43Е9Н, мы получим 43ЕЕН (или 17390 в десятичном формате). Это хорошо. Но попытка сдвига на один разряд вправо даст результат 21F7H (или 8695) - совсем не то, что нужно, а просто 17390, деленное пополам. Значит, нам необходима некая операция, эквивалентная сдвигу десятичного числа на один разряд вправо. Этого результата можно достичь, разделив двоичное число на 10 (или ОАН), т.е. 43Е9Н / ОАН = 6СВН. Преобразовав 6СВН в десятичный формат, мы получим 1739, что и требовалось получить. Теперь, просто вставив в нужную позицию десятичную запятую (в ассемблере, как и в английском языке, - это точка), получим округленное значение 17,39. Пользуясь этим методом, можно округлять любые двоичные числа. Для трех цифр после запятой - прибавьте 5 и разделите на 10, для четырех - прибавьте 50 и разделите на 100. Вообще, число, которое нужно прибавить к округляемому, равно половине числа, на которое мы делим для отбрасывания ненужных разрядов. Разумеется, десятичная запятая (точка) в примерах подразумевалась, но отсутствовала в памяти компьютера. Программа: преобразования между двоичными и ASCII-числами Программа, приведенная на рис. 13.5, позволяет пользователю вводить числа, перемножает их и выводит результаты на экран. С целью упрощения, в программе > ■ Глава 13. Арифметика: работа с данными форматов ASCII и BCD 339 22*
отсутствуют средства контроля правильности врода и проверки результатов. Кроме того, каждая процедура простр содержит перечисление используемых ею регистров, но не сохраняет их в стеке. TITLE ; ; LEFTCOL RIGHTCOL TOPROW BOTROW QTYPARAM MAXQLEN ACTQLEN QTYFLD RATEPAR MAXRLEN ACTRLEN RATEFLD PROMPT1 PROMPT2 PROMPT3 ASCPROD PROMPT4 ADJUST~ BINPROD BINQTY BINRATE COL DECIND MULT10 NODECIMS ROW | SHIFT TENWD ? fifi A10MAIN A13CALC (EXE) Вводит определяет количество в двоичные, вычисляет .MODEL .STACK .DATA EQU EQU EQU EQU LABEL DB DB DB LABEL DB DB DB DB DB DB DB DB DW DW DW DW DB DB DW DW DB DW DW .CODE PROC MOV MOV MOV MOV INT SMALL 64 числа, как ASCII-величины, десятичных знаков, преобразует произведение, выводит как ASCII. 2 8 ;Экранные позиции 52 10 14 BYTE ;Первое 6 ; для ? 6 DUP(?) BYTE ;Второе б ; для •? 6 DUP(?) 'Quantity? 'Rate? 'Product = ' 10 DUP(3 0H) 'Press any key ? 00 00 00 00 00 01 00 00 ? 10 FAR AX,©data DS, AX ES,AX AX,0003H 10H число (множимое): ввода с клавиатуры число (множитель): ввода с клавиатуры to continue or Esc to quit' ;Данные Инициализация DS и ES регистров Установка видеорежима и очистка экрана Рис. 13.5. Обработка ASCII- и двоичных данных 340 Часть IV. Обработка данных
А20: A10MAIN '" Bl0INPUT B10INPUT ' C10QTY CIOQTY CALL CALL CALL CALL CALL- CALL CALL CALL CMP JNE MOV INT ENDP Ввод PROC MOV MOV LEA MOV CALL MOV LEA INT MOV INC LEA MOV CALL MOV / LEA ? INT INC RET ENDP Q10WINDOW ; BIOINPUT CIOQTY DlORATE E10MULT F10PROD G10FORMAT Hi0PAUSE AL/1BH A20 AX,4C00H 21H Очистка экрана Ввод двух чисел Преобразование первого в двоичное Преобразование второго в двоичное Вычисление произведения Преобразование его в ASCII Вывод произведения Пауза для пользователя Клавиша Esc нажата? нет, продолжить Завершить чисел с клавиатуры: NEAR ROW,TOPROW+l COL,LEFTCOL+S BP,PROMPTl CX,15 K10DISPLY AH,0AH DX,QTYPARAM 21H COL,LEFTCOL+3 ; ROW BP,PROMPT2 ; CX,15 K10DISPLY AH,OAH . , DX,RATEPAR 21H ROW Используются АХ, BP, CX, SI Запрос первого числа (множимого) Число символов Ввод первого числа (множимого) с клавиатуры Установка столбца Следующая строка Запрос второго числа (множителя) Число символов Ввод второго числа (множителя) с клавиатуры Следующая строка Преобразование первого числа (множимого) в двоичное: PROC -MOV MOVZX LEA , ADD CALL MOV MOV RET ENDP NEAR NODECIMS/00 CX,ACTQLEN SI,QTYFLD-1 SI,CX J10ASCBIN AX,BINPROD BINQTY,AX Используются АХ, CX, SI Сброс количества цифр Длина первого числа Установка позиции первого числа Преобразование в двоичное Сохранить результат как первое двоичное число Рис. 13.5 (Продолжение) Глава 13. Арифметика: работа с данными форматов ASCII и BCD 341
/ DlORATE v DlORATE ElOMULT - E20: E30: E40: E50: E70: E80: ElOMULT Преобразование второго числа (множителя) в двоичное: PROC MOVZX LEA ADD CALL MOV MOV RET ENDP NEAR CX,ACTRLEN SI,RATEFLD-1 SI,CX J10ASCBIN AX,BINPROD BINRATE,AX Используются AX, CX, SI Количество цифр Установка позиции второго числа Преобразование в двоичное Сохранить результат как второе двоичное число Умножение первого числа на второе, округление произведения до указанного числа разрядов: PROC MOV ' LEA MOV CLD NEAR CX,10 DI,ASCPROD AL,30H REP STOSB MOV MOV MOV CMP JA SUB JLE MOV MOV IMUL LOOP MOV SHR MOV MOV MUL ADD ADC CMP JB XOR JMP CMP JZ DIV XOR RET ENDP SHIFT,10 ADJUST,00 CX,NODECIMS CL,06 E40 'CX,02 E30 NODECIMS,02 AX,01 AX, 10 E2 0 SHIFT,AX AX,1 ADJUST,AX AX,BINQTY BINRATE AX,ADJUST DX,00 DX,SHIFT E50 AX,AX E7 0 ADJUST,00 E80 SHIFT DX,DX Используются АХ, CX, DI Длина произведения Установить ASCII-произведение равным 30 Начальная величина сдвига Если более б цифр, ошибка •Пропустить, если 0, 1 или 2 цифры иначе установить 2 цифры •Вычислить ■ величину сдвига = 1, 10, 100,... •Величина округления = половина сдвига = 0, 5, 50,... •Множимое х множитель = произведение •Округление произведения •Произведение слишком велико для выполнения деления? •Если да, то очистить АХ и выйти •Нужен сдвиг? нет, пропустить •Сдвиг произведения •Очистка остатка Рис. 13.5 (Продолжение) 342 Часть IV. Обработка данных
/ F10PROD F30: F40: F50: F60: F10PROD # G10FORMAT G20: G30: G10FORMAT Преобразование произведения PROC LEA MOV ADD CMP JNE DEC CMP JNZ CMP JB DIV OR MOV DEC SUB JMP OR MOV RET ENDP NEAR SI,ASCPROD+7 BYTE PTR[SI], SI,NODECIMS BYTE PTR[SI], F40 SI DX,00 F50 AX,0010 F60 TENWD DL,30H [SI],DL SI DX,DX F30 AL,30H [SI],AL t t t t Форматирование произведения PROC MOV MOV , LEA CMP JNE MOV INC LOOP LEA MOV CALL RET ENDP NEAR COL,LEFTCOL+3 CX,09 SI,ASCPROD BYTE PTR[SI], G3 0 BYTE PTR[SI], SI G20 BP,PROMPT3 CX,20 K10DISPLY ; Ожидание ввода пользователя H10PAUSE PROC MOV MOV LEA MOV NEAR COL,20. ROW,22 BP,PROMPT4 CX,40 ЗОН 2 OH , нажмите ;Исполь ;Устано в ASCII: I Используются АХ, DX, SI Установка десятичной точки в ASCII-произведении Установка начальной позиции Пропустить, если в позиции точки Если DX:AX < 10, обработка заканчивается Остаток - ASCII-цифра Сохранение ASCII-символа Очистка остатка Сохранение последнего ASCII-символа для вывода: •Используются ВР, СХ, SI •Установка столбца •Длина ASCPROD • Замена ведущих нулей в ASCPROD пробелами •Подсказка произведения •Число символов •Вывод подсказки * любую клавишу для выхода: зуются АХ, СХ, ВР вка ; позиций ;Подска ;Число зка для пользователя символов Рис. 13.5 (Продолжение) Глава 13. Арифметика: работа с данными форматов ASCII и BCD 343
НЮ PAUSE JIOASCBIN J20: J30: J40: J90: J10ASCBIN / K10DISPLY KIODISPLY CALL MOV INT RET ENDP KIODISPLY AH,10H 16H Вывод сообщения Запрос ответа с клавиатуры Преобразование перемножаемых ASCII-чисел в двоичные В SI PROC MOV MOV MOV XOR MOV CMP JNE MOV JMP AND MUL ADD MOV IMUL MOV CMP JNZ INC DEC LOOP CMP JZ ADD RET ENDP Вывод PROC MOV MOV MOV MOV INT RET ENDP заносится адрес NEAR MULT10,Q001 BINPROD,00 DECIND,00 BX,BX AL,[SI] AL,'.' J3 0 DECIND,01 J40 AX,000FH MULT10 BINPROD,AX AX,MULT10 AX,10 MULT10,AX DECIND,00 J40 BX SI J20 DECIND,00 J90 NODECIMS,BX символов, устан NEAR AX,1301H BX,0016H DH,ROW DL,COL 10H числаNпри вызове: Используются АХ, BX, SI Инициализация Очистка ВХ Получение ASCII-символа Десятичная точка? нет Да, установить флажок Установить АН = 0 Умножение на коэффициент Добавление к двоичное величине Вычисление следующего коэффициента х 10 •Дошли до точки? нет да, увеличить счетчик •Следующий символ •Конец цикла •Десятичная точка? да, добавить к сумме овка атрибутов: ;ВР, СХ установлены при вызове •Запрос вывода строки ;Страница и атрибуты ;Строка экрана ; и столбец Рис. 13.5 (Продолжение) 344 Часть IV. Обработка данных
/ Q10WINDOW Q10WINDOW Прокручивание окна отображения, установка атрибутов: PROC MOV MOV w MOV MOV MOV MOV INT RET ENDP END NEAR АХ,0605Н ВН,1бН CH,TOPROW CL,LEFTCOL DH,BOTROW DL,RIGHTCOL ЮН * A10MAIN Пять строк Атрибуты Верхний левый угол Правый нижний угол * Рис. 13.5 (Окончание) Программа состоит из следующих процедур: • A10MAIN выполняет инициализацию и вызывает последовательно другие процедуры; • B10INPUT воспринимает ввод с клавиатуры в ASCII-формате; вводимые числа могут содержать десятичную точку; • C10QTY инициализирует преобразование ASCII-числа в двоичное; • Dl ORATE инициализирует преобразование ASCII-числа в Двоичное; • E10MULT выполняет умножение и округление результата с тремя или более (до шести) разрядами; • F10PROD вставляет десятичную точку, определяет младший разряд для начала сохранения ASCII-символов и преобразует двоичное число в ASCII- формат; • G10FORMAT удаляет ведущие нули (заменяет пробелами) и вызывает K10DISPLY для вывода значения; • H10PAUSE запрашивает ввод с клавиатуры (нажатие <Esc> указывает программе завершить работу); • J10ASCBIN преобразует ASCII-числа в двоичные (единая процедура для множимого и множителя) и определяет число десятичных позиций во вводимых данных; • Kl 0DISPLY выводит данные на экран; • Q10WINDOW выполняет прокрутку окна в середине экрана, где выводятся результаты. Глава 13. Арифметика: работа с данными форматов ASCII и BCD 345
Ограничения Эта программа ограничивает общее число десятичных цифр в обоих множителях до 6. Еще одно ограничение - если произведение превысит по величине 6553,50, программа обнулит его. Практически, программы в таких случаях выдают сообщение об ошибке. Проверка ошибок Программа, предназначенная для массового использования, должна не только обрабатывать ошибки, но и проверять исходные величины, не позволяя вводить символы, не допустимые в составе чисел. При вводе любого другого символа (кроме цифр от 0 до 9 и десятичной точки) программа должна выдавать соответствующее сообщение и повторять процедуру ввода. Для реализации такой проверки полезна инструкция XLAT, рассмотренная в следующей главе. Практически, такие программы проверяют много условий, например, ввод нулевых, слишком большие и отрицательных значений. Отрицательные значения Некоторые программы должны позволять вводить отрицательные значения, особенно для корректировки или изменения записей. Можно организовывать ввод минуса (-) перед числом, например, -14,32, или после, например, 14,32-. Программа может проверять наличие минуса при преобразовании числа в двоичную форму. С другой стороны, можно оставить двоичное число положительным, и просто выводить минус вместе с результатом после выполнения арифметических операций. Для получения из отрицательного двоичного числа ASCII-формата преобразуйте его в двоичную форму и измените знак на противоположный, как описано в предыдущей главе. Используйте инструкции IMUL и ЮТУ для обработки знаковых величин. Для округления отрицательного числа вместо прибавления необходимо отнимать половину значения округляемого разряда. Ключевые моменты • ASCII-число занимает один байт под каждую цифру. Для числового элемента младшие 4 разряда содержат значащую цифру, а старшие - всегда цифру 3. • Обнуление четырех старших разрядов каждого байта ASCII-числа преобразует его в двоично-десятичное (BCD) неупакованное число. • Поместив значащие разряды двух байтов ASCII-числа в один байт, мы получим упакованное десятичное число. 346 Часть IV. Обработка данных
• После сложения ASCII-чисел для корректировки используется инструкция AAA; после вычитания - инструкция AAS. • Перед умножением ASCII-чисел множители необходимо преобразовать в неупакованный двоично-десятичный формат. После умножения для корректировки результата необходимо использовать инструкцию ААМ. • Перед делением ASCII-чисел делимое и делитель необходимо преобразовать в неупакованный двоично-десятичный формат и использовать инструкцию ААМ для корректировки делимого (преобразование в двоичную форму). • Для выполнения сложных арифметических преобразований ASCII-числа лучше преобразовать его в двоичный формат. Для представления чисел в ASCII-формате достаточно цифр от 0 до 9 (символы от ЗОН до 39Н), точки и знака минуса. Вопросы и упражнения 1. Предположим, что ВХ содержит ASCII-число 9 (0039Н), a DX - ASCII- число 5 (0035Н). Объясните результаты выполнения следующих команд: a) ADD ВХ,34Н б) ADD BX,DX в) SUB BX,DX г) SUB ВХ,0СН AAA AAA AAS AAS 2. Используя шестнадцатеричную запись, представьте десятичное число 4217 в следующих форматах: (а) упакованном двоично-десятичном, (б) неупакованном двоично-десятичном, (в) ASCII. 3. Неупакованная двоично-десятичная величина BCDVAL содержит значение 01060803Н. Напишите программу, преобразующую его в ASCII-формат (31363833Н). 4. Элемент данных ASCVAL1 содержит ASCII-число 174, a ASCVAL2 - ASCII- число 4. Укажите инструкции, необходимые для перемножения .чисел и сохранения результата в формате ASCII в элементе ASCPROD. 5. Используя элементы данных из предыдущего задания, разделите ASCVAL1 на ASCVAL2 и сохраните частное в ASCQUOT. 6. Вручную выполните следующие действия: (а) преобразуйте ASCII-число 27965 в двоичное представление, затем представьте результат в шестнадца- теричной форме, и (б) преобразуйте его обратно в ASCII-представление. 7. Напишите и протестируйте программу, организующую (а) ввод двоичных значений в регистры ЕАХ, ЕВХ, ЕСХ и EDX и (б) вывод значений из этих регистров на экран в формате ASCII. Глава 13. Арифметика: работа с данными форматов ASCII и BCD 347
8. Напишите и протестируйте программу, организующую (а) ввод двух чисел с клавиатуры, (б) преобразование их из ASCII-формата в двоичную форму, (в) сложение двоичных чисел, (г) преобразование суммы в ASCII-формат и (д) вывод результата на экран. Организуйте возможность ввода любого количества пар чисел. Если при очередном запросе нажать только клавишу <Enter>, программа должна завершать работу. 348 Часть IV. Обработка данных
Глава 14 Определение и обработка таблиц Цель: рассмотреть методы определения таблиц и массивов, выполнения в них поиска и сортировки их элементов. Введение Многие программы используют в работе массивы или таблицы, содержащие различные данные, например, имена, описания, количества или даты. Эта глава начинается с демонстрации методов определения таблиц, а затем показывает несколько методов поиска в них. Методы поиска в таблицах зависят от способов Ьпределения таблиц, кроме того, существует много методов поиска и определения помимо рассмотренных в этой книге. Определение и использование таблиц в значительной степени основывается на применении уже изученного материала. Еще один существенный момент при работе с таблицами - сортировка и расположение элементов таблицы в определенном порядке. В главе также рассматривается адресация таблиц и двумерные таблицы. Единственная инструкция, представляемая в этой главе - XLAT. Определение таблиц Для обеспечения возможности поиска большая часть таблиц имеет определенную структуру - каждая запись определяется в том же самом формате (цифровом или текстовом), что и все остальные, имеет определенную длину. Часто записи отсортированы в определенном порядке для еще большего ускорения поиска. Первая таблица, которую мы определяли в этой книге - это стек. В приведенном примере эта таблица содержит 64 записи по 2 байта каждая, не заполненных какими-либо определенными значениями. Имя стека - STACK - указывает на первое слово, а таблица формируется как STACK DW 64 DUP(?). Следующие две таблицы, MONTHTBL и CUST_TBL, инициализируются символьными и числовыми значениями, соответственно. MONTH_TBL содержит сокращенные названия месяцев, a CUSTTBL - номера покупателей: MONTH_TBL DB ' ' Jan ' , ' Feb' , ' Mar ' , . . . , , ' Dec ' CUST_TBL DB 205, 208, 209, 212, 215, 224 Каждая запись в MONTHTBL имеет длину 3 байта. Однако, хотя числа в CUST_TBL тоже трехразрядные, ассемблер определит для их хранения однобайтовые элементы, если значение ни одного элемента не превысит 255. Глава 14. Определение и обработка таблиц 349
Таблица может также содержать одновременно и числовые, и строковые данные, при условии, что их структура строго определена. В следующем списке товаров каждое число состоит из двух десятичных разрядов и хранится в одном байте, а каждая строковая запись имеет длину 9 байтов и, если название товара короче, то оно дополняется в конце пробелами: STOCK_TBL DB 12, 'Computers', 14, 'Paper ' , 17, 'Diskettes' , ... Четыре точки после слова 'Paper' указывают на четыре пробела, которые будут дополнять слово в записи. Для большей наглядности можно записывать каждую пару число-строка в отдельной строке, например, так: STOCK_TBL DB 12, 'Computers' DB 14, 'Paper. DB 17, ' 'Diskettes' Чтобы определить таблицу со 100 записями, где каждая длиной в 15 байтов (всего - 1500 байтов), можно воспользоваться следующей строкой: STORE_TBL DB 100 DUP(15 DUP(' ')) В программе такая таблица может использоваться для хранения вплоть до 100 значений, сгенерированных в процессе работы, или для хранения записей, считанных с клавиатуры или из файла на диске. На практике очень многие программы используют в работе таблицы. Эти таблицы хранятся в файлах на диске, и любая программа может обращаться к таким файлам. Для получения доступа к данным программа может считать содержимое файла в определенную для этого таблицу в памяти. Смысл такой организации в том, что содержимое определенной во внешнем файле таблицы можно изменять. Если данные определяются внутри программы, то при изменении этих данных понадобится достать исходный текст, внести в него необходимые изменения, оттранслировать программу и запустить на выполнение. Если таблица определена в отдельном файле на диске, изменять данные намного проще. Пример файла таблицы приведен в гл. 17. Операторы TYPE, LENGTH и SIZE Язык ассемблера содержит несколько операторов, облегчающих работу с таблицами. Например, длина таблицы может измениться и, как следствие, может потребоваться учесть эти изменения в программе и добавить процедуру, проверяющую длину таблицы. Использование операторов TYPE, LENGTH и SIZE может значительно уменьшить объем кода, который необходимо будет изменять. Рассмотрим определение таблицы из 12 слов: RAIN_TBL DW 12 DUP (?) /Таблица из 12 слов 350 Часть IV. Обработка данных
Оператор TYPE можно использовать в программе для определения типа элементов таблицы (в данном случае - слово, директива DW), оператор LENGTH - для определения числа элементов (12 - в данном случае), а оператор SIZE-для определения общего размера таблицы (12x2 = 24 байта). Следующие примеры иллюстрируют применение этих операторов: MOV AX,TYPE RAINJTBL ;АХ = 0002Н, 2 байта MOV BX,LENGTH RAIN_TBL ;BX = 000CH, 12 элементов MOV CX,SIZE RAINJTBL ;СХ = 0018Н, 24 байта Значения, получаемые с помощью этих операторов, можно использовать при сортировке таблицы или поиске в таблице. Например, следующим образом можно проверить, содержит ли индексный регистр адрес (смещение) конца таблицы: CMP SI,JSIZE RAIN_TBL В гл. 25 операторы SIZE, LENGTH и TYPE рассмотрены более подробно. В последующих параграфах этой главы демонстрируются различные применения таблиц в программах. Прямая адресация элементов таблицы Предположим, что программа должна при вводе номера месяца выводить его название, например, March для 03. Для реализации такой процедуры необходимо определить таблицу названий месяцев, причем названия должны иметь одинаковую длину. Длина каждого элемента должна быть равна длине самого длинного элемента - September, и таблицу можно определять следующим образом: MONTH_TBL DB 'January DB 'February.', DB 'March. DB 'December.' Адрес элемента 'January' равен MONTH_TBL+0, элемента 'February' - MONTH_TBL+9, 'March' - MONTH_TBL+l 8 и т.д. Допустим, что пользователь вводит цифру 3 для элемента 'March', и программа должна найти его в таблице. Программа должна выполнить следующие действия. 1. Преобразовать введенную цифру из ASCII-формата (ЗЗН) в двоичный (03Н). 2. Вычесть из полученного числа 1, т.е. 3-1=2 (поскольку первый элемент имеет адрес MONTH_TBL+0). Глава 14. Определение и обработка таблиц 351
3. Умножить полученный номер на 9 (длину каждой записи): 2x9 = 18. 4. Прибавить произведение (18) к адресу MONTH_TBL; результат - адрес требуемого элемента, т.е. элемент 'March' начинается с адреса MONTH_TBL+l 8. Этот метод обращения к элементам таблицы называют прямой адресацией. Поскольку алгоритм вычисляет непосредственный адрес требуемого элемента таблицы, нет необходимости определять номера месяцев в отдельной таблице или последовательно перебирать названия месяцев. Прямая адресация, пример 1: таблица месяцев С помощью фрагмента программы, приведенного на рис. 14.1, продемонстрирован прямой доступ к таблице с названиями месяцев. Программа принимает число 12 в качестве вводимого значения (месяц December) и преобразует его из ASCII-формата в двоичный (по методу, описанному в гл. 13): Исходное введенное число (12) = 3132Н Результат XOR с 3030Н = 0102Н Старший байт умножен на 10: = ОАН Прибавлен младший байт = ОСН (десятичное число 12) * Далее программа определяет адрес требуемого элемента таблицы: Вычитается 1 из номера в АХ 000BH (десятичное число 11) Результат умножен на 9 (длина элемента) 0063Н (десятичное число 99) Результирующий адрес равен MONTHTBL + 63Н LEN_ENTRY MONTH_IN MONTH_TBL .386 EQU 9 DB '12'. DB 'January DB 'April DB 'July DB 'October 7 • • • ;Длина элемента таблицы /ASCII ' ', 'February ', 'May ', 'August ', 'November 3132' ', 'March ' •, 'June ' ', 'September' ', 'December ' ; Преобразование ASCII-номера месяца в двоичный формат: XOR WORD PTR MONTH_IN,3 030H ;Очистка старшего ; полубайта ASCII Рис. 14.1. Прямая адресация в таблице: пример 1 352 Часть IV. Обработка данных
MOVZX IMUL ADD ; DEC IMUL LEA ADD ; MOV MOV MOV MOV INT AX,MONTH_IN AX, 10 AL,MONTH_IN+l Вычисление адр( AL AX,LEN_ENTRY BP,MONTH_TBL BP,AX Умножение левой цифры на 10 и добавление правой цифры эса месяца в таблице: ■Коррекция на 1 •Умножение AL на 9 •Добавление смещения к адресу таблицы Отображение названия месяца: AX,1301H BX,0016H CX,LEN_ENTRY DX,0812H ЮН •ES:BP уже загружены • Запрос вывода • Страница:атрибуты ;9 символов ;Строка:столбец Рис. 14.1 (Окончание) Программу можно улучшить, организовав ввод с клавиатуры и проверку корректности введенного номера месяца. Прямая адресация, пример 2: таблица месяцев и дней недели В программе, фрагмент которой приведен на рис. 14.2, выполняется считывание текущей системной даты и вывод ее на экран. Функция 2АН прерывания INT 21Н помещает в регистры следующие значения: AL День (Воскресенье = 0) DH Месяц (01—12) СХ Год (в данной программе не DL День месяца (01—31) используется) Программа использует эти значения для вывода названия дня недели, номера дня в месяце и названия месяца, например, "Wednesday", "September" и "12". Для этого программа использует две таблицы, DAYSTBL и MONTHTBL, содержащие, соответственно, названия дней недели и месяцев. Записи в MONTH_TBL и DAYS^TBL имеют длину по 9 байтов каждая. Если соответствующее название имеет длину меньше 9 байтов, справа оно дополняется пробелами. Программа умножает номер дня недели на 9 (длину записей в таблице DAYS_TBL). Результат - смещение в таблице, по которому расположен нужный день - например, Sunday по адресу DAYS_TBL+0, Monday - DAYS_TBL+9 и т.д. Название дня выводится на экран непосредственно из таблицы. Глава 14. Определение и обработка таблиц 353 23 —5-741
LEN_ENTRY DAYOFMON SAVEDAY , SAVEMON TEN ROW COLUMN DAYS_TBL MONTH_TBL .386 ; EQU DW DB DB DB DB DB DB DB DB DB DB DB DB DB MOV INT MOV MOV МП\7 rlUV IMUL LEA ADD MOV CALL MOVZX DEC IMUL LEA ADD MOV CALL • МПЛ7'7'У DIV OR MOV LEA MOV CALL 9 00 *? ? 10 10 30 'Sunday 'Tuesday 'Thursday 'Saturday 'January 'April 'July 'October AH,2AH 21H SAVEMON,DH SAVEDAY,DL AM П AX,LEN_ENTI BP,DAYS_TBI BP,AX CX,LEN_ENTI B10DISPLY Л V CAT r'C'M'PiTvT ЛЛ , DAV £iivlvJl\l AX AX,LEN_ENTI , Длина элемента таблицы Строка экрана и столбец , 'Monday ' , 'Wednesday' , 'Friday ' , 'February ', 'March ' , 'May ', 'June ' , 'August ', 'September' , 'November ', 'December ' i IY Vf Vf BP,MONTH_TBL BP,AX CX,LEN_ENTI B10DISPLY AV QZWCnZW TEN AX,3030H DAYOFMON, A] BP,DAYOFMOI CX,2 B10DISPLY IY < <I Получение текущей даты Сохранение месяца Сохранение дня месяца Вывод дня недели: Длина названия дня недели Адрес таблицы + смещение Длина •Вывод месяца: Уменьшение номера месяца на •Длина названия месяца •Адрес таблицы + смещение • Длина Вывод дня месяца: ■Преобразование дня из • двоичного в ASCII •Сохранение ASCII-дня •Число символов 1 Рис. 14.2. Прямая адресация в таблице: пример 2 354 Часть IV. Обработка данных
B10DISPLY BIODISPLY PROC PUSHA MOV MOV MOV MOV INT INC POPA RET ENDP NEAR AX,1301H BX,0016H DH,ROW DL,COLUMN 10H ROW ;Общая программа вывода: ;BP, CjX при вводе установлены ;Сохранение регистров ;Запрос вывода /Атрибуты страницы /Строка экрана ; и столбец ;Следующая строка /Восстановление регистров Рис. 14.2 (Окончание) Поиск в таблицах Некоторые таблицы содержат числа, не расположенные в каком-то определенном порядке. Типичный пример - таблица товаров с номерами, идущими не подряд, например, 34, 38, 41, 139 и 145. Другая таблица, например таблица сумм доходов, может содержать произвольные значения в некотором диапазоне. Далее рассматриваются таблицы обоих типов и методы поиска в них. Таблицы с уникальными элементами Номера товаров в большинстве случаев не представляют собой непрерывной последовательности. Вместо этого они обычно сгруппированы по категориям товаров, в которых старшие разряды могут указывать на категорию товара или его размещение в определенном отделе. Кроме того, с течением времени некоторые товары могут исчезать из списка, а другие - добавляться в список. Например, попробуем определить таблицу с номерами товаров и соответствующими названиями. Их можно определять и в разных таблицах, например: STOCK_NO DB '05','10', »12',... STOCK_DESC DB 'Excavators','Lifters...','Presses...',... При поиске в таблицах на каждом шаге можно увеличивать адрес в первой таблице на 2 (длину записи в STOCK_NO), а адрес во второй - на 10 (длину записи в STOCK_DESC). Или можно считать число шагов, проделанных при поиске в первой таблице до обнаружения нужного элемента, отнять от него 1, умножить его на 10 и прочитать по полученному адресу элемент второй таблицы. С другой стороны может оказаться проще определить номера и названия товаров в одной таблице, используя одну строку для одной пары определений: Глава 14. Определение и обработка таблиц 355 23*
STOCK_TBL DB ' 05 ' , 'Excavators' DB '10' ,'Lifters...' DB '12','Presses...' Во фрагменте программы, приведенном на рис. 14.3, определена таблица с 6-ю парами номер-название. (В примере используется таблица с уникальными элементами, отсортированными по возрастанию номеров товаров. Сортировка в таблицах рассмотрена далее в этой главе. - Прим.ред.) LEN_ LEN_ _STKNO .DESCR STOCKN_IN STOCK_TBL A2 0 A30 A40 A50 Ia90 EQU EQU DB DB DB DB DB DB DB MOV LEA MOV CMP JNE MOV CMP JE JB ADD ADD LOOP JMP INC INC MOV MOV MOV MOV MOV INT 02 10 , '12' '05','Excavators' ; '10','Lifters '12','Presses ' ; ' 15' ,'Valves ' 23 ' , 'Processors' , ' 27 ' , 'Pumps CX,06 SI,STOCK_TBL AL,STOCKN_IN AL,[SI] A3 0 AL,STOCKN_IN+l AL,[SI+1] A5 0 A40 SI,LEN_STKNO SI,LEN_DESCR A2 0 A90 SI SI AX,1301H bp,si BX,0061H CX,LEN_DESCR DX,0812H 10H Длина номера товара и описания • Вводимый номер товара Начало таблицы Конец таблицы Инициализация сравнения Старшая цифра номера не равна, выход равна : младшая цифра номера равна, товар найден меньше, нет в таблице больше, переход к следующему товару ■Нет в таблице, вывод сообщения об ошибке и выход • Выборка описания ;Запрос вывода описания товара ;Страница:атрибут ;10 символов ;Строка:столбец Рис. 14.3. Поиск в таблице с помощью инструкции СМР 356 \ Часть IV. Обработка данных
Процедура поиска начинается со сравнения первого байта введенного номера, STOCK_IN, с первым байтом номера в таблице. Результат сравнения может быть меньше, больше или равно. 1. Меньше. Если результат сравнения первого или второго байта будет меньше, программа определяет, что товара с таким номером нет в таблице и выпол^ няет переход к метке А40 (после этой метки должна идти не включенная в текст программы процедура обработки ошибок). Например, программа сравнивает номер 01 с первым номером 05: первые байты равны, но второй байт введенного номера, 1, меньше второго байта первого элемента таблицы, 05. Поэтому программа определит, что элемента с таким номером в таблице нет. 2. Больше. Если сравнение первого или второго байта введенного номера с номером из таблицы даст результат "больше", программа должна продолжать поиск. Для этого увеличивается значение в регистре SI, содержащем адрес таблицы. Например, программа сравнивает введенный номер 06 с номером из таблицы 05: первые байты равны, но второй байт введенного номера (6) больше второго байта номера из таблицы (5). Поэтому программа продолжает поиск. 3. Равно. Если оба байта сравниваемых номеров равны, значит, требуемый товар найден. Например, если введен номер 10, то при сравнении с табличным номером 10 программа определит, что искомый товар найден и выполнит переход к метке А50, за которой следует процедура вывода названия товара на экран. Процедура поиска в программе повторяется максимум 6 раз. Если после шести сравнений введенный номер не найден, программа определит, что товара с таким номером нет в таблице. Таблица также могла бы содержать цены за единицу товара. Пользователь мог бы вводить номер товара и количество покупаемых единиц, а программа, найдя нужный товар в таблице, вычислять сумму, подлежащую оплате, и выводить ее на экран вместе с названием товара и объемом заказа. В программе (рис. 14.3) номер товара имеет длину 2 байта, а название - 10 байтов. Детали реализации программы могут различаться в зависимости от размеров элементов. Например, для сравнения элементов величиной 3 байта можно использовать инструкцию REPE CMPSB, но при этом придется учесть, что регистр СХ уже используется инструкцией LOOP. Таблицы, содержащие диапазоны значений Некоторые таблицы должны содержать определенные диапазоны значений, например, таблица для определения величины налога на прибыль. Эта таблица может выглядеть, например, так: Глава 14. Определение и обработка таблиц 357
Доход 0-1000.00 1000.01-2500.00 2500.01-4250.00 4250.01-6000.00 6000.01 или больше Ставка налога 0.1 0.15 0.18 0.20 0.23 Поправка 000.00 050.00 125.00 260.00 390.00 Как видно из этой таблицы, ставка налога возрастает с ростом суммы дохода. Поправка учитывается при вычислении налога для больших сумм. Первый элемент записи в таблице должен содержать максимальный доход (для каждого уровня налогообложения): DD DD DD DD DD 100000, 250000, 425000, 600000, 999999, ю, 15, 18, 20, 23, 00000 05000 12500 26000 39000 При поиске в такой таблице программа сравнивает введенную сумму дохода с первым полем строки и в зависимости от результата выполняет следующие действия: • если сумма больше, чем величина в первом поле, поиск будет продолжен; • если сумма меньше или равна, то для вычислений используются данные следующих элементов соответствующей строки таблицы; заметьте, что последняя запись в таблице содержит число 999999 - максимально возможное, и это сделано для корректного завершения поиска. Поиск в таблице при помощи сравнения строк Инструкции REPE CMPS можно использовать для сравнения номеров элементов с длиной 2 или более байт. Фрагмент программы, приведенный на рис. 14.4, определяет STOCK_TBL о трехзначными номерами товаров. Последняя запись в таблице содержит номер 999 (максимально возможный) для корректного завершения поиска. Программа могла бы использовать инструкцию LOOP для завершения поиска, но инструкция REPE тоже использует регистр СХ, делая невозможным одновременное использование LOOP. Процедура поиска сравнивает величину STOCKN_IN (в тексте определенную значением 123) с номером в каждой строке таблицы: 358 Часть IV. Обработка данных
STOCKN_IN 123 123 123 123 123 Запись в таблице 035 038 049 102 123 Результат сравнения Больше, перейти к следующей записи Больше, перейти к следующей записи Больше, перейти к следующей записи Больше, перейти к следующей записи Равно, запись найдена 0000 0003 0010 001D 002А 0037 0044 0051 / А2 0: АЗО: А40: / |а90: LEN LEN STOCKN STOCK_ _STKNO _DESCR _IN TBL CLD LEA MOV LEA EQU 03 EQU 10 DB '123' DB '035', DB '038', DB '049', DB '102', DB '123', DB '127', DB '999', DI,STOCK_ 'Excavators 'Lifters 'Presses 'Valves 'Processors ' Pumps 10 DUP(' ' TBL . ; CX,LEN_STKNO SI,STOCKN REPE CMPSB JE JB ADD ADD JMP MOV MOV MOV MOV MOV INT JMP A3 0 A40 DI,CX _IN DI,LEN_DESCR A20 AX,1301H BP,DI BX,0061H CX,LEN_DESCR DX,08l'2H 10H A90 Вывод сообщения об ошибке ;Длина номера товара ; и описания ' ;Таблица ) Иниц-я адреса таблицы Сравнивать 3 байта Иниц-я адреса номера товара Сравнение номеров равно, выход меньше, нет в таблице Добавить СХ к смещению Следующий элемент таблицы Запрос вывода описания товара Страница: атрибуты 10 символов Строка:столбец Рис. 14.4. Поиск в таблице с помощью CMPSB В начале выполнения программа инициализирует регистр DI адресом смещения для STOCK_TBL (0003H), СХ - длиной элемента таблицы (03Н), a S1 - смещением STOCKN_IN (000). Пока сравниваемые байты содержат одинаковые Глава 14. Определение и обработка таблиц 359
значения, инструкция CMPSB будет продолжать сравнения, увеличивая значения в регистрах SI и DI на 1 для каждой пары байтов. Сравнение с первой записью в таблице закончится со следующим результатом: DI содержит 0004, SI - 0001, СХ - 0002, а результат - больше (после сравнения первых байтов). При втором сравнении DI должен содержать 0010, SI - 0000. Коррекция адреса в SI сводится просто к повторной загрузке в него адреса STOCKN_IN. Однако для коррекции адреса в DI необходимо учесть, каким образом закончилось сравнение: после первой, второй или третьей пары байтов. СХ содержит количество оставшихся байтов, в данном случае, 0002. Прибавив к DI значение из СХ и длину названия в таблице, получим смещение следующего элемента таблицы: Адрес в DI после завершения CMPSB 0004H Плюс оставшаяся длина в СХ + 02Н Плюс длина названия товара + РАН Результат - адрес следующего номера в таблице 0010Н Поскольку СХ содержит число байтов, не подвергавшихся сравнению, данная операция выполняется корректно в любом случае (несовпадение первых, вторых или третьих байтов). В случае полного равенства (все три пары байтов совпали) СХ содержит 0000, a DI - значение адреса требуемого названия. Программа выводит название на экран непосредственно из таблицы. Таблицы с записями различной длины Можно определить таблицу с записями различной длины. Например, символ ООН мог бы означать конец очередной записи, а символ FFH - конец всей таблицы. Для поиска символов-ограничителей подошли бы инструкции SCAS. Однако нужно убедиться, что в самой записи не может встретиться символ- ограничитель; например, в записях, содержащих результаты вычислений, могут встретиться любые символы, включая ООН и FFH. Инструкция XLAT (преобразовать) S Инструкция XLAT заменяет определенные байты на поставленные им в соответствие в заданной таблице. Эта инструкция может применяться, например, для оценки значений в таблицах или для шифрования данных перестановками. Формат этой инструкции таков: [метка: ] XLAT /операндов нет Чтобы использовать XLAT, нужно определить таблицу преобразования, содержащую все 256 символов с соответствующими эквивалентами. Инструкция XLAT получает адрес таблицы из регистра ВХ, а преобразуемый байт должен находиться в AL. 360 Часть IV. Обработка данных
В следующем фрагменте программы ASCII-цифры О—9 преобразуются в формат EBCDIC, используемый в мэйнфреймах фирмы IBM. Поскольку ASCII-символы 0—9 имеют номера 30—39Н, a EBCDIC-символы - F0—F9H, для выполнения преобразования можно использовать инструкцию OR. Однако для преобразования чисел необходимо также преобразовывать знаки минуса (в ASCII он имеет код 2DH) и десятичной точки (2ЕН) в символы EBCDIC (60H и 4ВН). Все остальные символы можно преобразовать в пробелы (в EBCDIC - код 40Н). В таблице преобразования EBCDIC-коды определяются на местах соответствующих ASCII-символов; то есть, коды F0H—F9H размещаются в байтах с номерами ЗОН—39Н, а коды символа минуса и десятичной точки - в соответствующих байтах. Все остальные байты.в таблице должны содержать код пробела (40Н). Таблица может быть определена, например, так: XLAT_TBL DB DB DB DB DB DB 45 DUP(40H) ;Таблица 60H, 4BH 40H 0F0H,0F1H,0F2H,0F3H,0F4H 0F5H,0F6H,0F7H,0F8H,0F9H 198 DUP(40H) Заметьте, что в первой строке объявления определены 45 пробелов, по адресам от XLAT_TBL+00 до XLAT_TBL+44. Далее определены эквиваленты знака минуса и точки, затем - еще один пробел, затем - цифры, а завершают таблицу 198 пробелов. XLAT использует значение в AL как величину смещения; т.е. ВХ содержит адрес начала таблицы, AL - смещение от ее начала, а инструкция, суммируя их, получает адрес требуемого символа. Например, если AL содержит ООН, то требуемый символ - байт по адресу XLAT_TBL+00 (он содержит 40Н). XLAT заменит ООН в регистре AL на 40Н из таблицы. А если AL содержит 32Н, то требуемый символ - байт по адресу XLAT_TBL+50 (он содержит 0F2H). XLAT поместит это значение в AL. Следующий фрагмент программы обрабатывает 6-байтовую ASCII-последовательность ASC_NO, используя таблицу для преобразования этой последовательности в формат EBCDIC. Изначально, ASC_NO содержит -31,5 с пробелом в конце. В шестнадцатеричной форме это 2D33312E3520H. В конце выполнения EBC_NO содержит результат преобразования - 60F3F14BF540H, в чем можно убедиться, использовав DEBUG: ;ASCII-строка для преобразования ;Строка для хранения результата /Адрес ASCII-строки ;Адрес EBCDIC-строки /Длина обрабатываемой строки ASC_NO EBC_NO DB DB LEA LEA MOV '-31.5 ' 6 DUP(' ' SI,ASC_NO DI,EBC_NO CX,06 Глава 14. Определение и обработка таблиц 361
LEA BX,XLAT_TBL ;Адрес таблицы переводов L10: LODSB ;Загрузить символ из ASC_NO в AL XLAT /Преобразовать символ STOSB ;Сохранить символ из AL в EBC_NO LOOP L10 /Повторить со следующим символом Программа: вывод шестнадцатеричных кодов байтов и соответствующих ASCII- символов Фрагмент программы, приведенный на рис. 14.5, реализует вывод. 256 шестнадцатеричных кодов (от ООН до FFH), включающий большинство символов ASCII-таблицы, например, символ S и соответствующий ему код, 53Н. Результат вывода представляет собой таблицу размером 16x16 ячеек: 00 *F0 01 F1 02 F2 03 F3 04 F4 05 F5 Об F6 07 F7 08 F8 09 F9 0А FA ов FB ОС FC 0D FD 0Е FE 0F FF Как было показано в гл. 7 (рис. 7.1), вывод символов таблицы ASCII на экран не вызывает особых проблем. Однако вывод шестнадцатеричных кодов - более сложная процедура. Например, ООН необходимо преобразовать в 3030Н (ASCII-представление), 01Н - в 3031Н и т.д. То есть 01Н (один байт) нужно преобразовать в два символа, 01. Если какой-либо из разрядов содержит цифру больше 9, то ее необходимо преобразовать в соответствующую букву. Программа определяет переменную HEXCTR, изначально содержащую ООН, и последовательно увеличивает на 1 ее значение для каждого из 256 символов ASCII. Процедура В10НЕХ разделяет HEXCTR на два символа. Например, если HEXCTR содержит 4FH, процедура выделит 4, и эта цифра будет преобразована в формат ASCII, и в регистр AL будет помещено значение 34Н. Затем будет выделена цифра F и преобразована в 46Н. Результат, 3446Н, будет выведен на экран как 4F. Процедура C10DISPLY преобразовывает не-ASCII символы в пробелы. Поскольку функция 13Н прерывания INT 10H воспринимает Backspace и другие управляющие символы, программа заменяет их пробелами. Процедура выводит строку из 16 символов, и программа завершается после вывода 16-й строки. Есть также много других способов преобразования шестнадцатеричных цифр в ASCII-символы; например, можно поэкспериментировать со сдвигами и сравнениями. 362 Часть IV. Обработка данных
ROW DISPROW HEXCTR XLATAB .386 ; A20: « • B10HEX B10HEX ; C10DISPLY C20: DB DB DB DB DB CALL LEA CALL CALL CMP JE INC JMP PROC MOVZX SHR LEA XLAT MOV MOV AND XLAT MOV RET ENDP Отобра PROC MOV MOV CMP JB CMP JAE MOV ADD LEA CMP JNE MOV MOV LEA 02 16 DUP(5 DUP(' 00 30H/31H/32H/33H 41H/42H/43H/44H clear_screen SI,DISPROW B10HEX C10DISPLY HEXCTR,OFFH A90 HEXCTR A2 0 )) 34H,35H,3 6H,37H,3 8H,3 9H 1 45H,46H 1 Стандартная процедура Иниц-я строки дисплея Преобразование и вывод Последний элемент (FF)? да, выход нет, следующий элемент Преобразование из ASCII в шестнадцатеричный код: NEAR АХ,HEXCTR АХ,04 ВХ,XLATAB [SI],AL AL,HEXCTR AL,OFH [SI]+1,AL Получение байта в АХ Выбор старшего полубайта Установка адреса таблицы Преобразование Сохранение левого символа в строке дисплея Выбор младшего полубайта Преобразование ■Сохранение правого символа жение шестнадцатеричного кода: NEAR AL,HEXCTR [SI]+3,AL AL,07H C20 AL,10H C20 'BYTE PTR [SI]+3 SI,05 DI,DISPROW+80 DI,SI C90 AX,1300H ВХ,0031Н BP,DISPROW , 20H ►Получение символа ■Менее "7? да •Более/равно 16? да иначе - пробел ;Следующая позиция строки « ;Строка заполнена? ; нет, выход ;Запрос вывода ;Страница и атрибуты ;Дата Рис. 14.5. Вывод ASCII и шестнадцатеричных значений Глава 14. Определение и обработка таблиц 363
С90: C10DISPLY MOV MOV MOV INT INC LEA RET ENDP CX, 80 DH,ROW DL, 00 10H ROW SI,DISPROW ;Длина цепочки ;Строка ;Столбец ;Следующая строка ;Повторная инициализация Рис. 14.5 (Окончание) Сортировка элементов таблицы Часто возникает потребность отсортировать данные в таблице в определенном порядке - например, имена по алфавиту или числа по убыванию. Существует множество разных способов сортировки: от простых, но медленных, до весьма замысловатых, но быстро работающих. Способ, рассмотренный в этом параграфе, весьма эффективен и подходит для большинства практических применений. Все методы сортировки сводятся к последовательным сравнениям элементов друг с другом и их перестановкам. Если сравниваемый элемент больше, чем тот, . с которым сравниваем, то выполняем перестановку. Затем сравниваем два следующих элемента и т.д. Если при первом проходе по таблице были выполнены какие-либо перестановки, то по достижении конца таблицы просмотр (с выполнением сравнения) необходимо повторить с начала. Если при очередном проходе по таблице ничего не меняется, процедуру можно закончить. В следующем псевдокоде SWAP - элемент данных, указывающий, производились перестановки (YES) или не производились (N0). L10: Инициализировать адреа последней записи в таблице L2 0: Установить SWAP в N0 Инициализировать адрес начала таблицы L30: Больше ли текущая запись следующей? Если да, то выполнить перестановку и установить SWAP в YES Перейти к следующей записи в таблице Достигнут ли конец таблицы? Если нет, то перейти к L3 0 Если да, то установлен ли SWAP в YES? Если да, то переходим к L20 (еще пройти по таблице) Если нет, то сортировка завершена Программа, приведенная на рис. 14.6, позволяет пользователю ввести с клавиатуры до 30 имен, сохраняя их в таблице NAMETABLE, затем выполняет их сортировку и вывод на экран отсортированной таблицы. 364 Часть IV. Обработка данных
TITLE LEN_NAME ENDADDR MESSG1 NAMECTR NAMESAVE NAME_TBL ROW SWAPPED NAMEPAR MAXNLEN NAMELEN NAMEFLD .386 A10MAIN A20: A30: A90: A10MAIN A14NMSRT (EXE) Сортировка имен, введенных с клавиатуры .MODEL .STACK .DATA EQU DW DB DB DB DB DB DB LABEL DB DB DB .CODE PROC MOV MOV MOV CLD CALL LEA CALL CMP JE CMP JE CALL JMP CALL CMP JBE CALL CALL MOV INT ENDP SMALL ' 64 ! 20 ? 'Name? '" 00 * 20 DUP(?) 30 DUP(20 DUP( ' ') ) 00 00 BYTE 21 7 21 DUP(' ' ) FAR AX,@data DS,AX ES,AX Q10CLEAR DI,NAME_TBL B10ENTER NAMELEN,00 A3 0 NAMECTR, 30 A3 0 C10STORE A2 0 Q10CLEAR NAMECTR,01 A90 D10SORT F10NAMES AX,4C00H 21H ;Длина имени /Таблица имен ;Строка экрана ;Список параметров имени: ; максимальная длина ; число введенных символов ; введенное имя •Инициализация регистра DS и ES •Очистка экрана ;Инициализация адреса таблицы ;Прием имени с клавиатуры ;Еще имена? ; нет, сортировать ;30 имен введено? ; да, сортировать Сохранение введенного имени в таблице /Повтор Конец ввода Очистка экрана Введено хотя бы одно имя? да, выход Сортировка сохраненных имен Вывод отсортированных имен Конец обработки Рис. 14.6. Сортировка таблицы имен Глава 14. Определение и обработка таблиц 365
/ BIOENTER B20: BIOENTER '* CIOSTORE C10STORE / D10SORT D20: D30: Ввод имени, дополнение пробелами: PROC LEA MOV CALL INC MOV LEA INT MOVZX MOVZX SUB MOV INC LOOP RET ENDP NEAR BP,MESSG1 CX,06 G10DISPLY ROW AH,0AH DX,NAMEPAR 21H BX,NAMELEN CX,MAXNLEN CX,BX NAMEFLD[BX], BX B2 0 9 20H Сохранение введенного PROC INC • CLD LEA MOV NEAR NAMECTR SI,NAMEFLD CX,LEN_NAME REP MOVSB RET ENDP Сортировка имен PROC SUB MOV MOV LEA MOV MOV ADD MOV MOV NEAR DI,40 ENDADDR,DI SWAPPED,00 SI,NAME_TBL CX,LEN_NAME DI,SI DI,LEN_NAME AX, DI BX, SI ;DI ;DI установлен при вызове ;Используются АН, ВР, BX, CX, DX ;Подсказка ;Число символов ;Вызов программы вывода ;Следующая строка ";Ввод имени ;Число символов ;Максимальная длина ;Число оставшихся символов ;Замена оставшихся символов ; пробелами имени в таблице: для таблицы установлен при вызове ;Используются АН, ВР, BX, CX ;Увеличить число имен ;Пересылка ; имени (SI) ; в таблицу {DI) в таблице по возрастанию: ;DI для таблицы установлен при вызове ;Используются АХ, BX, CX, DI, SI ;Установка конечного адреса для ; последнего имени в таблице ;Установка начала ; таблицы ;Длина сравниваемых строк ;Следующее сравниваемое имя ;Сохранение ; адресов Рис. 14.6 (Продолжение) 366 Часть IV. Обработка данных
D40: D10SORT '" E10XCHNG E10XCHNG r FlONAMES F20: FlONAMES REPE JBE CALL MOV CMP JBE CMP JNZ RET ENDP CMPSB D40 E10XCHNG SI, AX SI,ENDADDR D3 0 SWAPPED,00 D2 0 Сравнение имени со следующим нет обмена обмен Конец таблицы? нет, продолжим Были обмены? да, продолжим нет, конец сортировки <*» Обмен местами элементов таблицы: PROC MOV LEA MOV NEAR CX,LEN_NAME DI,NAMESAVE SI,BX REP MOVSB MOV MOV CX,LEN_NAME DI,BX REP MOVSB MOV LEA CX/LEN_NAME SI,NAMESAVE REP MOVSB MOV RET ENDP SWAPPED,01 Вывод сортированных име PROC MOV LEA MOV CALL INC ADD DEC JNZ RET ENDP NEAR ROW, 00 BP,NAME_TBL CX,LEN_NAME G10DISPLY ROW BP,LEN_NAME NAMECTR F20 •ВХ устанавливается при вызове •Используются CX, DI, SI ■Число символов •Сохранение меньшего элемента временно •Пересылка старшего элемента на место младшего •Пересылка сохраненного элемента ; на место старшего ?Установка признака обмена н из таблицы: ;Используются BP, CX ;Инициализация строки инициализация начала таблицы ;Число символов ;Вызов программы вывода ;Следующая строка /Следующее имя "в таблице ;Последнее имя? ; нет, повторить ; . да, завершить Рис. 14.6 (Продолжение) Глава 14. Определение и обработка таблиц 367
/ G10DISPLY G1QDISPLY / QIOCLEAR QIOCLEAR Общая процедура PROC MOV MOV MOV MOV INT RET ENDP NEAR - AX,1301H BX,0016H DH,ROW DL, 10 10H Очистка экрана, PROC MOV MOV MOV MOV INT RET ENDP END NEAR AX,0600H BH,61H CX,00 DX,184FH 10H A10MAIN вывода: ;BP, CX устанавливаются пи вызове /Используются АХ, ВХ, DX ;Запрос вывода ;Атрибуты ;Строка (; Столбец установка атрибутов: ;Используются АХ, ВН, CX, DX /Атрибуты ;Полный экран Рис. 14.6 (Окончание) Процедуры программы выполняют следующие действия: • AlOMAIJNf (главная процедура) вызывает все остальные процедуры; • B10ENTER позволяет пользователю ввести имя и дополняет введенное имя справа пробелами; чтобы завершить ввод, при очередном запросе на ввод необходимо нажать <Enter>; • C10STORE последовательно сохраняет вводимые имена в таблице; • D10SORT и E10XCHNG сортируют таблицу по алфавиту; • F10NAMES выводит на экран отсортированную таблицу. Заметьте, что все записи в таблице имеют фиксированную длину - 20 байтов. Процедура для сортировки строк различной длины значительно сложнее. Таблицы адресов В программе может возникнуть необходимость проверки ряда условий и вызова определенных процедур (в зависимости от результатов проверок). Рассмотрим, например, следующий фрагмент программы, проверяющий равенство содержимого элемента данных CODE одному из ряда значений: 368 Часть IV. Обработка данных
CODE DB ? ;Элемент данных для проверки CMP JE CMP JE CMP JE CODE,1 CODEl_RTNE CODE,2 CODE2_RTNE CODE,3 CODE3 RTNE ;Равно ;Равно ;Равно 1? 2? 3? При такой реализации очень легко допустить ошибку - достаточно просто ввести неверную цифру. Более элегантно данная задача решается определением таблицы адресов: DW DW DW DW DW CODEl_RTNE CODE2_RTNE CODE3_RTNE CODE4_RTNE CODE5_RTNE MOVZX BX,CODE DEC SHL ЛУ1Р BX BX, 1 [ADDRTBL+BX] Таблица адресов процедур Загружаем адрес таблицы в ВХ Уменьшаем его на 1 и удваиваем Используем таблицу для адресации В примере адрес загружается в ВХ, уменьшается на 1 и умножается на 2 - . так, что 0 остается 0, 1 преобразуется в 2, 2 - в 4 и т.д. Результат указывает на определенную ячейку таблицы: ADDRTBL+0 - содержащую первый адрес, ADDR_TBL+2 - содержащую второй, и т.д. В инструкции JMP [ADDRTBL+BX] используется косвенная адресация, предварительно суммируя адрес начала таблицы и величину в ВХ для получения адреса ячейки таблицы, в которой хранится адрес перехода. Инструкция JMP выполняет переход к требуемой процедуре. Вместо JMP в примере можно использовать инструкцию CALL - в этом случае таблица должна содержать имена процедур: CALL [ADDRTBL+BX] ;Используем таблицу для адресации Заметим, что попытка получить адрес из ячеек, расположенных за пределами таблицы, приведет к непредсказуемым результатам, поэтому разумно было бы организовать проверку вычисляемого адреса на корректность. Двумерные таблицы и массивы Двумерный массив состоит из х строк и у столбцов, как показано на следующем рисунке с тремя строками и пятью столбцами: Глава 14. Определение и обработка таблиц 24 —5-741 369
0 12 3 4 столбцы 0 строки 1 К ячейкам массива обращаются, указывая номер строки и номер столбца. Массив на рисунке содержит 3x5 = 15 ячеек (или элементов). В памяти каждую строку можно рассматривать как одномерный массив, причем строки располагаются в памяти последовательно одна за другой. Однако удобнее представлять себе массив в виде двумерной структуры и определять его соответствующим образом: DATA_ARRAY DW 3 DUP(5 DUP(?)) Обращение к элементу массива, например, [2, 3], т.е. к строке 2, столбцу 3, осуществляется следующим образом. 1. Умножаем номер строки на число элементов в строке: 2x5 = 10. 2. Прибавляем номер элемента в строке: 10 + 3 = 13. В этом случае требуемый элемент имеет номер 13 (при условии, что нумерация столбцов и строк начинается с 0). Заметьте, что элементы определялись как слова, но адресация ведется побайтно, поэтому значение необходимо удвоить (13-й элемент начинается с 26-го байта). Обратиться к элементу можно так: MOV ВХ,20 MOV DI,б ADD DATA_ARRAY[BX+DI],АХ Строка - начальный адрес Столбец - смещение Прибавляем к элементу массива значение из АХ Однако номер элемента в массиве определяется обычно в результате вычислений, а не объявляется прямо в тексте программы. Иногда проще рассматривать массив в виде одномерной структуры, как в следующем фрагменте программы, обнуляющем массив DATA_ARRAY: L10: MOV MOV MOV ADD LOOP CX,15 BX, 0 DATA_ARRAY[BX] , 0 BX,2 L10 ;Число элементов ;Смещение первого элемента ;Обнуляем элемент /Следующее слово в массиве ;Повторить 15 раз Табличная арифметика Этот параграф посвящен арифметическим действиям над элементами таблиц. Таблица в данном случае - это двумерный массив, состоящий из множества ячеек. Ячейки в крайнем справа столбце содержат сумму значений, содержащихся в ячейках, по строкам, а ячейки в нижней строке - суммы значений по столбцам. 370 Часть IV. Обработка данных
В программе, приведенной на рис. 14.7, определена таблица и выполняется суммирование значений по строкам и столбцам. В целях уменьшения объема программы таблица невелика и содержит предопределенные значения, а не вводимые с клавиатуры. Пятая строка и шестой столбец хранят суммы. TITLE • SPRD_TBL COUNT COLS ROWS NOCOLS NOROWS COLTOT i A10MAIN A10MAIN ' B10HORZL A14SPRED .MODEL .STACK .DATA DW DW DW DW DW DB EQU EQU EQU EQU EQU .386 .CODE PROC MOV MOV MOV CALL CALL MOV INT ENDP (EXE) Табличная арифметика SMALL 64 05 04 06 10 00 ? 12 10 06 05 03, Об 05, 01 02, 00 07, 03 00, 00 04, 09, 10, 05, 00, NOCOLS*(NOROWS- FAR AX DS ES ,©data ,AX ,AX B10HORZL C10VERTL AX ,4C00H 21H Вычисление горизонт PROC PUSHA MOV LEA LEA ADD NEAR COUNT,NOROWS DI,SPRD_ SI,SPRD_ SI,ROWS TBL TBL 10, 00 06, 00 11, 00 02, 00 00, 00 1)*2 ;Строки данных ;Строка сумм 6 столбцов х 2 байта 5 строк х 2 байта Число столбцов Число строк 6 столбцов х 4 строки х 2 Инициализация сегментных регистров Вызов Вызов Конец сложения по горизонтали сложения по вертикали обработки альных сумм (строки): -1 /Сохранение регистров ; Числ о строк /Инициализация табличного ; адреса Рис. 14.7. Табличная арифметика Глава 14. Определение и обработка таблиц 371 24*
BIO: B20: BIOHORZL ; ClOVERTL CIO: C20: ClOVERTL MOV MOV ADD ADD LOOP ADD ADD DEC JNZ POPA RET ENDP PROC PUSHA MOV MOV LEA LEA ADD MOV MOV ADD ADD LOOP ADD ADD - DEC JNZ POPA RET ENDP END CX,NOCOLS-l AX,[DI] [SI],AX DI, 02 B2 0 DI, 02 SI,COLS COUNT BIO ;Число столбцов ;Выбор содержимого ячейки ;Добавление его к сумме строки ;Следующая ячейка в строке ;Для всех ячеек строки ;Первая ячейка следующей строки ;Ячейка суммы следующей строки ;Повторить для всех строк ;Восстановить регистры Вычисление вертикальных сумм (столбцы): NEAR COUNT,NOCOLS-1 BX,00 ;Сохранение регистров ;Число столбцов SI, [SPRD_TBL+COLTOT] ;Ячейка суммы столбца DI,SPRD_TBL DI,BX CX,NOROWS-l AX,[DI] [SI],AX DI,COLS C20 BX;02 SI,02 COUNT CIO A10MAIN /Верхняя ячейка столбца ;Число строк ;Выбор содержимого ячейки ;Добавление к сумме ;Следующая ячейка в столбце ;Для всех ячеек в столбце ;Следующая ячейка в строке ;Следующая ячейка суммы ;Для всех столбцов /Восстановление регистров Рис. 14.7 (Окончание) Сначала программа суммирует значения по строкам: значение каждой ячейки строки прибавляется к значению в последней ячейке в строке. Эта операция выполняется для всех строк. Затем аналогично выполняется суммирование значений по столбцам, суммы сохраняются в ячейках нижней строки. Процедуры станут более понятными, если просмотреть листинг программы. 372 Часть IV. Обработка данных
Ключевые моменты • В большинстве случаев таблицы состоят из связанных элементов одинаковой длины, содержащих данные в одинаковых форматах. • Структура таблицы зависит от формата данных: ее элементы могут содержать символьные данные или числа, обычно элементы имеют одинаковую длину. • Если таблица должна часто изменяться или к ней должны иметь доступ различные программы, разумно выделить ее в отдельный файл на диске. Каждая программа может изменять содержимое таблицы на диске. При этом саму программу изменять не нужно. • При прямой адресации таблиц программа вычисляет адрес элемента таблицы и обращается к нему по вычисленному адресу. • При поиске элемента в таблице программа последовательно сравнивает значение каждого элемента таблицы с требуемым значением до тех пор, пока не будет достигнуто совпадение или не будет достигнут конец таблицы. Необходимо помнить, что инструкции СМР и CMPSW ожидают, что байты хранятся в памяти в переставленном порядке. • Инструкция XLAT может преобразовывать исходные байты в соответствующие им байты результата, используя таблицу в памяти. Вопросы и упражнения 1. Объясните разницу между прямой адресацией и поиском элемента в таблице. 2. Определите таблицу с именем ANNUAL_TBL из 365 слов, инициализировав их (а) пробелами для символьных данных, (б) нулями для чисел. 3. Определите три отдельных взаимосвязанных таблицы, содержащих следующие данные: (а) ASCII-числа 04, 07, 14, 17 и 24, (б) названия для DVD, приемников, модемов, клавиатур и дискет, (в) цены 22.20, 95.75, 47.45, 49.35 и 12.95. 4. Соедините данные из таблиц вопроса 3 в единую таблицу. Для первого предмета определите его номер и название в первой строке, а цену - во второй; для второго предмета - номер и название в третьей строке, а цену - в четвертой, и т.д. 5. Модифицируйте программу, приведенную на рис. 14.3, так, чтобы она воспринимала ввод номера месяца с клавиатуры в ASCII-формате. Если введен корректный номер (01—12), необходимо вывести название месяца, если , нет - вывести сообщение об ошибке. Необходимо реализовать возможность Глава 14. Определение и обработка таблиц 373
многократного ввода - сигналом для завершения работы должно служить нажатие клавиши <Enter> без ввода номера. 6. Напишите программу, позволяющую пользователю вводить номера товаров и их количества с клавиатуры. Используя таблицу, определенную в вопросе 4, реализуйте процедуру поиска, находящую в таблице товар с заданным номером. Получите из таблицы название и цену. Вычислите стоимость покупки (цена х количество) и выведите ее и название товара на экран. Завершение работы должно выполняться при нажатии <Enter> вместо ввода очередного номера товара. 7. Используя таблицу названий из вопроса 3, напишите программу, которая (а) переместит содержимое таблицы в другую (пустую) таблицу, (б) отсортирует содержимое новой таблицы по алфавиту, и (в) выведет каждое название из отсортированной таблицы в отдельной строке на экране. Организуйте возможность прокрутки экрана. 8. Фрагмент программы в параграфе "Инструкция XLAT (преобразовать) " преобразует ASCII-числа в формат EBCDIC. Модифицируйте программу для реализации обратного преобразования - из EBCDIC в ASCII-формат. Необходимо преобразовывать EBCDIC-символы знака минуса (60Н), десятичной точки (4ВН), цифр 0—9 (0F0H—0F9H). Все остальные символы можно заменять пробелами. Для проверки воспользуйтесь строкой EBCDIC- символов F0F0F1F24BF5F060 (определив каждый символ как OFOH, 0F1H и т.д.), преобразовав ее в формат ASCII и выведя на экран. Результат должен быть равен 303031322E35302DH. 9. Напишите программу для выполнения простого шифрования данных. Определите 80-байтную область данных, содержащую ASCII-символы, назвав ее CRYPTDATA. Объявите таблицу преобразований, заменяющую буквы на другие, например, А на М, X на R и т.д. Задействуйте все 256 символов. Создайте таблицу для проведения обратного преобразования. Программа должна выполнять следующие действия: (1) выводить исходное содержимое CRYPTDATA в строке; (2) шифровать содержимое CRYPTDATA и выводить зашифрованные символы во второй строке; (3) расшифровывать данные и выводить их в третьей строке (она должна быть идентична первой). Заметьте, что нельзя заменить два различных исходных символа одним и тем же символом - при этом зашифрованное сообщение невозможно будет расшифровать. 10. Модифицируйте программу, приведенную на рис. 14.7, так, чтобы она складывала ячейки строчных сумм в крайнем правом столбце и ячейки столбцовых сумм в нижней строке. 374 Часть IV. Обработка данных
Часть V Усовершенствованные операции ввода/вывода Глава 15. Работа с мышью Глава 16. Устройство и принципы работы дисковых накопителей Глава 17. Дисковые накопители: запись и чтение файлов Глава 18. Дисковые накопители: функции поддержки дисков и файлов прерывания INT 21Н Глава 19. Дисковые накопители: функции BIOS прерывания INT 21Н Глава 20. Устройства и методы печати
Глава 15 Работа с мышью Цель: описать методы работы с мышью. Введение В этой главе описывается использование мыши: ее инициализация, отображение/сокрытие указателя на экране, установка его в определенную позицию системы координат, реагирование на нажатия клавиш мыши. Описывается также прерывание INT 33H, функции которого обеспечивают поддержку мыши. Мышь - широко используемое устройство ввода, управляемое программным модулем, известным как драйвер мыши. Этот драйвер обычно запускается командой в файле CONFIG.SYS или AUTOEXEC.BAT. (Драйвер мыши загружался через эти файлы в среде MS-DOS. В среде Windows 95 и более поздних он обычно загружается согласно записи в реестре, хотя его по-прежнему можно загружать, используя файл CONFIG.SYS или AUTOEXEC.BAT. - Прим.ред.) Для того чтобы программы могли использовать мышь, предварительно необходимо загрузить драйвер. Ниже приведены несколько основных терминов, используемых в этой главе: • пиксель - наименьший адресуемый элемент экрана; например, в текстовом режиме ОЗН одному байту соответствуют восемь пикселей; • указатель мыши (иногда, курсор) - в текстовом режиме это мерцающий прямоугольник в инверсном видеорежиме, в графическом режиме его вид может быть произвольным, но обычно это стрелка; • микки - наименьшее расстояние, перемещение на которое мышь может зарегистрировать; обычно это около 1/400 дюйма; • счетчик микки - отсчитывает число микки, на которое шарик мыши сместился горизонтально или вертикально; драйвер мыши отсчитывает эти перемещения и соответственно перемещает указатель мыши на экране; • пороговая скорость - определенная скорость перемещения (микки/с), при превышении которой скорость перемещения курсора по экрану удваивается; по умолчанию эта скорость равна 64 микки в секунду. Все операции, связанные с мышью, в программах выполняются посредством функций прерывания INT 33H. Общий формат их вызова таков: 376 Часть \/.Усовершенствованные операции ввода/вывода
MOV AX, функция ;Запросить функцию ;Параметры функции (если они есть) INT 33H /Вызвать обработчик прерывания Заметьте, что в отличие от остальных прерываний, функции которых определяются значением в регистре АН, прерывание INT 33H использует весь регистр АХ. Первой используемой в программе функцией этого прерывания должна быть функция ООН. Эта функция просто инициализирует интерфейс мыши, делая его способным воспринимать дальнейшие команды. Обычно эта команда используется только один раз - в самом начале программы. После вызова ООН, скорее всего, следует вызвать 01Н - отображение указателя на экране. После этого можно использовать практически любые функции. В нижеследующем списке перечислены функции прерывания INT 33H, однако не следует пугаться, увидев перечень внушительных размеров - большая часть этих функций редко используется. ООН Инициализировать мышь 01Н Отобразить указатель мыши на экране 02Н Скрыть указатель мыши 03Н Прочитать состояние кнопок и положение указателя 04Н Установить указатель в заданную позицию 05Н Получить информацию о нажатии кнопок 06Н Получить информацию об отпускании кнопок 07Н Установить горизонтальные пределы для указателя 08Н Установить вертикальные пределы для указателя 09Н Установить тип графического указателя ОАН Установить тип текстового указателя ОВН Прочитать значения из счетчиков перемещения мыши ОСН ' Установить обработчик прерывания для событий мыши 0DH Включить эмуляцию светового пера ОЕН Выключить эмуляцию светового пера 0FH Установить соотношение микки/пиксель ЮН Установить область исключения указателя 13Н Установить пороговую скорость 14Н Поменять прерывание событий мыши Глава 15. Работа с мышью 377
15Н Получить размер буфера для состояния драйвера мыши 16Н Сохранить состояние драйвера мыши 17Н Восстановить состояние драйвера мыши 18Н Установить альтернативный обработчик событий мыши 19Н Получить адрес альтернативного обработчика 1АН Установить чувствительность мыши 1ВН Получить чувствительность мыши 1СН Установить частоту опроса прерывания мыши 1DH Выбрать страницу для отображения указателя 1ЕН Прочитать номер страницы для отображения указателя 1FH Отключить драйвер мыши 20Н Активизировать драйвер мыши 21Н Сбросить параметры драйвера мыши 22Н Установить язык сообщений драйвера мыши 23Н Получить номер языка 24Н Получить информацию о мыши Основные операции при работе с мышью В этом параграфе описываются основные функции прерывания INT 33H, необходимые в программах, работающих с мышью. Функция ООН: инициализация мыши Эта функция должна вызываться первой из всех команд обработчика. Ее необходимо вызвать только однажды. Загрузите в АХ значение ООН без каких- либо параметров и вызовите INT 33H. Операция возвращает следующие значения: • АХ = 0000Н - если нет поддержки мыши, или АХ = FFFFH, если она есть; • ВХ = количество кнопок у мыши, если мышь обнаружена. Если мышь поддерживается, операция инициализирует драйвер мыши следующим образом: • устанавливает указатель мыши в центр экрана; • скрывает курсор мыши, если он видим; J • выбирает страницу 0 для отображения указателя; 378 f Часть \/.Усовершенствованные операции ввода/вывода
• устанавливает указатель мыши соответственно режиму дисплея: прямоугольник в инверсном цвете - для текстового режима, или стрелка - для графического; • устанавливает соотношение микки/пиксел: 8/8 - по горизонтали, и 16/8 — по вертикали; • устанавливает горизонтальные и вертикальные пределы для указателя соответственно их максимальным и минимальным значениям; • устанавливает пороговую скорость 64 микки в секунду (позже вы сможете ее изменить). Функция 01Н: отображение указателя мыши Эта операция, используемая обычно после вызова ООН, вызывает отображение указателя на экране. Операция не требует задания параметров и не возвращает значений. Драйвер мыши содержит флаг указателя, определяющий', должен ли указатель отображаться на экране. Указатель отображается, если флаг установлен в 0, и скрывается, если флаг установлен в любое другое значение. Изначально его значение равно -1; функция 01Н увеличивает его значение до 0, и курсор становится видимым (см. также функцию 02Н). Функция 02Н: сокрытие указателя мыши Обычно эта функция вызывается в конце программы, чтобы скрыть указатель на экране. Операция не требует параметров и не возвращает значений. Значение флага указателя уменьшается этой операцией до -1, и указатель * перестает отображаться (см. также функцию 01Н). Функция ОЗН: получение состояния кнопок и текущей позиции указателя Эта функция не требует задания параметров и возвращает следующие значения: • регистр ВХ - состояние кнопок, по битам: бит 0 - левая кнопка (0 - отпущена, 1 - нажата); бит 1 - правая кнопка (0 - отпущена, 1 - нажата); бит 2 - средняя кнопка (0 - отпущена, 1 - нажата); биты 3—15 -зарезервированы для внутреннего использования; • регистр СХ - координата по горизонтали (х); • регистр DX - координата по вертикали (у). Глава 15. Работа с мышью 379
Горизонтальные и вертикальные координаты исчисляются в пикселях даже в текстовом режиме (8 пикселей на байт в текстовом режиме 03). Эти координаты всегда находятся в допустимых пределах. Функция 04Н: установка указателя в заданную позицию Эта операция устанавливает вертикальную и горизонтальную координату указателя согласно заданным параметрам (всегда - в пикселях, даже в текстовых режимах): MOV АХ,04Н ;Запросить установку позиции указателя MOV СХ, горизонталь ;Горизонтальная координата (х) MOV DX, вертикаль ;Вертикальная координата (у) INT 33H ;Вызов обработчика Операция устанавливает курсор в заданную позицию, корректируя ее, если она находится за допустимыми пределами. Программа: вывод позиции указателя мыши В программе, приведенной на рис. 15.1, продемонстрированы основные операции работы с мышью, рассмотренные ранее. Она выводит горизонтальную и вертикальную координаты указателя мыши в процессе ее перемещении без нажатий кнопок. TITLE LEN_DATA XCOORD YCOORD ASCVAL DISPDATA XMSG XASCII YMSG YASCIJ "*Q£ . J О О , A15MOUSE (EXE) .MODEL .STACK .DATA EQU DW DW DW LABEL DB DW DB ' DB DW .CODE SMALL 64 14 0 0 ? BYTE 'X = ? r •Y = • ? Обслуживание мыши ;Длина сообщения ;Двоичная X координата ;Двоичная Y координата ;ASCII поле ;Отображаемые поля: ; X сообщение ; X ASCII-значение ; Y сообщение Y ASCII-значение _ Рис. 15.1. Использование мыши 380 Часть \/.Усовершенствованные операции ввода/вывода
A10MAIN A20: « A80: A90: AlOMAIN / BIOINITZ B90: BIOINITZ ' ClOPOINTR | C20: PROC MOV MOV MOV CALL CALL CMP JE CALL CMP JE MOV CALL MOV MOV MOV CALL MOV MOV CALL JMP MOV INT CALL MOV INT ENDP FAR AX,©data DS,AX ES,AX QlOCLgAR BIOINITZ AX, 00 A90 ClOPOINTR BXf 01, A80 AX,XCOORD D10CONVRT AX,ASCVAL XASCII, AX AX,YCOORD D10CONVRT AX,ASCVAL YASCII,AX E10DISPLY A2 0 AX,02H 33H Q10CLEAR AX,4C00H 21H Инициализация PROC MOV INT CMP JE MOV INT RET ENDP NEAR AX,ООН 33H AX,00 B90 AX,01H 33H указателя Получение координат PROC MOV NEAR AX,03H укг ;Использу ;Получить Инициализация DS и ES регистров Очистка экрана Инициализация мыши Мышь установлена? нет, выход Получение указателя мыши Кнопка нажат? да, ^выход Преобразовать X в ASCII Преобразовать Y в ASCII Вывести значения X и Y •Повторить ■Запросить сокрытие указателя ■Очистка экрана ■Завершение работы i мыши: ;Используется АХ ;Запрос инициализации ; МЫШИ ;Мышь установлена? ; нет, выход ;Показать указатель ;Возврат 13ателя мыши: ется АХ, ВХ, СХ, DX позицию указателя Рис. 15.1 (Продолжение) Глава 15. Работа с мышью 381
СЗО: С90:, ClOPOINTR ; DIOCONVRT D20: DIOCONVRT ; E10DISPLY E10DISPLY ' QIOCLEAR INT CMP JE SHR SHR CMP JNE CMP JE MOV MOV RET ENDP ЗЗН BX,00000001В С90 СХ,03 DX, 03 CX,XCOORD СЗО DX,YCOORD; С20 XCOORD,СХ YCOORD,DX/ Нажата левая кнопка? да, выход Разделить пиксельную координату на 8 Положение указателя изменилось? нет, повторить операцию да, сохранить новое положение Возврат Преобразование двоичного Х или Y значения в ASCII: PROC MOV MOV LEA CMP JB DIV ' OR MOV DEC OR MOV RET ENDP ;АХ при вызове = двоичное X или Y NEAR ;Используется СХ, SI ASCVAL,2020H /Очистить ASCII' поле СХ,10 /Установить коэффициент деления "SI,ASCVAL+1 ;Загрузить ASCVAL адрес АХ,СХ /Сравнить позицию с 10 D2 0 CL меньше, пропустить больше, разделить на 10 АН, ЗОН /Добавить ASCII старший полубайт 1 [SI] ,АН /Сохранить в правом байте SI ;Уме'нынить адрес ASCVAL AL,30H /Добавить ASCII старший полубайт [SI] ,AL ,-Сохранить в левом байте ;Возврат Отображение положений PROC MOV MOV LEA MOV MOV INT RET ENDP NEAR АХ,1300Н ВХ,0031Н BP,DISPDATA CX,LEN_DATA DX,0020H ЮН X и Y: /Используются АХ, BX, BP, СХ, DX /Запрос вывода ;Страница:атрибуты /Адрес строки /Число символов /Экран строка:столбец /Возврат Очистка экрана, установка атрибутов: PROC MOV NEAR АХ,0600Н /Использовать АХ, ВН, СХ, DX ;Запрос очистки экрана Рис. 15.1 (Продолжение) 382 Часть \/.Усовершенствованные операции ввода/вывода
MOV MOV MOV INT Ъет Q10CLEAR ENDP END BH,30H CX,00 DX,184FH 10H A10MAIN Цвета Полный экран Возврат Рис. 15.1 (Окончание) Основные процедуры программы. • A10MAIN - главная процедура, вызывает процедуры B10INITZ, C10POINTR, D10CONVRT и E10DISPLY. При нажатии левой кнопки мыши вызывается функция обработчика 02Н, курсор перестает отображаться, и программа завершается. • B10IMTZ - вызывает функцию ООН прерывания INT 33H для инициализации драйвера мыши (или чтобы убедиться, что он не загружен). Затем вызывает функцию 01Н для отображения указателя на экране. • C10POINTR - вызывает функцию 03Н, чтобы проверить, не нажата ли левая кнопка мыши. Если она нажата, программа завершается, если нет, то программа преобразует горизонтальную и вертикальную координаты указателя в двоичные ^числа, сдвигая их на 3 разряда вправо. При этом координаты фактически делятся на 8, в результате чего получаются координаты не в пикселях, а в знакоместах (номера столбца и колонки в текстовом режиме). Если координаты те же, что и при предыдущем вызове функции, подпрограмма продолжает вызывать функцию 03Н; если координаты изменились, то управление передается вызывавшей процедуре. • D10CONVRT преобразует двоичные значения, полученные в процедуре C10POINTR, в пригодные к выводу на экран ASCII-числа. Заметьте, что одному символу соответствует 8 пикселей, поэтому значение, возвращаемое для символа 79 (на правом краю экрана) есть 79 х 8 = 632. Процедура делит это максимальное значение на 8, чтобы получить 79, т.е. максимальный номер символа в строке. Следовательно, преобразование позволяет удостовериться, что результат находится в допустимых пределах. • E10DISPLY выводит горизонтальные и вертикальные координаты в центре экрана в виде X = столбец и Y = строка. Эту программу можно улучшить, например, используя функцию ОСН (рассмотренную далее в этой главе) для установки обработчика событий. При этом требуемые инструкции будут вызываться по необходимости, когда происходят какие-либо события; связанные с мышью. Глава 15. Работа с мышью 383
Дополнительные операции работы с мышью В этом параграфе рассмотрены оставшиеся функции для работы с мышью, а в следующем - приведена программа, демонстрирующая их использование. Функция 05Н: получить информацию о нажатии кнопки мыши Эта функция возвращает информацию о нажатиях на кнопку мыши. Загрузите номер требуемой кнопки в ВХ: 0 - для левой, 1 - для правой, и 2 - для средней: MOV АХ,05Н /Запросить информацию о нажатии кнопок MOV ВХ,номер ;Номер требуемой кнопки INT 33H ;Вызов обработчика Операция возвращает состояние (нажаты/отпущены) всех кнопок, содержимое счетчика нажатий заданной кнопки, а также позицию указателя при последнем нажатии: • АХ = состояние кнопок, по битам: бит 0 - левая кнопка ((Ь- отпущена, 1 - нажата); бит 1 - правая кнопка (0 - отпущена, 1 - нажата); бит 2 - средняя кнопка (0 - отпущена, 1 - нажата); биты 3—15 - зарезервированы для внутреннего использования; • ВХ - счетчик нажатий выбранной кнопки мыши; • СХ - горизонтальная координата (х, в пикселях) указателя при последнем нажатии выбранной кнопки; • DX - вертикальная координата (у, в пикселях) указателя при последнем нажатии выбранной кнопки. При вызове операция сбрасывает счетчик (регистр ВХ) в 0. Функция 06Н: получить информацию об отпускании кнопки мыши Эта функция возвращает информацию об отпускании кнопки мыши. Загрузите номер требуемой кнопки в ВХ: 0 - для левой, 1 - для правой, и 2 - для средней: MOV АХ,06Н ;Запросись информацию об отпускании кнопок MOV ВХ,номер ;Номер требуемой кнопки INT 33H ;Вызов обработчика 384 Часть \/.Усовершенствованные операции ввода/вывода
Операция возвращает состояние (нажаты/отпущены) всех кнопок и содержимое счетчика отпусканий заданной кнопки, а также позицию указателя при последнем отпускании: • АХ - состояние кнопок, по битам: бит 0 - левая кнопка (0 - отпущена, 1 - нажата); бит 1 - правая кнопка (0 - отпущена, 1 - нажата); бит 2 - средняя кнопка (0 - отпущена, 1 - нажата); биты 3—15 - зарезервированы для внутреннего использования; • ВХ - счетчик отпусканий выбранной кнопки мыши; • СХ - горизонтальная координата (х, в пикселях) указателя при последнем отпускании выбранной кнопки; • DX - вертикальная координата (у, в пикселях) указателя при последнем отпускании выбранной кнопки. При вызове операция сбрасывает счетчик (регистр ВХ) в 0. Функция 07Н: установить пределы перемещения указателя по горизонтали Операция устанавливает граничные значения координат, в которые можно переместить указатель по горизонтали (в пикселях): MOV АХ,07Н /Запросить установку пределов перемещения ; указателя MOV CX,минимум /Минимальное значение по горизонтали MOV DX,максимум /Максимальное значение по горизонтали INT 33H /Вызов обработчика Если минимальное значение больше максимального, они автоматически меняются местами. Если курсор находится за пределами разрешенной области, он будет перемещен в нее. См. также функции 08Н и ЮН. Функция 08Н: установить пределы перемещения указателя по вертикали Операция устанавливает граничные значения координат, в которые можно переместить указатель по вертикали (в пикселях): MOV АХ,08Н ;Запросить установку пределов ; перемещения указателя MOV СХ,минимум ;Минимальное значение по вертикали MOV DX,максимум ;Максимальное значение по вертикали INT 33H ;Вызов обработчика Глава 15. Работа с мышью 385 25 —5-741
Если минимальное значение больше максимального, они автоматически меняются местами. Если курсор находится за пределами разрешенной области, он будет перемещен в нее. См. также функции 07Н и ЮН. Функция ОВН: прочитать содержимое счетчиков перемещения мыши Эта операция возвращает величины смещений мыши по вертикали и горизонтали (в микки) со времени последнего вызова функции. Эти значения могут находиться в пределах от -32768 до 32767. Функция не требует параметров и возвращает значения в следующей форме: • СХ - перемещение по горизонтали (положительное значение соответствует перемещению вправо, отрицательное - перемещению влево); • DX - перемещение по вертикали (положительное значение соответствует перемещению вниз, отрицательное - перемещению вверх). Функция ОСН: установка обработчика прерывания для событий мыши В программе может возникнуть необходимость немедленно' реагировать на события, связанные с мышью (например, перемещение или нажатие кнопки). Назначение функции ОСН - предоставить обработчик событий, который может прервать выполнение вашей программы, выполнить требующиеся от него в ответ на событие действия и вернуть управление программе. Загрузите в регистр СХ маску событий, чтобы определить, на какие события должен реагировать устанавливаемый обработчик, а в пару ES:DX - адрес в форме сегмент:смещение,'по которому располагается обработчик: MOV АХ,ОСН " ;Запросить создание обработчика прерываний LEA СХ,маска ;Маска событий LEA DX,обработчик ;Адрес обработчика (в паре ES:DX) INT 33H ;Вызов обработчика Маска строится следующим образом: установленные в 1 биты соответствуют событиям, на которые обработчик должен реагировать. Бит 0 - мышь перемещена Бит 4 - отпущена правая кнопка Бит 1 - нажата левая кнопка Бит 5 - нажата средняя кнопка Бит 2 - отпущена левая кнопка Бит 6 - отпущена средняя кнопка Бит 3 - нажата правая кнопка Биты 7—15 - зарезервированы (установите их в 0) 386 Часть \/.Усовершенствованные операции ввода/вывода
Определяйте обработчик прерывания как процедуру типа FAR. Драйвер мыши использует вызов типа FAR для передачи управления обработчику, загрузив в регистры следующие значения: • АХ - содержимое соответствует структуре маски, но биты устанавливаются в 1, только если соответствующее событие произошло; • ВХ - состояние кнопок (если в 1 установлен бит 0 - нажата левая кнопка, бит 1- нажата правая кнопка, бит 2 - средняя); • СХ - горизонтальная координата (х); • DX - вертикальная координата (у); • SI - последнее значение счетчика вертикального перемещения мыши; • DI - последнее значение счетчика горизонтального перемещения мыши; • DS - сегмент данных драйвера мыши. При передаче управления от программы обработчику сохраните в стеке все регистры и инициализируйте регистр DS адресом вашего сегмента данных. В обработчике используйте только прерывания BIOS, а не DOS. При выходе из обработчика извлеките сохраненные регистры из стека. Функция ЮН: установка области исключения указателя Эта функция определяет область экрана, находясь в которой, курсор не отображается на экране. Параметры этой функции задаются так: ;Запросить установку области исключения /Горизонтальная координата верхнего левого угла ;Вертикальная координата верхнего левого угла ;Горизонтальная координата нижнего правого угла ;Вертикальная координата нижнего правого угла /Вызвать обработчик прерывания Чтобы изменить размеры и положение области исключения, вновь вызовите эту функцию с другими параметрами или повторно вызовите функции ООН или 01Н. Функция 13Н: установка пороговой скорости Эта операция позволяет изменить пороговую скорость перемещения мыши, при превышении которой указатель на экране начинает двигаться вдвое быстрее. Загрузите новое значение скорости в регистр DX (значение по умолчанию равно 64 микки в секунду). См. также функцию 1 АН. Функция 1АН: установка чувствительности мыши Чувствительность мыши - это минимальное расстояние в микки, на которое нужно переместить мышь, чтобы указатель мыши на экране начал движение. Глава 15. Работа с мышью 387 25* MOV MOV MOV MOV MOV INT АХ, ЮН СХ,угол1_х DX, угол1_у SI, угол2_х DI, угол2_у ЗЗН
Эта функция позволяет изменять чувствительность мыши отдельно по вертикали и горизонтали (в микки на восемь пикселей экрана). Можно также установить пороговую скорость, при превышении которой скорость перемещения указателя по экрану удваивается (см. также функции OFH, 13H, и 1ВН): MOV АХ,1АН ;Запросить установку чувствительности мыши MOV ВХ,горизонталь /Горизонтальная чувствительность (8 по умолчанию) MOV СХ,вертикаль /Вертикальная чувствительность (16 по умолчанию) MOV DX, порог /Пороговая скорость перемещения (64 по умолчанию) INT 33H /Вызвать обработчик прерывания Функция 1ВН: получение чувствительности мыши Операция возвращает чувствительность мыши по вертикали и горизонтали и пороговую скорость в те же регистры, в которые функция 1АН загружает новые значения при их установке. Функция 1DH: выбор страницы для отображения указателя Страница отображения указателя, выводимого на экран, выбирается функцией 05Н прерывания ПЧТ ЮН. Для работы с мышью загрузите номер страницы в регистр ВХ и вызовите эту функцию. Функция 1ЕН: получение номера страницы отображения указателя Операция помещает номер страницы отображения указателя в регистр ВХ. Функция 24Н: получить информацию о мыши Операция возвращает информацию о версии и типе мыши, установленной в системе: ВН - основной номер версии; BL - вспомогательный номер версии; СН - тип мыши (1 - для параллельного порта, 2 - для последовательного). Программа: использование мыши для работы с меню Ранее (в гл. 10) для выбора пункта меню использовались клавиши управления курсором. Программа, приведенная на рис. 15.2, с теми же целями использует мышь. Кроме того, в меню добавлен пункт "Exit program" (Выход из программы). 388 Часть \/.Усовершенствованные операции ввода/вывода
TITLE TOPROW BOTROW LEFTCOL ' LEN_LINE ATTRIB COL ROW SHADOW MENU • PROMPT ЧЯ6 • A10MAIN A20: A15SELMU (EXE) .MODEL .STACK .DATA EQU EQU EQU EQU DB DB DB DB DB DB DB DB DB DB DB DB DB DB DB .CODE PROC MOV MOV MOV CALL CALL CMP JE CALL MOV MOV CALL CALL CMP JNE MOV INT MOV CALL SMALL 64 08 16 26 19 ? 00 00 Выбор 19 DUP(ODBH) OC9H, 17 OBAH, OBAH, OBAH, OBAH, OBAH, OBAH, OBAH, OC8H, ] 'To se~. ■buttor FAR AX,@dat DS,AX ES,AX QlOCLEi Bl0lNIr AX,00 A90 CI 0MENT ROW, TO] ATTRIB E10DIS1 D10POII DX, ВОТ] A2 0 AX,02H 33H AX,060 QlOCLEi L7 элемента меню DUP(OCDH), /Верхняя строка меню ;Нижняя строка меню ;Левый столбец меню ;Длина строки меню ;Атрибуты экрана ;Столбец экрана ;Строка экрана ;Теневые символы | ОВВН Add records Delete records Enter Print Updat View Exit orders report e accounts records program DUP(OCDH), Lect an item, овен , OBAH , ОВАН , ОВАН , ОВАН , ОВАН , ОВАН , ОВАН press left ' i of mouse pointer.' :a \R rz J PROW+1 Д6Н ?I/ STT] Ш DH \R r 4 Л7-1 инициализация сегментных ; регистров ;Очистка экрана /Инициализация мыши ;Мышь установлена? ; нет выход ;Вывод меню ;Строка на верхний элемент ;Инверсное отображение /Подсветка текущего элемента ;Вызов программы мыши ;Запрошен выход? ; нет ;Скрыть , продолжать указатель мыши ;Очистка ; экрана Рис. 15.2. Выбор из меню Глава 15. Работа с мышью 389
А90: A10MAIN BIOINITZ B90: BIOINITZ ' ClOMENU C20: MOV INT ENDP AX,4C00H 21H ;Конец работы Инициализация указателя мыши, установка горизонтальных и вертикальных пределов: PROC MOV INT СМР JE MOV INT MOV MOV MOV MOV MOV MOV SHL SHL INT MOV MOV MOV SHL INT SHL INT RET ENDP NEAR AX,ООН 33H AX, 00 B90 AX,01H ' 33H AX,04H CX,256 DX,108 AX,07H CX,LEFTCOL+l DX,LEFTCOL+17 CX,03 DX,03 33H AX,08H CX,TOPROW+l DX,BOTROW-l CX,03 33H DX,03 33H Используются AX, CX, DX Запросить инициализацию мыши Мышь установлена? нет, выход Показать указатель Установить указатель •Горизонтальные пределы левый столбец правый столбец •Поделить на 8 для получения значения в пикселях •Вертикальные пределы верхняя строка нижняя строка •Поделить на 8 Отобразить теневую рамку и полное меню: PROC MOV MOV LEA MOV MOV MOV INT INC CMP CTNE NEAR AX,1301H BX,0060H BP,SHADOW CX,LEN_LINE DH,TOPROW+l DL,LEFTCOL+l 10H DH DH,BOTROW+2 C20 ;Используются АХ, BP, BX, CX, ;Запрос вывода ;Черный на коричневом ;Адрес тени ;Длина строки ;Строка экрана ;Столбец экрана /Следующая строка ;Все строки выведены? ; нет, повторить DX Рис. 15.2 (Продолжение) 390 Часть \/.Усовершенствованные операции ввода/вывода
СЗО: ClOMENU MOV MOV MOV MOV LEA MOV MOV MOV INT ADD INC CMP CTNE MOV MOV MOV LEA MOV MOV MOV INT RET ENDP ATTRIB,7lH АХД300Н BH,00 BL,ATTRIB BP,MENU CX,LEN_LINE DH,TOPROW DL,LEFTCOL 10H BP,LEN_LINE DH DH,BOTROW+l C30 AX,1300H BH,00 BL,ATTRIB BP,PROMPT CX,45 DH,BOTROW+4 DL,15 10H ;Синий на белом ;Запрос вывода ;Страница 0 ;Атрибуты ;Адрес меню ;Длина строки ;Строка экрана, ; и столбец ;Следующая строка меню ;Следующая строка ;Все строки выведены? ; нет, повторить ;Запрос вывода ;Страница 0 ;Атрибуты ;Строка подсказки ;Длина подсказки ;Строка экрана ; и столбец ; Если левая кнопка нажата, установить для старой строки меню ; нормальное отображение, для DIOPOINTR D20:' f D90: DIOPOINTR PROC MOV INT CMP CTNE SHR CMP JE PUSH MOV CALL POP MOV MOV CALL JMP RET ENDP NEAR AX,03H 33H BX,00000001B D20 DX,03 DX,BOTROW-l D90 DX ATTRIB,71H E10DISPLY DX ROW,DL ATTRIB,17H E10DISPLY" D2 0 [ новой строки - инверсное: Используются АХ, ВХ, DX Получить состояние кнопок Левая кнопка нажата? нет, повторить Поделить верт. на 8 Запрос выхода? да, выход нет, сохранить строку Синий на белом Отобразить старую строку нормально Получить строку Белый на синем Новая строка - инверсно Повтор Рис. 15.2 (Продолжение) Глава 15. Работа с мышью 391
/ E10DISPLY • ElODISPLY / QIOCLEAR QIOCLEAR Установка строки меню PROC MOVZX SUB IMUL LEA ADD MOV MOV MOV MOV MOV MOV MOV INT RET ENDP NEAR AX, ROW AX,TOPROW AX,LEN_LINE SI,MENU+1 SI,AX АХД300Н BH,00 BL,ATTRIB BP,SI CX,LEN_LINE-2 DH,ROW DL,LEFTCOL+l 10H нормальной или выделенной: /Используются АХ, ВХ, ВР, СХ, DX ;Строка меню ;Умножить на длину строки ; для выбора строки меню ;Запрос вывода ;Страница ;Новые атрибуты ;Строка меню ;Длина строки ;Строка ;Столбец , Очистка экрана, установка атрибутов: PROC MOV MOV % MOV MOV INT RET ENDP END NEAR AX,0600H BH,61H CX,0000 DX,184FH 10H A10MAIN /Используются АХ, ВН, СХ, DX ;Запрос прокрутки ;Синий на коричневом ;Полный экран Рис. 15.2 (Окончание) Программа содержит следующие процедуры: • A10MAIN - главная процедура; вызывает: B10INITZ - для инициализации мыши, C10MENU - для вывода меню, ElODISPLY - для подсветки выбранной строки меню, D10POINTR-для реакции ца события мыши, и завершает работу при выборе пункта меню "Exit Program"; • B10INITZ - инициализирует мышь, отображает указатель и устанавливает горизонтальные и вертикальные пределы перемещения мыши; • С10MENU - выводит на экран меню; • D10POINTR - проверяет нажатие левой кнопки мыши; если кнопка нажата, вызывается процедура ElODISPLY, устанавливающая в обычный режим отображения ранее выбранную строку и в инверсный - выбранную последней; • El 0DISPLY - выводит строки меню с заданными атрибутами. 392 Часть \/.Усовершенствованные операции ввода/вывода
Ключевые моменты • В текстовом режиме указатель мыши представляет собой мерцающий прямоугольник, в графическом - обычно стрелку. • Операции с мышью задаются кодом в регистре АХ и выполняются обработчиком прерывания INT 33H. • Перед любой другой функцией работы с мышью необходимо вызвать функцию ООН прерывания ПЧТ ЗЗН, чтобы инициализировать драйвер мыши. • Дня отображения курсора мыши необходимо вызвать функцию 01Н прерывания INT ЗЗН, для получения информации о состоянии кнопок- функцию 03Н, для получения позиции указателя - функцию 04Н, для регистрации нажатия кнопок - 05Н, и 06Н - для регистрации отпускания кнопок. • Горизонтальные и вертикальные координаты указателя мыши измеряются в пикселях. Вопросы и упражнения 1. Объясните термины (а) микки, (б) счетчик перемещения, (в) указатель мыши. 2. Укажите функции прерывания INT ЗЗН, выполняющие следующие действия: (а) сокрытие указателя мыши; (б) получение информации о нажатии кнопок; (в) установку позиции курсора; (г) установку обработчика прерываний для событий мыши; (д) получение информации об отпускании кнопок; (е) чтение значений счетчиков перемещения. 3. Объясните назначение флага указателя мыши. 4. Напишите инструкции, решающие следующие задачи: (а) инициализирующие мышь; (б) отображающие указатель на экране; (в) получающие информацию о мыши; (г) устанавливающие указатель мыши в строку 22 в центральный столбец; (д) получающие информацию о чувствительности мыши; (е) получающие информацию о нажатых кнопках и позиции указателя; (ж) скрывающие указатель мыши. Глава 15. Работа с мышью 393
5. Объедините инструкции предыдущего вопроса в единую программу. Попробуйте протестировать ее с помощью DEBUG (хотя иногда DEBUG может перемещать указатель за пределы экрана). 6. Напишите инструкции, устанавливающие область исключения со следующими координатами: верхний левый угол: х = 40, у = 40; нижний правый угол: х= 160, у = 80. 394 Часть \/.Усовершенствованные операции ввода/вывода
Глава 16 Устройство и принципы работы дисковых накопителей Цель: объяснить основные форматы жестких дисков и дискет, а также структуру файловых систем, загрузчиков, директорий и таблиц размещения файлов. Введение Профессиональный программист должен быть знаком с техническими деталями организации дискового пространства, особенно если он разрабатывает прикладные программы, оперирующие с содержимым дисков, дискет и CD-ROM. В этой главе разъясняется, что такое дорожки, сектора и цилиндры, а также приводятся сведения о емкости некоторых широко распространенных устройств. Рассматривается также организация важных данных, записываемых в начале диска, включая загрузчик (выполняющий загрузку операционной системы с диска в память), корневую директорию и таблицу размещения файлов (file allocation table или FAT), содержащую информацию о расположении, именах и состоянии каждого файла на диске. Под термином диск в этой главе могут пониматься как жесткий диск (винчестер), так и дискета. Характеристики дисковых накопителей Чтобы работать с данными на дисках, вы должны знать термины и характеристики, используемые для описания этих устройств. Дискета хранит данные на двух сторонах единственного диска, в то время как винчестер может содержать несколько дисков на единой оси, и на каждой из сторон любого диска может храниться информация. Дорожки и сектора Каждая сторона дискеты или жесткого диска содержит множество концентрических дорожек (tracks), нумерующихся, начиная от края диска, где расположена дорожка 00. Каждая дорожка разбита на еектора (sectors) размером по 512 байтов каждый. В этих секторах и хранится информация. Глава 16. Устройство и принципы работы дисковых накопителей 395
Процесс чтения данных с дискет и жестких дисков и записи на них организуется контроллером (controller), управляющим перемещением головок записи/считывания по поверхности диска и передачей данных из/в память компьютера. Одной поверхности диска соответствует одна головка записи/считывания. При операциях записи или чтения головка перемещается к указанной контроллером дорожке, после чего ждет, пока при очередном обороте диска под ней окажется нужный сектор. При чтении данных головка считывает данные бит за битом, пока сектор проходит под ней. На рис. 16.1 показан этот процесс. Сектора Головка Чтения/записи Сектора Поверхность Диска Рис. 16.1. Поверхность диска и головка чтения/записи Между дискетой и жёстким диском есть два существенных отличия. Во- первых, при записи/чтении данных на/с дискеты головка соприкасается с ее поверхностью, в то время как головки жесткого диска всегда находятся на определенном (очень небольшом) расстоянии от поверхности. Во-вторых, дискеты начинают вращаться в дисководе только тогда, когда к ним есть обращение от системы, а жесткие диски вращаются непрерывно. Цилиндры Цилиндр (cylinder) - это совокупность дорожек с одинаковым номером на всех поверхностях всех дисков винчертера или обеих сторон дискеты. Например, цилиндр 0 - это дорожки номер 0 на обеих сторонах дискеты или на всех поверхностях дисков винчестера. Головки накопителей, осуществляющие чтение/запись, имеют те же номера, что и поверхности, к которым они обращаются. Например, в дисководе головка 1 396 Часть V. Усовершенствованные операции ввода/вывода
обращается к стороне (поверхности) 1, а в винчестере головка 5 обращается к поверхности 5. При записи файла контроллер заполняет данными сектора одного цилиндра, после чего переводит головки для продолжения записи к следующему. Например, при записи на дискету сначала заполняются все сектора в цилиндре 1, стороне О, затем - в цилиндре 1, стороне 1, затем - в цилиндре 2, стороне 0 и т.д. Как видно из сказанного выше, к поверхностям (сторонам) диска, головкам, цилиндрам, дорожкам и секторам обращаются по их номерам. Номера поверхностей и дорожек начинаются с 0, но сектора могут нумероваться одним из двух способов. 1. По физическим секторам (physical sectors), где номера секторов каждой дорожки начинаются с 1. При этом к первому сектору на диске обращаются как "цилиндр 0, головка/сторона 0, сектор 1", к следующему за ним - "цилиндр 0, головка/сторона 0, сектор 2" и т.д. 2. По относительным секторам (relative sectors), где нумерация секторов начинается с 0 и является сквозной, т.е. первый сектор на диске, расположенный на цилиндре 9, дорожке 0 - относительный сектор 0, второй - относительный сектор 1, и так до последнего сектора на диске. Различные дисковые операции могут использовать тот или иной метод, в зависимости от того, как происходит обращение к диску. Контроллер диска Контроллер диска (disk controller) - это устройство, управляющее работой диска и выполняющее указания процессора. Контроллер воспринимает указания и данные от процессора и преобразует их в форму, пригодную для восприятия непосредственно диском. Например, процессор может запросить данные из определенного цилиндра, поверхности и сектора. Контроллер должен выполнить действия, в результате которых данные будут прочитаны и переданы процессору, т.е. переместить указанную головку к требуемой дорожке, прочитать данные из определенного сектора и передать их процессору в заданном формате. Пока контроллер выполняет эту задачу, процессор может ожидать данные или выполнять другие задачи. При таком подходе контроллер может обрабатывать данные только побитно, что существенно замедляет работу. Однако контроллер может выполнять дисковые операции куда быстрее, если будет передавать данные непосредственно в память или принимая их из памяти. Этот метод передачи больших блоков данных известен как прямой доступ к памяти (direct memory access или DMA). При использовании этого метода процессор только передает контроллеру команду записи или чтения, адрес буфера ввода/вывода, количество секторов для считывания/записи, и номера цилиндра, головки и Глава 16. Устройство и принципы работы дисковых накопителей 397
начального сектора. При этом процессор должен ожидать окончания операции ввода/вывода, поскольку шина будет занята - только один компонент может обращаться к памяти в любой момент времени. Скорость передачи данных - скорость, с которой накопитель поставляет данные компьютеру или воспринимает их, - определяют два фактора: время доступа и скорость вращения. (Скорость работы винчестера определяется и другими параметрами - объемом буфера, интерфейсом, протоколами передачи данных и т.д. - Прим.ред.) 1. Время Доступа (или поиска) связано с необходимостью перемещать головки к требуемой дорожке. При расположении данных в последовательных секторах одной дорожки скорость доступа к данным выше - головкам не нужно передвигаться. Если же данные разбросаны по секторам разных дорожек, головки должны часто перемещаться, и время доступа возрастает. 2. Скорость вращения (или частота) определяет время, за которое требуемый сектор за счет вращения диска достигнет головки. Среднее время, естественно, равно половине длительности одного оборота диска и называется временем ожидания (latency). При скорости вращения 7200 оборотов в минуту, время одного оборота равно 1/120 секунды или В'/змиллисекунды. Поэтому время ожидания равно приблизительно 4Пмиллисекунды. Кластеры Кластер (cluster) - это группа последовательно расположенных секторов, которую система рассматривает как единицу дискового пространства. Число секторов в кластере - это всегда степень двойки (1, 2, 4, 8 и т.д.). На диске, где каждый кластер содержит единственный сектор, число кластеров равно числу секторов. Если на диске кластер соответствует двум секторам, то это выглядит так: сектор 1 класч сектор 2 сер 1 сектор 3 класч сектор 4 сер 2 сектор 5 класч сектор 6 гер 3 Диск с четырьмя секторами на кластер будет иметь такую структуру: сектор 1 сектор 2 класп сектор 3 сер 1 сектор 4 сектор 5 сектор 6 класч сектор 7 сер 2 сектор 8 Жесткий диск может делиться на несколько разделов или логических дисков. Каждый раздел обозначается буквой латинского алфавита, начиная с С. Два типа файловых систем, применяющихся на винчестерах, FAT 16 и FAT32, определяют число секторов в кластере в зависимости от размера раздела. 398 Часть V. Усовершенствованные операции ввода/вывода
FAT16 Размер Число Размер раздела секторов кластера в кластере Менее 128 МБ 4 2 КБ 128-255 МБ 8 4 КБ 256-511 МБ 16 8 КБ 512-1023 МБ 32 16 КБ 1024-2047 МБ 64 32 КБ FAT32 Размер Число Размер раздела секторов кластера в кластере 260 МБ-8 ГБ 8 4 КБ 8-16 ГБ 16 8 КБ 16-32 ГБ 32 16 КБ Более 32 ГБ 64 32 КБ Файл на диске всегда начинается с границы кластера и независимо от своего размера должен занимать хотя бы один кластер, даже если файл целиком помещается в одном секторе. 400-байтовый файл (помещающийся в одном секторе) при хранении на диске с 64 секторами на кластер займет один кластер (32768 байт). Остающееся незанятым место в кластере для использования недоступно. Каждый файл сохраняется на диске в наборе последовательных (хотя и не обязательно непрерывно следующих друг за другом) кластеров, например, в кластерах 8, 9, 10, 14, 17 и 18. Кластер может содержать в себе сектора, расположенные на разных дорожках. Емкость дискет Ниже приведены емкости дискет размера 3,5м. Емкость 720 КБ 1,44 МБ Дорожек на каждой из сторон (цилиндров) 80 80 Секторов на дорожке 9 18 Байтов на сектор 512 512 Общая емкость * 737280 1457664 Секторов на кластер 2 ' 1 Емкости жестких дисков непрерывно растут и значительно превосходят емкости дискет. Для определения параметров жесткого диска (числа цилиндров, секторов на дорожку и головок) можно использовать, например, функцию 1FH прерывания INT 21H или функцию 440DH с кодом подфункции 60Н того же прерывания. Эти функции рассмотрены в гл. 18. Системная область диска и область данных Определенные области диска предназначены системой для хранения информации о файлах на диске. Структура системной области зависит от емкости дискет и жестких дисков. Жесткие диски и, в некоторых случаях, дискеты Глава 16. Устройство и принципы работы дисковых накопителей 399
форматируются таким образом, чтобы с них можно было загрузить операционную систему при включении или перезагрузке компьютера. Вообще, системная область диска располагается в первых его секторах, а за ней Следует область данных, занимающая все оставшееся пространство. Системная область Системная область (system area) - это область в начале диска, на внешних дорожках, начиная со стороны 0, дорожки 0, сектора 1. Информация, хранимая системой в системной области, используется, например, для определения начального кластера каждого файла на диске. Системная область содержит три основных компонента: (1) загрузочную запись (boot record), (2) таблицу размещения файлов (FAT) и (3) структуру директорий. Системная область и область данных на диске размещены в таком порядке: Загрузочная запись < Систе] FAT мная об Директории ласть > Системные файлы Файлы пользователей ... < Область данных > В приведенной ниже таблице отображена структура системной области 3,5-дюймовых дискет, показывающая начальные и конечные сектора загрузочной записи, FAT и директорий. Сектора обозначаются относительными номерами, начинающимися с сектора 0, т.е. первого сектора в цилиндре 0 на стороне О (как объяснялось ранее в параграфе "Цилиндры "). Тип дискеты 720 Кбайт 1,44 Мбайт 3 агруз очная запись 0 0 FAT 1-6 1-18 Директории 7-13 19-32 Секторов на кластер 2 1 На жестком диске сектора загрузочной записи и начала FAT обычно те же самые, но размеры FAT и структуры директорий могут различаться. Отформатированная дискета содержит следующую информацию (по физическим и относительным секторам): Объект Загрузочная запись FAT1 FAT2 Структура директорий Область данных 720 Кбайт (9 секторов Цилиндр Сторона 0 0 0 0 0 0 0 0 0 1 на дорожку) Сек- Относи- тор тельный сектор 1 0 2 1 4 3 8 7 6 14 1,44 Мбайт (18 секторов Цилиндр Сторона 0 0 0 0 0 0 0 1 0 1 на дорожку) Сек- Относи- тор тельный сектор 1 0 2 1 11 10 2 19 16 33 400 Часть V. Усовершенствованные операции ввода/вывода
Область данных на дискете 720К начинается с цилиндра 0, стороны 1, сектора 6. Далее система сохраняет записи на цилиндре 1, стороне 0, затем - на цилиндре 1, стороне 1, затем - на цилиндре 2, стороне 0, и т.д. Заполнение данными дорожек одного цилиндра перед переходом к следующему позволяет уменьшить потребность в перемещении головок и используется как для дискет, так и для жестких дисков. Область данных Область данных (data area) следует за системной областью и на дисках, отформатированных как загрузочные, начинается с файлов ядра системы - для DOS это файлы IO.SYS и MSDOS.SYS. При использовании DOS-команды FORMAT с ключом /s системные файлы будут перенесены на дискету после форматирования. Пользовательские файлы будут следовать за системными, или, если системные файлы отсутствуют, пользовательские файлы начнутся сразу за системной областью. В следующих разделах рассматриваются загрузочная запись, структура директорий и FAT. Загрузочная запись Загрузочная запись (boot record) содержит команды, загружающие системные файлы (если они есть) с Диска в оперативную память. Загрузочная запись есть на любом отформатированном диске, даже если на нем и нет системных файлов. Она содержит следующую информацию (поля перечислены в порядке возрастания адреса): • ООН - Инструкция перехода к загрузчику - по адресу ЗЕН в этом же секторе; • ОЗН - Имя или номер версии DOS, которой была создана загрузочная запись; • ОВН - количество байтов в секторе (обычно 200Н, т.е. 512); • 0DH - количество секторов в кластере (1, 2, 4 или 8); • ОЕН - количество зарезервированных секторов; • ЮН - количество экземпляров FAT (1 или 2); • ПН - число записей в корневой директории; • 1ЗН - число секторов, если размер тома меньше 32 Мбайт; • 15Н - байт описания носителя (такой же, как и первый байт FAT, описанной далее); • 16Н - количество секторов для FAT; Глава 16. Устройство и принципы работы дисковых накопителей 401 26 —5-741
18Н - количество секторов на дорожке; 1 АН - количество головок (и, соответственно, поверхностей/сторон); 1СН - количество скрытых секторов; 1ЕН - зарезервировано системой; 20Н - общее число секторов, если размер тома больше 32 Мбайт; 24Н - номер физического диска (для дискеты А = 0; для жесткого диска, например, 80Н соответствует диску С); 25Н - зарезервировано системой; 26Н - метка дополнительного загрузочного сектора (содержит 29Н); 27Н - серийный номер (ID) тома; 2ВН - метка тома; 36Н - зарезервировано системой; ЗЕН—1FFH - код загрузчика. Структура директорий Каждый файл на диске начинается с границы кластера, то есть с первого сектора кластера. Для каждого файла система создает 32-байтовую запись, сохраняющую информацию о файле - его имя, дату создания, размер и положение начального кластера. Эти записи имеют следующую структуру: Байты Назначение ООН—07Н Имя файла (file name), определяемое программой, создающей файл. Первый байт может также обозначать состояние файла: ООН Файл никогда не использовался 05Н Первый символ имени файла в действительности есть 0Е5Н 2ЕН Файл является поддиректорией Е5Н Файл был удален 08Н—ОАН Расширение файла (file extension), например, .EXE или .ASM ОВН Атрибуты файла (file attributes), определяющие тип файла (заметьте: файл может одновременно иметь несколько атрибутов): ООН Обычный файл 01Н Файл нельзя изменять или удалять (атрибут read-only - только для чтения) 02Н Скрытый файл, не отображается в ходе поиска 402 Часть V. Усовершенствованные операции ввода/вывода
04Н Системный файл, не отображается в ходе поиска 08Н Метка тома (если это запись метки тома, то сама метка хранится в полях имени и расширения файла) ЮН Поддиректория 20Н Архивный файл (указывает, что файл перезаписывался со времени последнего обновления) (Например, код 07Н (00000111В) означает: только для чтения (01Н), скрытый (02Н), системный файл (04Н)) 0CH—15Н Зарезервированы системой 16Н—17Н Время суток, в которое файл был создан или обновлен в последний раз; хранится в 16 битах в двоичной форме в виде hhhhhmmmmmmsssss, где h - биты часов, m - биты минут, s - биты секунд 18Н—19Н Дата создания файла или его последнего обновления; хранится в 16 битах в двоичной форме в виде yyyyyyymmmmddddd, где у - биты года, m - биты месяца, s - биты дня. Год может изменяться в пределах от 0 до 119 (году 0 соответствует 1980-й год). Месяц может быть от 1 до 12, день - от 1 до 31. 1 АН—1ВН Первый кластер файла. Номер указывается относительно двух последних секторов директории. Если системных файлов на диске нет, первый пользовательский файл начинается с кластера 002. Действительные сторона, дорожка и кластер зависят от типа диска. Нуль в записи означает, что для данного файла место на диске не выделено. 1бН—1FH Размер файла в байтах. При записи файла система вычисляет его размер и записывает в эти байты. Если размер поля больше одного байта, данные хранятся в поле в последовательности с переставленными в обратном порядке байтами. Таблица размещения файлов Таблица размещения файлов (FAT - file allocation table) определяет расположение файлов на диске. FAT содержит сведения о каждом кластере на диске. Когда программа создает новый файл или модифицирует уже существующий, система соответственно изменяет записи в FAT о кластерах, в которых расположен этот файл. На диске FAT размещается, начиная с сектора 2, сразу после загрузочной записи. Если на диске один кластер содержит 4 сектора, то FAT Глава 16. Устройство и принципы работы дисковых накопителей 403 26*
некоторого определенного размера может адресовать вчетверо больший объем, чем на диске, где кластер состоит из единственного сектора. Поэтому использование кластеров с множеством секторов в каждом уменьшает количество записей в FAT и позволяет адресовать диски большего объема. Изначально разработчики предусмотрели создание двух экземпляров FAT (FAT1 и FAT2), исходя из того, что в случае потери FAT1 его можно будет восстановить, используя FAT2. Однако, хотя FAT2 все еще поддерживается, он практически никогда не используется. Ранее, в параграфе "Системная область диска и область данных " в таблице были учтены оба экземпляра FAT. В дальнейшем в книге рассматривается только FAT1. Первая запись в FAT Первый байт FAT - байт описания носителя - указывает на тип устройства (его содержание такое же, как и байта 15Н в загрузочном секторе). Он может содержать следующие значения: F0H - дискета 3,5м, двухсторонняя, 18 секторов на дорожку (1,44 Мбайт) или 36 секторов на дорожку (2,88 Мбайт); F8H - жесткий диск или виртуальный диск в оперативной памяти; F9H - дискета 3,5", двухсторонняя, 9 секторов на дорожку (объем 720 Кбайт) или дискета 5,25м, двухсторонняя, 15 секторов на дорожку (1,2 Мбайт). Вторая запись в FAT Вторая запись FAT содержит: FFFFH - для дискет с 12-разрядным FAT (FAT12), и FFFFFFH - для жестких дисков, использующих 16-битовую FAT (FAT16). Таким образом, первые две записи FAT имеют такую структуру: Дискета на 1,44 Мбайта Жесткий диск Как уже говорилось, первый сектор на диске занят загрузочной записью, за ним идет FAT, а далее -структура директорий. За ней следует область данных. В целом это выглядит так: F0 F8 ' FF FF FF FF ■FF кластер 0 кластер 1 кластер 2 кластер 3 кластер п <--Системная область--> -Область данных- Можно было бы ожидать, что нумерация кластеров начинается с первого кластера области данных. Однако первые два кластера (0 и 1) принадлежат системной области, а область данных начинается с кластера номер 2. 404 Часть V. Усовершенствованные операции ввода/вывода
Записи указателей в FAT За первыми двумя записями следует множество записей-указателей (pointer entries), указывающих на принадлежность определенных кластеров определенным файлам. Структура директорий (в 1 АН—1ВН) содержит список первых кластеров всех файлов на диске, a FAT указывает, в какой последовательности кластеров хранится каждый файл. Длина записи-указателя для дискет равна 12 битам (три шестнадцатеричных цифры), а для винчестеров с FAT16 - 16 битам (четырем шестнадцатеричным цифрам). Современные операционные системы поддерживают также 32-разрядную FAT, т.е. с длиной записей в 32 бита. Каждая запись-указатель в FAT описывает состояние соответствующего кластера, согласно следующей таблице: FAT12 000 Ш1П FF0-FF6 FF7 FFF FAT16 0000 ПШ1П FFF0-FFF6 FFF7 FFFF Описание Данный кластер сейчас не используется Соответствующий номер следующего кластера для файла, которому принадлежит этот кластер Зарезервированный кластер Неиспользуемый (поврежденный) кластер Последний кластер файла Первые две записи-указателя для дискеты на 1,44 Мбайта (с 12-битовой FAT) содержат, соответственно, F0F и FFF, для кластеров 0 и 1: F0F FFF nnn Соответствующий кластер 0 1 2 ... конец F0 в начале указывает на то, что данный носитель - двухсторонняя дискета с восемнадцатью секторами на дорожку (объемом 1,44 Мбайт). Первая запись заканчивается байтами FFFFH. Термин "соответствующий кластер" означает, например, что записи-указателю 3 в FAT соответствует кластер 2, записи- указателю 4 - кластер 3, и т.д. Вообще говоря, первые две записи в FAT (соответствующие кластерам 0 и 1) указывают на системную область, с которой начинается нумерация кластеров. Структура директорий содержит номера стартовых кластеров всех файлов на диске, а цепочки записей в FAT определяют последовательности кластеров, составляющих файлы, и свободное пространство на диске. Запись-указатель, содержащая значение FFFH или FFFFH (соответственно, для дискеты и жесткого диска), означает, что данный кластер содержит конец файла. Глава 16. Устройство и принципы работы дисковых накопителей ' 405
Пример записей в FAT Приведенные ниже примеры должны продемонстрировать устройство FAT. Предположим, что дискета содержит единственный файл - TEMPSTAT.FIL, полностью помещающийся в кластерах 2, 3 и 4. Запись об этом файле в структуре директорий содержит: (1) имя, TEMPSTAT, (2) расширение, FIL, (3) ООН для файла без атрибутов, (4) дату и время создания файла, (5) 0002Н - для указания на начальный кластер этого файла - 2, и (6) размер файла в байтах. Таким образом, записи в FAT12 должны выглядеть так (за исключением того, что байты должны быть переставлены): F0F FFF 003 004 FFF Соответствующий 0 1 2 3 4 5 б ... конец кластер Если программе нужно загрузить файл TEMPSTAT.FIL с диска в оперативную память, система выполняет следующие операции. • Просматривает структуру директорий в поисках номера первого кластера файла с именем TEMPSTAT и расширением FIL, и считывает данные из секторов этого кластера в память. • Чтобы определить следующий кластер файла, система просматривает запись- указатель в FAT, соответствующий кластеру 2. Согласно вышеприведенной диаграмме, запись содержит величину 003, означающую, что файл продолжается в кластере 003. Система считывает в оперативную память содержимое этого кластера. • Чтобы определить следующий кластер файла, система просматривает запись- указатель кластера 3 в FAT. Согласно вышеприведенной диаграмме, запись содержит 004, указывающую на кластер 004. Система считывает в оперативную память данные его секторов. • Запись-указатель в FAT для кластера 004 содержит значение FFFH, означающую, что этот кластер - последний кластер файла. Теперь система считала , все данные из файла в оперативную память. Мы рассмотрели, как FAT работает теоретически. Практическая реализация несколько более замысловата, поскольку байты записей переставлены в обратном порядке. Обработка записей FAT12 . Ниже приведены примеры записей FAT 12 для только что рассмотренного файла TEMPSTAT.FIL. Однако теперь мы будем рассматривать эти записи с I 406 Часть V. Усовершенствованные операции ввода/вывода
учетом того, что байты переставлены в обратном порядке. Записи FAT12 в данном случае содержат следующее: F0F FFF 034 000 FF0 Fxx Соответствующий кластер 0 1 2 3 4 5 ... Теперь нужно расшифровать записи, приведя их в форму, соответствующую показанной выше, но представляющую FAT побайтно: F0 FF FF 03 40 00 FF "0F Соответствующий кластер 0 1 2 3 4 5 б 7 ... (Такое представление диаграммы соответствия кластеров и записей-указателя в FAT поможет нам правильно выполнить перестановку байтов FAT.) Ниже приведены шаги, необходимые для правильного отслеживания соответствия и доступа к записям FAT. • Для обработки первой записи FAT умножаем 2 (первый кластер файла,, номер которого записан в структуре директорий) на 1,5 (восстанавливаем длину записей FAT) и получаем 3 (при программировании умножьте на 3 и сдвиньте направо на 1 бит). Просмотрите слово из 3-го и 4-го байтов FAT (нумерация также с нуля). Эти* байты содержат 03 40, что после перестановки превратится в 40 03. Поскольку кластер 2 имел четный номер, три младших цифры слова (003) - это номер следующего кластера файла. • Дня третьего кластера умножим номер кластера на 1,5 и получим 4 (с дробной частью). Считываем байты 4 и 5 из FAT. Эти байты содержат 4000, что после перестановки дает нам 0040. На этот раз считываем три старших цифры слова (поскольку номер текущего кластера - 003 - нечетный). Полученное число 004 и есть номер следующего кластера файла. • В четвертом кластере умножаем 4 на 1,5 и получаем 6. Считываем байты 6 и 7 из FAT. Эти байты содержат FF0F, что после перестановки дает нам 0FFF. На этот раз считываем три младших цифры слова (поскольку номер текущего кластера - 004 - четный). Полученное число FFF указывает, что файл заканчивается на этом кластере. Обработка записей FAT16 Как говорилось выше, первый байт FAT жесткого диска содержит F8H - указание, что данный носитель - жесткий диск. За ним следуют байты FFFFFFH, как показано ниже, что указывает на FAT16. Структура директорий указывает начальные кластеры всех файлов на диске. Определение номеров кластеров для файлов из FAT16 проще, чем из FAT12, хотя байты так же переставлены местами. Глава 16. Устройство и принципы работы дисковых накопителей 407
Например, рассмотрим случай с одним файлом на диске с FAT16. Файл занимает 4 кластера, с 4 секторами на кластер, т.е. всего 16 секторов. Согласно структуре директорий файл начинается с кластера 2. Каждая запись в FAT16 представляет собой целое слово, так что перестановка байтов затрагивает только одну запись. Вот записи в FAT в виде с переставленными байтами: F8FF FFFF 0300 0400 0500 FFFF Соответствуккций кластер 0 1 2 3 4 5 ... Запись-указатель в FAT ддя кластера 2 содержит 0300, что после перестановки байтов дает 0003 - номер следующего кластера. Запись-указатель для кластера 0003 содержит 0400, что после перестановки дает 0004 - номер третьего кластера файла. Запись FFFFH для кластера 0005 указывает на конец файла. FAT32 использует записи в формате двойных слов с переставленными в обратном порядке байтами. Если программа должна определять тип устройства, можно непосредственно проверять байт в загрузочной записи или использовать функцию 36Н прерывания INT21H. Упражнение: просмотр FAT Воспользуемся DEBUG для исследования FAT дискеты. Ддя выполнения этого упражнения нужна одна пустая дискета 3,5", отформатированная на емкость 1,44 Мбайт, без системных файлов. Скопируйте на дискету два файла: один из них должен иметь размер от 513 до 1024 байт, чтобы поместиться в двух секторах; второй - от 1537 до 2048 байт, чтобы поместиться в четырех секторах. Поскольку файлы сохраняются, начиная со смещения 100Н, записи можно находить так. 1. Загрузочная запись в начале, начиная со смещения 100Н. 2. FAT следует за загрузочной записью: 100Н + 200Н (1 сектор, 512 или 200Н байтов) = 300Н.Ч 3. Структура директорий следует за FAT: 300Н + [18 (12Н) секторов х 200Н] =.2700Н. Вставьте дискету с файлами в дисковод А. Запустите DEBUG и введите команду L (Load - загрузить) - эта команда подробно объясняется в приложении Е: l юо о о зо (для дисковода В используйте команду l юо 1 о зо) Параметры команды L означают следующее: • 100Н - смещение начала сегмента DEBUG, в котором будут сохраняться прочитанные с дискеты данные; 408 Часть V. Усовершенствованные операции ввода/вывода
• первый 0 означает дисковод А (смотрите параграф "Загрузочная запись "); • второй 0 означает чтение данных, начиная с относительного сектора 0; • 30 означает "прочитать ЗОН (48) секторов". Для просмотра считанных данных введите команду D 100. Теперь можно просмотреть загрузочную запись, FAT и структуру директорий дискеты. Загрузочная запись Некоторые из полей загрузочной записи таковы. • Байты со смещениями, начиная с 103Н, показывают номер версии DOS, под которой создавалась эта FAT. • Байты со смещениями, начиная с 10ВН, указывают количество байтов в секторе (0002Н после перестановки байтов дает 0200Н или 512 байтов на сектор). • Байт со смещением 115Н содержит F0H -описание типа устройства (дискета). Можете просмотреть и остальные поля, сверяясь с параграфом "Загрузочная запись ". Структура директорий Для ее просмотра воспользуйтесь командой D 2700. • Байты со смещениями, начиная с 2700Н, содержат имя и расширение первого файла. • Байты со смещениями, начиная с 271 АН, содержат номер первого кластера этого файла (0200 или, после перестановки байтов, 0002). • Байты со смещениями 271СН—271FH дают размер файла. Со смещения 2720Н начинаются данные о втором файле. Заметьте, что байт со смещением 273АН указывает на его стартовый кластер (0400 соответствует кластеру 0004). FAT Для просмотра FAT введите команду D 300. При этом должны быть выведены следующие байты: F0 FF FF 03 F0 FF 05 60 00 07 F0 FF Номер байта 0123456789 10 11 • F0 - описание типа носителя. • FF FF - содержимое второй записи FAT. Глава 16. Устройство и принципы работы дисковых накопителей 409
Записи-указатели, начинающиеся с байта 3, интерпретируются так: • Для обработки первой записи FAT умножаем 2 (первый кластер файла, номер которого записан в структуре директорий) на 1,5 и получаем 3. Считываем байты 3 и 4 из FAT, содержащие 03 F0, и переставляем их местами - получаем F0 03. Поскольку кластер 2 имел четный номер, используем три младших цифры. 003 - это номер следующего кластера. Номер кластера 3 х 1,5 есть 4 (с дробной частью). Байты 4 и 5 содержат F0 FF. После перестановки получаем FF F0. Поскольку кластер 3 имеет нечетный номер, считываем три старших разряда. Это FFF - знак того, что этот кластер - последний в файле. Значит, файл размещается в кластерах 2 и 3. • Для второго файла первый кластер - 4: Кластер 4 х 1,5 5 х 1,5 б х 1,5 7 х 1,5 Байты в FAT 6 и 7 7 и 8 9 и 10 10 и 11 Значения 60, 05 00 60 F0 07 FF F0 Кластер четный нечетный четный нечетный Цифры младшие старшие младшие старшие Следующий кластер 005 006 007 FFF (последний кластер) FFF указывает на конец файла. Обработчик INT 21H предоставляет функции поддержки, позволяющие программам эффективно получать информацию о FAT и структуре директорий, включая функцию 47Н (получить текущую директорию). Эти функции описываются в гл. 18. Работа с файлами на диске Данные и программы хранятся на диске в форме файлов. Хотя нет никаких ограничений на тип данных, которые можно хранить в файлах, обычно файлы данных состоят из ряда записей. Каждая запись в таком файле содержит несколько полей, хранящих данные в определенных форматах. В файле все записи обычно (хотя и не всегда) имеют одинаковый размер. Запись содержит одно или более полей для хранения информации. Например, файл с информацией о покупателях может содержать в каждой записи поле с номерами покупателей, поле с именами, поле адреса и поле текущих платежей. Записи могут быть отсортированы в определенном порядке. Доступ к файлам на жестких дисках и дискетах осуществляется одинаково: и в том, и в другом случае нужно указать имя файла и путь к нему по дереву директорий. 4Ю Часть V. Усовершенствованные операции ввода/вывода ■
Для выполнения операций ввода/вывода с дисков предусмотрено несколько специализированных прерываний. Программа, записывающая (создающая) файл на диске, сначала вызывает функцию обработчика прерывания, создающую запись об этом файле в структуре директорий. Когда все записи файла записаны, он закрывается, при этом система может вычислить размер файла и сохранить это размере в структуре директорий. Программа, которая должна прочитать существующий файл, открывает его. При этом проверяется существование файла. По окончании работы с ним файл должен быть закрыт, чтобы разрешить доступ к нему другим программам. Записи в файле можно считывать или последовательно, одну за другой, или в случайном порядке. Прерывание INT 21H предоставляет функции высокого уровня для работы с файлами. Эти функции работают с записями в структуре директорий, "блокируя" и "разблокируя" их по мере необходимости. Этот обработчик выполняет некоторые предварительные действия до того, как передать управление BIOS. Функции записи и чтения файлов на диске с помощью INT 21Н рассматриваются в гл. 17, а функции работы с директориями - в гл. 18. Низкоуровневые функции работы с диском (на уровне дорожек и секторов) предоставляются обработчиком прерывания INT 13H. Они рассматриваются в гл. 19. Ключевые моменты • Каждая сторона дискеты или жесткого диска содержит набор концентрических круговых дорожек, нумерующихся от края диска, начиная с дорожки 0. Каждая дорожка делится на сектора емкостью 512 байтов, начиная с сектора 1. • Цилиндр -- это набор дорожек с одинаковыми номерами на всех поверхностях дисков накопителя. • Программа может обращаться к сектору по физическому (цилиндр-головка- номер) или относительному номеру. • Кластер - это группа последовательно расположенных секторов, рассматриваемая системой как единица объема диска. • Независимо от объема, все файлы начинаются с границ кластеров и занимают как минимум один кластер. • Загрузочная запись содержит инструкции, необходимые для загрузки (запуска) системы при включении.или перезагрузке. • Структура директорий содержит запись о каждом файле на диске. Эта запись содержит имя файла, его расширение, атрибуты, время и дату создания, номер начального кластера и размер в байтах. Глава 16. Устройство и принципы работы дисковых накопителей 411
• FAT (таблица размещения файлов) распределяет пространство на диске и указывает принадлежность кластеров диска определенным файлам. FAT размещается на диске, начиная с сектора 2, сразу за загрузочной записью и содержит по одной записи для каждого кластера каждого файла директории. Вопросы и упражнения ■ 1. Какова емкость стандартного сектора в байтах? 2. Что такое цилиндр? 3. Каково назначение контроллера дисковода? 4. (а) Что такое кластер? (б) Каково его назначение? (в) Сколько байт могут вместить кластера из 1, 2, 4 и 8 секторов? 5. Вычислите емкости дискет объемом 720 Кбайт и 1,44 Мбайт, исходя из количества дорожек, секторов на дорожку и байтов на сектор. 6. Какие три части содержит системная область диска? 7. (а) Каково назначение загрузочного сектора? (б) Где он расположен на диске? (в) Как можно использовать его для определения количества секторов на дорожке? 8. Как обозначается удаленный файл в структуре директорий? 9. Как в структуре директорий обозначается (а) обычный файл, (б) файл, защищенный от записи, (в) системный файл, (г) метка тома, (д) скрытый файл, защищенный от записи? 10. Какие действия, помимо форматирования, выполняются при использовании DOS-команды FORMAT с ключом /s? 11. Рассмотрим файл размером 2259 байт, (а) Где хранится его размер на диске? (б) Каков его размер в шестнадцатиричной форме? Покажите значение в том виде, в котором оно хранится на диске, (б) Сколько секторов этот файл займет на диске? 12. Где и как FAT указывает тип устройства на (а) жестком диске, (б) дискете 3,5м объемом 720 Кбайт, (в) дискете 3,5м объемом 1,44 Мбайт? 13. Какова структура записей FAT12 и FAT16? 412 Часть V. Усовершенствованные операции ввода/вывода
Глава 17 Дисковые накопители: запись и чтение файлов Цель: описать использование дескрипторов файлов и механизмы последовательного и произвольного доступа к файлам. Введение В этой главе рассматриваются механизмы записи и чтения файлов на дисках, а гл. 18 посвящена различным вспомогательным механизмам, необходимым для работы с дисками, директориями и файлами. Многие из этих дисковых операций требуют использования ASCIIZ-строки для указания требуемого диска, директории и файла, дескриптора файла, необходимого для реализации доступа к файлу, и специальных кодов для распознавания ошибок. Хотя новые инструкции ассемблера в этой главе и не рассматриваются, в ней описаны функции прерывания INT 21H, предназначенные для работы с файлами: ЗСН Создать файл 3FH Прочитать запись 3DH Открыть файл 40Н Записать запись ЗЕН Закрыть файл 42Н Переместить указатель файла Напомним, что термин кластер обозначает группу из одного или более (в зависимости от устройства) секторов данных. ASCIIZ-строки При использовании многих функций работы с дисками необходимо предварительно сообщить системе адрес ASCUZ-cmpoKU, содержащей адрес файла: диск, путь на этом диске и полное имя файла (любой из этих параметров можно пропустить). Строка заключается в апострофы. Признаком окончания такой строки является байт ООН. Максимальная длина этой строки равна 128 байтам. В следующем примере определяются диск и имя файла: PATHNAM1DB 'C:A17RANRD.ASM' , ООН В следующем примере определяются диск, путь и имя файла: PATHNAM2DB 'C:\UTILITY\A17RANRD.ASM' , ООН Глава"17. Дисковые накопители: запись и чтение файлов 413
Обратная косая черта играет роль разделителя имен директорий, файлов и дисков. Байт ООН завершает строку. Для функций, требующих наличия строки с адресом файла, загрузите ее в регистр DX, например, инструкцией LEA DX,PATHNAM1 Дескрипторы файлов В гл. 8 упоминалось, что некоторые предопределенные дескрипторы можно использовать непосредственно - это дескрипторы стандартных устройств: 00 - ввод, 01 - вывод, 02 - вывод сообщений об ошибках, 03 - вспомогательное устройство, и 04 - принтер. Другие устройства ввода/вывода, оперирующие файлами, обязательно требуют наличия дескриптора файла. В этом случае необходимо запросить такой дескриптор у системы. Для этого файл на диске должен быть сначала открыт. В отличие от передачи данных на дисплей и приема данных с клавиатуры, для обмена данными с диском система должна обращаться к FAT и структуре директорий, чтобы получить доступ к файлам. Записи в FAT и структуре директорий должны обновляться соответственно изменениям в файлах. Каждому файлу, к которому программа обращается в процессе работы, должен быть назначен уникальный дескриптор. Система предоставляет дескриптор файла при его открытии для ввода или создании нового файла для вывода. Эти операции используют ASCIIZ-строки и функции ЗСН и 3DH прерывания INT 21H. Дескриптор файла - это уникальное шестнадцатиразрядное двоичное число, возвращаемое в АХ и сохраняемое в виде слова в памяти. Дескриптор используется во всех последующих операциях ввода/вывода с данным файлом. Обычно первый открываемый файл имеет дескриптор 05, второй - 06 и т.д. PSP (префикс сегмента программы) содержит таблицу дескрипторов, предоставляемую по умолчанию и содержащую 20 дескрипторов - предельное количество одновременно открываемых файлов по умолчанию. Для увеличения этого числа можно использовать функцию 67Н прерывания INT 21H. Возвращаемые коды ошибок Операции, использующие дескрипторы файлов, возвращают состояние завершения через флаг переноса CF и регистр АХ. Успешная операция устанавливает CF в 0 и выполняет требуемые действия. В случае неудачи флаг CF устанавливается в 1, а в АХ помещается код ошибки, зависящий от выполнявшейся операции. В приведенной ниже таблице (рис. 17.1) перечислены коды ошибок 01—36 (остальные коды связаны с ошибками при работе в сети). Если 414 Часть V. Усовершенствованные операции ввода/вывода
этих сведений недостаточно, прерывание INT 59H предоставляет дополнительную информацию об ошибках (см. гл. 18). Код 01 02 03 04 05 Об 07 08 09 10 11 12 13 15 16 17 18 19 Ошибка Некорректный номер функции Файл не найден Путь не найден Слишком много открытых файлов В доступе отказано Некорректный дескриптор Разрушен блок управления памятью (МСВ) Недостаточно памяти Некорректный адрес блока памяти Некорректная среда Некорректный формат Некорректный код доступа Некорректные данные Некорректное указание диска (устройства) Попытка удаления директории Не то же устройство Больше нет файлов Диск защищен от записи Код 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Ошибка Неизвестная единица Устройство, не готово Неизвестная команда CRC-ошибка данных Плохая Длина структуры запроса Ошибка поиска Неизвестный тип носителя Сектор не найден В принтере закончилась бумага Ошибка записи Ошибка чтения Общая ошибка Ошибка совместного доступа Нарушение блокировки Некорректная замена диска FCB недоступен Переполнение буфера • Рис. 17.1. Основные ошибки дисковых операций Файловые указатели Система поддерживает отдельный фашовый указатель (file pointer) для каждого файла, с которым работает программа. Операции создания и открытия файлов инициализируют значение файлового указателя, устанавливая его в 0, - такой указатель указывает на начале* файла. Вообще файловый указатель всегда указывает на позицию с определенным смещением в файле. Каждая операция чтения/записи заставляет систему увеличивать значение указателя на число, равное количеству переданных байтов. При этом файловый указатель указывает на место, с которого должна начинаться следующая запись. Указатели используются как при последовательном, так и при произвольном доступе к файлам. Для произвольного доступа к определенной записи программа Глава 17. Дисковые накопители: запись и чтение файлов 415
может использовать функцию 42Н прерывания INT 21H (рассмотренную далее) для установки файлового указателя в произвольное значение. В последующих параграфах рассматриваются процедуры создания, записи и закрытия файлов. Создание файлов Файлы создаются с помощью процедуры, состоящей из следующих шагов. 1. Использовать ASCIIZ-строку для получения дескриптора файла от системы. 2. Вызвать функцию ЗСН прерывания INT 21H для создания записи о файле в структуре директорий. 3. Вызвать функцию 40Н прерывания INT 21Н для записи в файл отдельных записей. 4. По завершении работы с файлом использовать функцию ЗЕН прерывания, INT 21H для закрытия файла. Функция ЗСН прерывания INT 21Н: создать файл Для создания нового файла или перезаписи старого файлом с таким же именем (например, при копировании) используется функция ЗСН прерывания INT 21H. Загрузите требуемые для создаваемого файла атрибуты в регистр СХ (атрибуты рассмотрены в главе 16). В регистр DX загрузите адрес ASCIIZ-строки, содержащей путь для создания файла. Вот пример, демонстрирующий создание на диске С нового файла без атрибутов: PATHNAMl DB ' С : \A<bCOUNTS . FIL ' , О ОН DW MOV MOV LEA INT JC MOV ? АН,ЗСН CX,00 DX, PATHNAMl 21Н ошибка FILHAND1, ,AX /Дескриптор файла ;Запросить создание файла ;Без атрибутов ;Адрес строки пути ;Вызвать обработчик прерывания /Переход в случае ошибки ;Сохранить дескриптор файла При успешном выполнении операции система создаст запись с заданными атрибутами в структуре директорий, установит флаг CF в 0 и поместит в АХ дескриптор нового файла. Этот дескриптор будет использоваться в текущем сеансе работы с файлом. Файл открывается, и его указатель устанавливается в О (начало файла). Теперь в файл можно вести запись. Если файл с заданным 416 Часть V. Усовершенствованные операции ввода/вывода
именем уже существует в указанной директории, указатель также будет установлен в 0, и при записи новые данные будут записываться поверх старых. В случае возникновения ошибки операция устанавливает флаг CF в 1 и помещает в регистр АХ код ошибки. Это может быть код 03, 04 или 05 (смотрите таблицу выше). Код 05 означает, что структура директорий уже полна или указанный файл на диске имеет атрибут "только для чтения". Обязательно проверяйте значение флага CF. Например, при создании файла ему может быть присвоен дескриптор 05, и его легко спутать с кодом ошибки 05 - "в доступе отказано". С рассмотренной функцией связаны функции 5АН и 5ВН прерывания INT 21Н, рассмотренные в гл. 18. Функция 40Н прерывания INT 21Н: записать в файл Функция 40Н прерывания ЕЧТ 21H используется для сохранения отдельных записей в файл на диске. Загрузите в регистр ВХ дескриптор файла, в который нужно произвести запись, в регистр СХ - число записываемых байтов, а в DX - адрес области вывода. В следующем фрагменте дескриптор из предыдущего примера используется для сохранения 256-байтовой записи из DSKAREA: /Дескриптор файла ;Область вывода /Запросить запись в файл /Загрузить дескриптор файла /Загрузить длину записываемого фрагмента (100Н = 256) /Загрузить адрес области вывода /Вызвать обработчик прерывания /Перейти к обработчику ошибки /Все ли байты записаны? /Если нет, обработать ошибку Корректная операция записывает все указанные байты на диск, увеличивает значение указателя файла, устанавливает флаг CF в 0 и записывает в АХ число действительно записанных байтов. Переполнение диска может привести к тому, что не все байты будут записаны. С точки зрения системы такая ситуация не является ошибкой, поэтому программа должна проверять число записанных байтов. Некорректная операция устанавливает флаг CF в 1 и помещает в АХ код ошибки - 05 (в доступе отказано) или 06 (некорректный дескриптор). FILHAND1 DSKAREA DW DB MOV MOV MOV LEA INT JC CMP JNE ? 256 DUP(' ' AH,40H BX,FILHANDl CX,100H DX,DSKAREA 21H ошибка 1 AX,100H ошибка 2 Глава 17. Дисковые накопители: запись и чтение файлов 417 27 —5-741
Функция ЗЕН прерывания INT 21Н: закрыть в файл Закончив работать с файлом, программа должна закрыть его. Для этого загрузите дескриптор файла в ВХ и вызовите функцию ЗЕН прерывания INT 21H: MOV АН,ЗЕН ;Запросить закрытие файла MOV BX,FILHANDl ;Загрузить дескриптор файла INT 21H ;Вызвать обработчик прерывания JC ошибка ;Перейти в случае ошибки Корректная операция закрытия файла записывает все данные, остающиеся в буфере, в файл, обновляет записи о файле в FAT и структуре директорий о размере файла, дате и времени. После этого освобождается указанный дескриптор. Неудачная операция устанавливает флаг CF в 1 и возвращает единственный возможный код ошибки - 06 (некорректный дескриптор). Программа: создание файла на диске В программе, приведенном на рис. 17.2, на диске создается файл, который затем заполняется именами, вводимыми пользователем с клавиатуры. Ее основные процедуры таковы: • A10MAIN - вызывает B10CREATE, C10PROC и, в конце ввода, E10CLOSE; • B10CREATE - использует функцию ЗСН прерывания INT 21H для создания файла и сохраняет полученный дескриптор в элементе данных FILEHAND; • C10PROC - воспринимает ввод с клавиатуры и очищает позиции от конца имени до конца области ввода; • D10WRITE - использует функцию 40Н прерывания INT 21H для записи введенных имен в файл; • E10CLOSE - использует функцию ЗЕН прерывания INT 21H для закрытия файла и создания корректной записи в структуре директорий в конце работы; • F10DISPLY - выводит данные на экран. Длина вводимой записи ограничена 30 байтами, и заканчивается двумя байтами для символов возврата каретки (0DH) и перевода строки (0AH). Таким образом, общая длина одной записи равна 32 байтам. Программа записывает эти 32 байта в виде записи фиксированной длины. Можно было бы пропустить символы перевода строки и возврата каретки, но если вы хотите, например, сортировать записи, то они могут понадобиться - в дальнейшем их использует программа сортировки. 418 Часть V. Усовершенствованные операции ввода/вывода
TITLE NAMEPAR MAXLEN NAMELEN NAMEREC ERRCODE FILEHAND PATHNAME PROMPT OPENMSG WRITEMSG ROW ЧЯ£ • A10MAIN A20: A90: A10MAIN ; B10CREATE A17CRFIL (EXE) .MODEL .STACK .DATA LABEL DB DB DB DB DW DB p DB DB DB DB .CODE PROC MOV MOV MOV MOV INT CALL CMP JNZ CALK CMP JNE CALL MOV INT ENDP PROC MOV MOV LEA INT JC MOV JMP SMALL 64 BYTE 30 '? Создание 30 DUP(' '),0DH, 00 ? 'С:\NAMEFILE 'Name? .DAT 1*** Open error 1*** Write error 0 FAR AX,©data DS,AX ES,AX АХ^ОООЗН ДОН B10CREATE ERRCODE, 00 A90 C10PROC NAMELEN,00 A2 0 E10CLOSE AX,4C00H 21H Создание файла, NEAR AH,3CH CX,00 DX,PATHNAME 21H B2 0 FILEHAND,AX B90 / / 0AH ; / ' , 0 • * * • * * прове файла имен Список параметров: Максимальная длина Фактическая длина Область имени, CR/LF для завершения области Индикатор ошибки Дескриптор файла i i Строка экрана Инициализация сегментных регистров Установка видеорежима и очистка экрана Создание файла Ошибка создания? да, выход Получение имени Колец ввода? нет, продолжить да, завершить, Конец обработки рка корректности: •Используются АХ, ВР, СХ, DX •Запрос создания •Обычный файл •Ошибка? нет, сохранить дескриптор Рис. 17.2. Создание файла на диске Глава 17. Дисковые накопители: запись и чтение файлов 419 27*
В20: В90: BIOCREATE ' ClOPROC C90: ClOPROC / 7 DlOWRITE D20: | DlOWRITE LEA MOV CALL MOV RET ENDP PROC MOV LEA CALL MOV LEA INT CMP JE MOV MOVZX LEA ADD NEG MOVZX ADD REP CALL RET ENDP PROC MOV MOV MOVZX ADD LEA INT JNC LEA MOV CALL MOV MOV RET ENDP BP,OPENMSG CX, 19 FlODISPLY ERRCODE,01 Прием имени с NEAR СХ,0б BP,PROMPT FlODISPLY AH,0AH DX,NAMEPAR 21H NAMELEN,00 C90 AL,20H CX,NAMELEN DI,NAMEREC DI,CX CX DX,MAXLEN CX,DX STOSB DlOWRITE Запись на диск NEAR AH,40H BX,FILEHAND CX,MAXLEN CX,2 DX,NAMEREC 21H D20 BP,WRITEMSG CX, 19 FlODISPLY ERRCODE,01 NAMELEN,00 ; •да, ошибка, ; длина сообщения ;Установка кода ошибки клавиатуры:. Используются АХ, BP, CX, DI, DX Длина подсказки Отображение подсказки Запрос ввода Прием имени Имя введено? нет, выход Пробел для сохранения Длина Адрес + длина J Вычисление оставшейся длины Замена пробелами Запись на диск , проверка корректности: Используются АН, BP, BX, CX, DX •Запрос записи на диск •30 - длина имени плюс 2 (для CR/LF) ;Успешная запись? сообщение об ошибке, ; длина ; вызов программы отображения ;Установка кода ошибки Рис. 17.2 (Продолжение) 420 Часть V. Усовершенствованные операции ввода/вывода
' ElOCLOSE ElOCLOSE / FIODISPLY FIODISPLY PROC MOV CALL MOV MOV INT RET ENDP PROC MOV MOV MOV MOV INT INC RET ENDP END Закрытие файла NEAR NAMEREC,1AH DlOWRITE AH,3EH BX,FILEHAND 21H на диске: Используются АН, ВХ Установка маркера EOF в конце файла Запрос закрытия файла Вывод данных на экран: NEAR АХ,1301Н ВХ,0016Н DH,ROW DL,00 ЮН ROW AlOMAIN Используются АХ, ВХ, DX •ВР и СХ установлены при вызове • Страница:атрибут ■Строка и столбец •Следующая 'строка . Рис. 17.2 (Окончание) Отметим два момента. (1) Символы 0DH и ОАН добавляются после каждой записи только для обеспечения возможности сортировки и могут быть отброшены. (2) Каждая запись могла бы иметь длину, достаточную только для сохранения имени. Как вы понимаете, при этом в программу понадобилось бы добавить дополнительные инструкции. Программу также можно модифицировать, добавив возможность прокрутки экрана. Чтение файлов с диска В этом параграфе описываются процедуры открытия файлов на диске и чтения из них данных. Процедура чтения файлов с диска включает следующие действия. 1. Использовать ASCIIZ-строку для получения от системы дескриптора файла. 2. Вызвать функцию 3DH прерывания INT 21Н для открытия файла. 3. Вызвать функцию 3FH прерывания INT 21H для чтения данных из файла. 4. В конце работы с файлом вызвать функцию ЗЕН прерывания INT 21H для закрытия файла. Глава 17. Дисковые накопители: запись и чтение файлов 421
Функция 3DH прерывания INT 21Н: открыть файл Для получения данных из файла программа должна предварительно вызвать функцию 3DH прерывания INT 21H для открытия файла. Функция проверяет существование указанного файла и, если он существует, предоставляет его дескриптор программе. Загрузите адрес ASCIIZ-строки пути в регистр DX, а в регистр AL поместите 8-битовый код доступа: Биты 0-2 Запрос 000 - только для чтения 001 - только для записи 010 - для чтения/записи Биты 3 4-6 7 Запрос 1 - зарезервировано Режим разделения доступа Наследование Перед чтением из файла программа должна использовать функцию 3DH для открытия файла, а не ЗСН для его создания. В следующем фрагменте файл открывается для чтения: PATHNAMl DB 'С:\ACCOUNTS.FIL' , ООН FILEHAND DW ? /Дескриптор файла MOV AH,3DH MOV AL,00 LEA DX,PATHNAMl INT 21H JC ошибка 1 MOV FILEHAND,AX ;Запросить открытие файла ;Режим доступа - только для чтения ;Загрузить адрес строки пути /Вызвать обработчик прерывания ;Перейти к обработчику ошибки ;Сохранить дескриптор файла Если файл с указанным именем существует, операция устанавливает длину записи в 1 (ее можно изменить), воспринимает текущие атрибуты файла, устанавливает указатель файла в 0 (на начало файла), сбрасывает флаг CF в 0 и возвращает в АХ дескриптор открытого файла. Этот дескриптор будет использоваться вплоть до закрытия файла. Если файл не существует, операция устанавливает флаг CF в 1 и помещает в АХ код ошибки 02, 03, 04, 05 или 12 (смотрите таблицу). Обязательно проверяйте состояние флага CF. Например, при открытии файла ему может быть присвоен дескриптор 05, и его легко спутать с кодом ошибки 05 - в доступе отказано. Программу также можно модифицировать, добавив возможность прокрутки экрана. 422 Часть V. Усовершенствованныечэперации ввода/вывода
FILEHAND INAREA DW DB MOV MOV MOV LEA INT JC CMP JE ? 512 DUPP *) AH,3FH BX,FILEHAND CX,20t)H DX,INAREA 21H ошибка 1 AX, 00 конег1_файла Функция 3FH прерывания INT 21H: прочитать запись из файла Функция 3FH прерывания INT 21Н используется для чтения записей из файлов на диске. Загрузите дескриптор требуемого файла в регистр ВХ, число байтов, которое нужно прочитать, в СХ, и адрес области ввода в DX. В последующем примере дескриптор файла, использовавшийся в предыдущих примерах, используется для чтения 512-байтовой записи: ;Дескриптор файла ;Запросить открытие файла /Загрузить дескриптор файла ;Загрузить длину считываемого ; фрагмента ;Загрузить адрес области ввода /Вызвать обработчик прерывания /Перейти к обработчику ошибки ;Все ли байты прочитаны? /Если да, конец файла Корректная операция передает данные программе, устанавливает в 0 флаг CF, и помещает в АХ число действительно прочитанных байтов. 0 в АХ в примере означает, что была предпринята попытка чтения после конца файла. Это предупреждение, а не ошибка. Некорректное завершение операции устанавливает флаг CF в 1 и загружает в АХ код ошибки 05 (в доступе отказано) или 06 (некорректный дескриптор). Поскольку число одновременно открытых в системе файлов ограничено, программа, последовательно обращающаяся к ряду файлов, должна закрывать любой из них, как только работа с ним окончена. Программа: последовательный доступ к файлу В программе, приведенной на рис. 17.3, выполняется чтение файла, созданного предыдущей программой (рис. 17.2) и отсортированного DOS-командой SORT. В данном случае команда для сортировки записей файла NAMEFILE.DAT в возрастающем порядке в файл NAMEFILE.SRT должна иметь вид SORT n:<NAMEFILE.DAT> NAMEFILE.SRT (Команда SORT помещает отсортированные данные из файла NAMEFILE.DAT в файл NAMEFILE.SRT.) Глава 17. Дисковые накопители: запись и чтение файлов 423
TITLE / RECD_LEN ENDCODE FILEHAND RECAREA OPENMSG PATHNAME READMSG ROW DB 4Rfi . JOU , A10MAIN A20: A80: A90: A10MAIN / B10OPEN A17RDFIL (EXE) Последовательное чтение записей с диска, .MOpEL .STACK .DATA EQU DB DW DB DB DB DB 00 .CODE PROC MOV MOV MOV MOV CALL CALL CMP JNZ CALL CMP JNZ LEA MOV CALL JOT MOV MOV INT MOV INT ENDP PROC MOV MOV LEA вывод на экран SMALL 64 32 00 7> 32 DUP(' ') 1 * * * Open error / * * * i ' С:\NAMEFILE.SRT' , 0 '* * * Read error FAR AX,@data DS,AX ES,AX AL,00H Q10SCROLL B10OPEN ENDCODE,00 A90 CIOREAD ENDCODE, 00 A80 BP,RECAREA CX,RECD_LEN D10DISPLY A20 AH,3EH BX,FILEHAND 21H AX,4C00H 21H Открытие файла, NEAR AH,3DH AL, 00 DX, PATHNAME *** i ■*" npOBG Индикатор завершения Дескриптор файла Область хранения данных Инициализация сегментных регистров Очистка экрана Открытие файла Удалось открыть? нет, выход •Чтение записи с диска •Удачное чтение? нет, выход да, вывод имени длина ; продолжение ;Запрос закрытия файла ;Конец обработки рка корректности: ;Используются АХ, BP, CX, DX ;Запрос открытия ;Обычный файл Рис. 17.3. Последовательное чтение записей 424 Часть V. Усовершенствованные операции ввода/вывода
В20: В90: B10OPEN ; / C10READ C20: C30: C90: C10READ / DlODISPLY — D80: INT JC MOV JMP LEA MOV CALL MOV RET ENDP PROC MOV MOV MOV LEA INT JC CMP JE CMP JE JMP LEA MOV CALL MOV RET ENDP PROC MOV MOV MOV MOV INT CMP JAE INC JMP MOV CALL 21H B20 FILEHAND,AX B90 BP,OPENMSG CX,18 ; DlODISPLY ; ENDCODE,01 Чтение записи с диска Ошибка? нет,' сохранить дескриптор да, сообщение длина вывод сообщения да , проверка конца файла: NEAR ;Используются АХ, ВР, ВХ, СХ, DX | AH,3FH BX,FILEHAND CX,RECD_LEN DX,RECAREA 21H C20 AX,00 C30 RECAREA,1AH C30 C90 BP,READMSG CX,18 DlODISPLY ENDCODE, 01 Программа вывода, про NEAR Запрос чтения 32=длина имени+CR/LF Ошибка чтения? Конец файла? Признак конца файла? да, выход Ошибка, сообщение длина сообщения •Завершение верка конца экрана: ■Используются АХ, ВХ, DX AX,1301H ;BP и СХ устанавливаются при вызове ВХ,001бН DH,ROW DL,10 ЮН ROW,23 D80 ROW D90 AL,01H Q10SCROLL ;Страница:атрибут •Строка •Столбец ;Конец экрана? ; да, пропустить ; нет, следующая строка ;Прокрутка одной строки Рис. 17.3 (Продолжение) Глава 17. Дисковые накопители: запись и чтение файлов 425
D90: D10DISPLY / / Ql0SCROLL Q10SCROLL RET ENDP PROC MOV MOV MOV MOV INT RET ENDP END Прокрутка NEAR AH,06H BH,1EH CX,0000 DX,184Ftf 10H A10MAIN экрана: k /Используются AX, BH, CX, DX ;AL устанавливается при вызове /Установка атрибутов ;Запрос прокрутки Рис. 17.3 (Окончание) Ниже перечислены основные процедуры программы: • A10MAIN - вызывает процедуры B10OPEN, C10READ, D10DISPLY, а в * конце сеанса закрывает файл и завершает работу; • B10OPEN - использует функцию 3DH прерывания INT 21H для открытия файла и сохраняет дескриптор; • С1 OREAD - вызывает функцию 3FH прерывания INT 21H для чтения из файла с помощью дескриптора; • D10DISPLY - выводит на экран записи и управляет курсором. Произвольный доступ к файлам В предыдущих параграфах рассматривался последовательный доступ к файлам. Этот метод хорошо работает при создании файла, распечатке его содержимого или при обработке маленьких файлов. Однако многие программы требуют работы с большими файлами, из которых необходимо выбрать только определенную запись - например, информацию о конкретном товаре или пользователе. Для пополнения файла новыми данными программа, использующая последовательный доступ к файлу, должна будет прочитать все записи в файле от первой до требуемой. Например, если номер требуемой записи - 300, то программа должна была бы прочитать 299 записей, прежде чем добраться до нужной (хотя система и предоставляет возможность начать с записи с указанным номером). Решение данной проблемы заключается в применении произвольного доступа. При таком методе работы с файлами к указанной записи можно обращаться 426 Часть V. Усовершенствованные операции ввода/вывода
непосредственно. Хотя создание файла все равно ведется последовательно, к отдельным записям можно обращаться последовательным или произвольным способом. Когда программа обращается к записи произвольным доступом, операция чтения определяет сектор (по FAT), в котором расположена эта запись, считывает весь сектор в память и передает запрошенные данные программе. В следующем примере записи имеют длину 128 байтов - в одном секторе их помещается четыре. Произвольный запрос к записи номер 21 приведет к считыванию в память следующих записей: Запись 2 0 Запись 21 Запись 22 Запись 23 Когда программа произвольным способом запрашивает другую запись, например, запись 23, система сначала проверяет буфер в оперативной памяти. Поскольку затребованная запись уже находится там, она передается программе. Если программа запрашивает запись, которой нет в буфере, операция находит сектор, в котором находится данная запись, считывает его в память и передает программе указанную запись. При таком методе общее количество обращений к диску уменьшается. Функция 42Н прерывания INT 21Н: переместить указатель файла Операция открытия файла инициализирует указатель файла значением 0. Последующие операции последовательного чтения и записи увеличивают его с каждой прочитанной записью. Для установки указателя в произвольную точку файла можно использовать функцию 42Н прерывания ЕМТ 21Н (перемещение указателя файла), после чего использовать другие функции для чтения и изменения данных в файле. Для обращения к функции 42Н загрузите дескриптор требуемого файла в ВХ, а требуемое значение смещения (в байтах) - в пару CX:DX. Для смещения вплоть до 65535 байтов установите в СХ 0, а смещение задайте в DX. Кроме того, установите код метода в AL - он указывает операции, с какой точки начинать отсчет смещение: 00-е начала файла; 01 - от текущей позиции указателя, которая может соответствовать любой точке файла, включая его начало; 02 - от конца файла; этот метод можно применить для добавления новых записей к концу файла; или для определения размера файла установите CX:DX в 0 и воспользуйтесь методом 02. Глава 17. Дисковые накопители: запись и чтение файлов 427
В следующем примере указатель перемещается на 1024 байта от начала файла: MOV MOV MOV MOV MOV INT JC АН,42Н AL, 00 ВХ,HANDLE1 СХ, 00 DX,1024 21Н ошибка ;Запросить перемещение указателя ; дт начала файла ;Загрузить дескриптор файла ;Старшее слово смещения ;Младшее слово смещения /Вызвать обработчик прерывания /Перейти в случае возникновения ошибки Успешная операция устанавливает флаг CF в 0 и помещает новый адрес указателя в пару DX:AX. Программа может использовать этот адрес для дальнейших операций произвольного доступа. Некорректная операция устанавливает CF в 1 и возвращает в АХ код ошибки 01 (неправильный код метода) или 06 (некорректный дескриптор). Программа: произвольный доступ к файлу на диске Программа, приведенная на рис. 17.4, читает файл, созданный программой, приведенной на рис. 17.2. Вводя относительный номер записи, находящийся в допустимых пределах, пользователь может запросить любую запись, и она будет выведена на экран. Если файл содержит 24 записи, то допустимые номера - от 1 до 24. Номер вводится с клавиатуры в формате ASCII и в данной программе имеет длину 1 или 2 цифры. Программа организована следующим образом: • A10MAIN - вызывает B10OPEN, C10RECNO, Dl 0READ, и E10DISPLY; работа заканчивается при простом нажатии <Enter> в ответ на очередной запрос номера; • B1.0OPEN - открывает файл и сохраняет дескриптор; • C10RECNO - воспринимает номер с клавиатуры и проверяет его корректность (более детальное описание приводится ниже); • Dl0READ - использует функцию 42Н и относительное положение записи из RECINDX для установки указателя файла и вызывает функцию 3FH для помещения требуемой записи из файла в IOAREA; • E10DISPLY - выводит считанную с диска запись на экран. В зависимости от длины введенного номера записи возможны 3 реакции: длина = 0 - завершить работу, длина = 1 - запрос записи с однозначным номером, сохраняемым в AL, длина = 2 - запрос записи с двухзначным номером, сохраняемым в АХ. Процедура должна преобразовывать введенный номер из 428 Часть V. Усовершенствованные операции ввода/вывода
ASCII-формата в двоичный. Поскольку номер хранится в АХ, для решения этой задачи используется инструкция AAD. Предположим, например, что введено число 14 - в ASCII формате это 3134Н. Тогда: • инструкция AND преобразовывает это значение в 0104; • инструкция AAD преобразует это значение в 000EH (14); • инструкция DEC отнимает 1 (поскольку система считает, что первая запись имеет номер 0) - результат равен 000DH (13); ' • инструкция SHL сдвигает значение влево на 4 бита - практически умножая число на 32 (длину одной записи в байтах); в результате получаем 01А0Н (416), и это число сохраняется в элементе данных RECINDX. Программу можно улучшить, например, введя проверку допустимости указанного номера (находится ли он в допустимых пределах). TITLE A17RDRAN (EXE) Чтение BOTT_ROW RECD_LEN FILEHAND RECINDEX ERRCODE PROMPT RECAREA PATHNAME OPENMSG READMSG ROW COL RECDPAR MAXLEN ACTLEN RECDNO 7 R£ A10MAIN .MODEL . STACK . DATA EQU EQU DW DW DB DB DB DB DB DB DB DB LABEL DB DB DB .CODE PROC MOV MOV MOV SMALL 64 22 32 ? ? 00 записей в произвольном порядке ;Низ экрана ;Длина записи /Дескриптор файла ;Индекс записи ;Индикато ошибки чтения 'Record number? ' 32 DUP(' ) 'С:\NAMEFILE ' * * * Open •'*** Read 00 10 BYTE 3 ? 3 DUP(' ' FAR AX,@data DS,AX ES,AX ;Дисковая запись SRT',0 error ***' error * * *' /Входные параметры: ; максимальная длина ; фактическая длина ; номер записи ;Инициализация сегментных ; регистров Рис. 17.4. Чтение записей файла в произвольном порядке Глава 17. Дисковые накопители: запись и чтение файлов 429
А20: АЗО: А90: AlOMAIN '" / BIOOPEN B20: B90: BIOOPEN MOV CALL CALL CMP JNZ MOV CALL CMP JE CALL CMP JNZ LEA MOV CALL CMP JAE INC ЛУГР MOV CALL ЛУГР MOV INT ENDP ' PROC MOV LEA INT JC MOV JMP MOV LEA MOV CALL RET ENDP AL,00H Q10SCROLL BIOOPEN ERRCODE,00 A90 COL,10 C10RECNO ACTLEN,00 A90 DlOREAD ERRCODE,00 A90 BP,RECAREA CX,RECD_LEN E10DISPLY ROW,BOTT_ROW A30' ROW A2 0 AL,01H Q10SCROLL A20 AX,4C00H 21H Открытие файла NEAR AX,3D00H DX, PATHNAME 21H B2 0 FILEHAND,AX . B90 ERRCODE,01 BP,OPENMSG CX, 18 <E10DISPLY Очистка всего экрана Открытие файла Удачно? нет, выход * Сброс столбца Запрос номера записи Есть еще запросы? нет, выход Чтение записи с диска Удачно? ; нет, выход да, вывод имени, длина Низ экрана? да, пропустить нет, увеличить номер строки Прокрутка одной строки, продолжение Конец обработки , проверка успеха: •Используются АХ, BP, CX, DX • Запрос открытия ; обычного файла ,- Ошибка? ; нет, сохранить дескриптор да, ; выдать сообщение, ; длина Рис. 17.4 (Продолжение) 430 Часть V. Усовершенствованные операции ввода/вывода
C10RECNO C20: C30: , C40: ClORECNO DlOREAD Получение номера записи от пользователя, проверка длины, преобразование в двоичное число, умноже PROC LEA MOV CALL MOV LEA INT CMP JB JA XOR MOV JMP MOV MOV AND AAD DEC SHL MOV MOV RET * ENDP Чтение ние на 32 для получения индекса: NEAR ВР,PROMPT СХ,15 E10DISPLY АН,0АН DX,RECDPAR 21Н ACTLEN,01 С40 С20 АН,АН AL,RECDNO СЗО AH,RECDNO AL,RECDNO+l AX,0F0FH AX AX,05 RECINDEX,AX COL,30 Используются 'АХ, ВР, СХ, DX Вывод подсказки, длина - Запрос ввода номера записи Проверка длины 0 г 1, 2 Длина = 0,завершить Длина = 1 символ Длина = 2 символа \ Очистка старших полубайтов Преобразование в двоичное Коррекция (1-я запись 0) Умножение на 32 Сохранение индекса произвольной дисковой записи по индексу записи, проверка успеха операции: PROC MOV MOV MOV MOV MOV INT JC MOV MOV MOV LEA NEAR АН,42Н AL, 00 BX,FILEHAND СХ,00 DX,RECINDEX 21Н D2 0 AH,3FH BX,FILEHAND CX,RECD_LEN DX,RECAREA •Используются АХ, ВР, ВХ, СХ, DX •Запрос установки указателя файла на начало файла •Дескриптор файла •Старшая часть смещения •Младшая часть смещения ;Ошибка? да, пропустить ;Запрос ввода ;32 = длина имени и CR/LF Рис. 17.4 (Продолжение) Глава 17. Дисковые накопители: запись и чтение файлов 431
D20: D30: D90: D10READ ' ElODISPLY EIODISPLY ' / Q10SCROLL Q10SCROLL INT JC CMP JE JMP LEA MOV CALL MOV RET ENDP PROC MOV MOV MOV MOV INT RET ENDP PROC MOV MOV MOV MOV INT RET ENDP END 21H D20 RECAREA,1AH D30 D90 BP,READMSG CX, 18 EIODISPLY ERRCODE,01 Ошибка при чтении? Конец файла? да, выход нет, вывод сообщения, длина Завершение Программа вывода: NEAR AX,130lH BX,0016H DH,ROW DL,COL 10H Прокрутка экра NEAR AH,06H BH,1EH CX,0000 DX,184FH 10H A10MAIN •Используются АХ, ВХ, DX •ВР, СХ установлены при вызове ■Страница и атрибут •Строка / •Столбец на, установка атрибутов: ;Используются BH, CX, DX ;AL устанавливается при вызове ;Установка атрибутов ;Запрос прокрутки Рис. 17.4 (Окончание) Программа: чтение ASCII-файла В предыдущих примерах программы создавали файлы и читали из них данные, но может возникнуть потребность обрабатывать ASCII-файлы, созданные программами редакторов или текстовых процессоров. Вам может потребоваться информация об организации FAT и структуры директорий и способе хранения данных в секторах. Например, данные в ASM-файле сохраняются точно в том ' виде, в каком мы их вводим, включая символы табуляции (09Н), возврата каретки (0DH) и перевода строки (ОАН). Для сохранения дискового пространства пробелы, 432 Часть V. Усовершенствованные операции ввода/вывода
появляющиеся на экране непосредственно перед символом табуляции и пробелы, находящиеся за символом возврата каретки, не сохраняются. Например, Мы вводим с клавиатуры инструкцию <Tab>MOV<Tab>AH,О 9<ТАВ> Шестнадцатеричное представление этой строки: 094D4F560941482C3 03 90D0AH Здесь байты 09Н соответствуют символам табуляции, 0DH - возврату каретки, а ОАН - переводу строки. Когда программа редактора считывает строку из файла и выводит ее на экран, эти символы автоматически обрабатываются должным образом. Рассмотрим программу, приведенную на рис. 17.5, которая реализует чтение и вывод записей файла, созданного программой, приведенной на рис. 17.3, по сектору за раз. Программа выводит каждую строку вплоть до символов возврата каретки и перевода строки и организована следующим образом: • A10MAIN - вызывает процедуры B10OPEN и С1 OREAD для чтения первого сектора и D10XFER, закрывающую файл в конце работы; • B10OPEN - открывает файл, сохраняет полученный дескриптор и определяет размер файла (основываясь на содержимом АХ); • С1 OREAD - считывает сектор данных в SECTOR; • D10XFER - перемещает данные из SECTOR в строку отображения, вызывает E10DISPLY для их вывода на экран, вызывает C10READ для чтения следующего сектора и продолжает такую последовательность действий до достижения конца файла (детальное описание этой процедуры приведено далее); • E10DISPLY - выводит данные на экран включительно до символа перевода строки; поскольку строки в ASCII-файле могут быть разной длины, необходимо находить конец каждой строки до ее вывода на экран (операция вывода с использованием функции 13Н прерывания INT 10H не воспринимает символы табуляции, выводя их на экран в виде кружков); • F10ERROR - должна выводить сообщения при ошибках работы с диском. Процедура E10DISPLY передает данные побайтно из SECTOR в DISPAREA, где хранятся данные, которые должны выводиться на экран. Процедура проверяет, достигнут ли конец сектора (чтобы своевременно прочитать новый сектор) и достигнут ли конец области вывода. В обычных текстовых файлах строки относительно короткие и всегда заканчиваются символами возврата каретки и перевода строки. Другие файлы (например, .ЕХЕ или .OBJ) могут не содержать этих символов или отдельных строк, поэтому программа должна проверять корректность перемещения данных, чтобы не записать их в область, следующую Глава 17. Дисковые накопители: запись и чтение файлов 433 28 —5-741
за DISPAREA. Программа предназначена для вывода обычных текстовых файлов, но в качестве предосторожности добавлена проверка достижения конца DISPAREA. Ниже перечислены шаги, выполняемые программой. 1. Инициализировать адреса SECTOR и DISPAREA. 2. Если достигнут конец SECTOR, прочитать следующий сектор. Если достигнут конец файла, завершить работу; в противном случае инициализировать адрес SECTOR. 3. Если достигнут конец DISPAREA, вывести строку и проинициализировать DISPAREA. 4. Получить символ из SECTOR и сохранить его в DISPAREA. 5. Если все символы обработаны, выйти. 6. Если текущий символ - перевод строки (ОАН), вывести строку и перейти к пункту 2; иначе перейти к пункту 3. TITLE DISPAREA ENDCODE FILESIZE FILEHAND OPENMSG PATHNAME ROW SECTOR / A10MAIN A17RDASC (EXE) Чтение .MODEL .STACK .DATA DB DW DW DW DB DB DB DB .CODE PROC MOV MOV MOV MOV INT CALL CMP JNE CALL CMP JE SMALL 64 80 DUP(' ') ; 00 о - 0 /вывод ASCII-файла Область вывода Флаг конца процесса Размер файла (младшая часть) Дескриптор файла '*** Open error ***' 1 'C:\A17RDRAN.ASM', 0 | 00 512 DUP(' ') FAR AX,©data DS,AX ES,AX AX,0003H 10H B10OPEN ENDCODE,00 A90 CIOREAD ENDCODE,00 A90 Область ввода Главная процедура Инициализация сегментных регистров Установка режима и очистка экрана Открытие файла Удачно? нет, выход Чтение 1-го сектора Конец файла, данных нет? да, выход Рис. 17.5. Чтение ASCII-файла 434 Часть V. Усовершенствованные операции ввода/вывода
CALL A90: A10MAIN ' BIOOPEN B20: B90: BIOOPEN ; CIOREAD CIOREAD D10XFER MOV MOV INT MOV INT ENDP AH,3EH BX,FILEHAND 21H AX,4C00H 21H Открытие файла,, если PROC MOV LEA INT JNC CALL JMP MOV MOV MOV MOV MOV INT MOV MOV MOV INT RET ENDP PROC MOV MOV MOV LEA INT MOV RET ENDP NEAR AX,3D00H DX,PATHNAME 21H B2 0 F10ERROR B90 FILEHAND,AX AX,4202H BX,FILEHAND ex, о DX,CX 21H FILESIZE,AX AX,4200H DX,CX 21H Чтение сектора NEAR AH,3FH BX,FILEHAND ; CX,512 DX,SECTOR 21H ENDCODE,AX Вывод/чтение Запрос закрытия файла Конец обработки успешно, подсчет размера файла: Используются АХ, ВХ, СХ, DX Открыть только для чтения Установлен флаг переноса? да, ошибка Сохранить дескриптор Установить указатель на конец файла для определения размера файла Сохранить размер (младш.часть) Сброс указателя файла на начало файла Использование АН, ВХ, СХ, DX Запрос чтения Устройство Длина сектора Буфер Сохранить статус Рис. 17.5 (Продолжение) Глава 17. Дисковые накопители: запись и чтение файлов 435 28*
/ / D10XFER D20: D30: D40: D50: D80: D90: DlOXFER / / ElODISPLY PROC CLD LEA LEA LEA CMP JNE CALL CMP JE LEA LEA CMP JB CALL LEA LODSB STOSB DEC JZ CMP JNE CALL JMP CALL RET ENDP PROC MOV MOV LEA LEA NEG ADD MOV MOV INT QMP Передача данных в строку вывода: NEAR ' ; / SI,SECTOR DI,DISPAREA DX,SECTOR+ 512 SI,DX D40 CIOREAD ENDCODE,00 D80 SI,SECTOR DX,DISPAREA+8C DI, DX D50 ElODISPLY DI,DISPAREA * FILESIZE D80 AL,0AH- D30 ElODISPLY D20 ElODISPLY , Вывод строки, NEAR AX,1301H BX,0061H BP,DISPAREA CX,DISPAREA , CX CX,DI ; DH,ROW DL,10 10H ROW,24 Используются AL, DI, DX, SI Слева направо Конец сектора? нет, пропустить да, читать дальше Конец файла? да, выход Конец DISPAREA? нет, пропустить да, вывести ;[SI] в AL, INC SI ;AL в [DI], INC DI ;Уже все символы? ; да, выход /Перевод строки? ; нет, цикл ; да, вывод ;Вывод последней строки проверка конца экрана: Используются АХ, ВР, QX, CX, DX Запрос вывода Страница и атрибуты Вычислить длину строки ;Конец экрана? Рис. 17.5 (Продолжение) 436 Часть V. Усовершенствованные операции ввода/вывода
Е20: Е90: E10DISPLY '* / Fl0ERROR F10ERROR JAE INC JMP MOV MOV INT MOV INT RET ENDP PROC MOV MOV LEA MOV MOV INT MOV RET ENDP END E20 ROW E90 ROW,00 АН,10Н 16H AX,0003H 10H нет, выход Следующая строка Ожидание ввода с клавиатуры Установка режима и очистка экрана Вывод сообщения об ошибке диска: NEAR АХ,1301Н ВХ,0031Н BP,OPENMSG СХ,18 DX,1020H ЮН ENDCODE,01 A10MAIN •Используются АХ, ВР, ВХ, СХ, DX •Запрос вывода •Страница и атрибут • Длина •Сорока : столбец ;Флаг ошибки Рис. 17.5 (Окончание) Попробуйте выполнить эту программу под управлением DEBUG с соответствующим номером устройства и файлом ASCII. После каждой операции с диском просматривайте область данных и наблюдайте за их обработкой. Программу можно улучшать, например, организовать ввод с клавиатуры пути и имени файла, и использовать пару регистров DX:AX для хранения размера файла. Ключевые моменты • Многие функции прерывания INT 21Н для работы с диском используют ASCIIZ-строку, содержащую путь и/или имя файла и заканчивающуюся байтом ООН. • При возникновении ошибки эти функции устанавливают флаг CF в 1 и помещают в регистр АХ код ошибки. • Система поддерживает файловый указатель для каждого обрабатываемого в данный момент файла. Операции создания или открытия файла устанавливают этот указатель в 0, т.е. на начало файла. Операции чтения и записи сдвигают указатель к концу файла. Глава 17. Дисковые накопители: запись и чтение файлов 437
• Функция ЗСН прерывания INT 21H используется для создания файла до того, как в него можно вести запись, а функция 3DH - для открытия уже суще- . ствующего файла. Обе операции возвращают дескриптор файла, по которому к нему в дальнейшем обращается программа. • После завершения работы с файлом программа должна закрыть его, чтобы система могла обновить запись о нем в структуре директорий. • ' Функция 42Н прерывания INT 21H (переместить указатель файла) используется для перемещения указателя при произвольном доступе к записям файла. Вопросы и упражнения 1. Какие коды ошибок соответствуют ошибкам (а) в доступе отказано, (б) файл не найден, (в) диск защищен от записи, (г) некорректный номер функции? 2. Определите ASCIIZ-строку с именем ASCSTRING, содержащую путь к файлу MONITOR.FIL на диске С:. 3. Для файла из вопроса 2 напишите инструкции для (а) его создания без атрибутов и сохранения полученного дескриптора в случае корректного выполнения операции, (б) записи в него 100 байтов с помощью полученного дескриптора, (в) закрытия файла. 4. Модифицируйте программу, приведенную на рис. 17.5, так, чтобы пользователь мог вводить имя файла с клавиатуры, а программа выводила бы его содержимое на экран. Организуйте возможность ввода нескольких запросов и возможность завершения работы при нажатии <Enter> в ответ на очередной запрос. 5. Напишите программу, позволяющую пользователю вводить номера деталей (три цифры), их имена (12 символов) и цены (в формате nnn.nn). Программа должна создавать файл из записей, содержащих эту информацию. Не забудьте преобразовывать цены из ASCII-формата в двоичный. Вот пример вводимых данных: Номер 023 024 027 049 114 117 Имя Assemblers Linkages Compilers Compressors Extractors Haulers Цена 00525 00630 00725 01020 11750 01530 Номер 122 123 127 232 237 999 Имя Lifters Processors Labelers Bailers Grinders Цена 12320 12535 01560 07345 09760 00000 438 Часть V. Усовершенствованные операции ввода/вывода
6. Напишите программу, выводящую на экран содержимое файла данных предыдущего вопроса. Эта программа должна преобразовывать двоичное значение цены в ASCII-формат. 7. Используя файл данных вопроса 5, напишите программу, выполняющую следующие функции: (а) считывающую все записи в таблицу в памяти, (б) запрашивающую у пользователя номер детали и количество, (в) находящую в таблице элемент с данным номером <и его цену, (г) если таковой найден, вычисляющую сумму закупки (количество х цена), (д) выводящую на экран результаты подсчета. Организуйте возможность ввода нескольких запросов. 8. Файл состоит из 100 записей длиной 256 байтов каждая. Напишите инструкции для установки файлового указателя (а) в конец файла, (б) на 20-ю запись, (в) на начало файла. 9. Напишите инструкции, позволяющие программе определить размер файла. 10. Модифицируйте программу из вопроса 6 так, чтобы она использовала произвольный доступ к диску. Определите таблицу из корректных номеров деталей со значениями смещений для соответствующих записей. Запросите у пользователя номер детали, запись о которой программа должна найти. Используйте смещение из таблицы для поиска записи в файле и вызовите функцию 42Н прерывания INT 21H для установки файлового указателя. Выведите найденную запись на экран. Запросите у пользователя количество деталей данного типа. После этого вычислите и выведите на экран стоимость покупки (количество х цена). Глава 17. Дисковые накопители: запись и чтение файлов 439
Глава 18 Дисковые накопители: функции поддержки дисков и файлов прерывания INT 21Н Цель: рассмотреть функции поддержки дисковых накопителей и файлов прерывания INT21H Введение В этой главе описывается ряд полезных функций, разделенных на 3 категории: предназначенные для работы с дисковыми накопителями, для работы со структурой директорий и FAT и для работы с файлами на дисках. В каждом параграфе функции описываются в порядке возрастания их кода. Операции для работы с дисками: Код Код функции подфункции 0DH ОЕН 19Н 1FH 2ЕН 32Н 36Н 4400Н 4401Н 4404Н 4405Н 4406Н 4407Н 4408Н Действие Сброс состояния накопителя Выбор устройства по умолчанию Определить устройство по умолчанию Получить блок параметров устройства по умолчанию (drive parameter block - DPB) Установить/отменить проверку диска Получить блок параметров устройства (DPB) Получить объем свободного пространства на диске Получить информацию об устройстве Установить информацию об устройстве Прочитать управляющие данные из устройства Записать управляющие данные в устройство Проверить состояние ввода Проверить состояние вывода Определить, заменяемый ли носитель в устройстве 440 Часть V. Усовершенствованные операции ввода/вывода
440DH 440DH 440DH 440DH 440DH 440DH 440DH 54Н 59Н 41Н 61Н 42Н 46Н 60Н 66Н 68Н Записать сектор диска Прочитать сектор диска Отформатировать дорожку Установить ID для носителя Получить параметры устройства Получить ID носителя Распознать тип носителя Получить состояние проверки Получить дополнительную информацию об устройстве Операции для работы с FAT и структурой директорий: 39Н Создать поддиректорию ЗАН Удалить поддиректорию ЗВН Сменить текущукГдиректорию 47Н Получить текущую директорию Операции для работы с файлами: 1 АН Установить адрес обмена с диском 29Н Проанализировать имя файла 41Н Удалить файл 43Н Установить/прочитать атрибуты файла 45Н, 46Н Продублировать дескриптор файла 4ЕН, 4FH Найти подходящий файл 56Н Переименовать файл 57Н Установить/прочитать время/дату файла 5АН, 5ВН Создать временный/новый файл Операции для работы с дисками Функция 0DH прерывания INT 21Н: сбросить состояние дискового накопителя Обычно при закрытии файла все остающиеся в буфере записи записываются в файл, после чего обновляется запись в структуре директорий. В особых случаях, Глава 18. Дисковые накопители: функции поддержки дисков... 441
например, при возникновении ошибок, программа может использовать функцию 0DH для сброса состояния накопителя: MOV AH,0DH /Запросить сброс состояния накопителя INT 21H /Вызвать обработчик прерывания Операция очищает буфер накопителя и устанавливает все головки чтения/записи на цилиндр 0. Функция не закрывает открытые файлы и не возвращает каких-либо значений. Функция ОЕН прерывания INT 21Н: выбрать дисковый накопитель по умолчанию Основное назначение функции ОЕН - выбрать какой-либо накопитель в качестве текущего накопителя по умолчанию. Для ее использования загрузите в регистр DL номер накопителя: 0 - диск А, 1 - диск В и т.д. MOV АН,ОЕН /Запросить выбор диска по умолчанию MOV DL,02 ;Выбрать диск С: INT 21H ;Вызвать обработчик прерывания Операция помещает общее число дисков (включая виртуальные) в регистр AL. Поскольку даже простейшие системы имеют, как минимум, два диска, минимальное возвращаемое значение - 02 (воспользуйтесь функциями прерывания INT 11H для определения действительного количества дисков). Функция 19Н прерывания INT 21Н: определить диск по умолчанию . Функция определяет текущее устройство по умолчанию: MOV АН,19Н /Определить диск по умолчанию INT 21H ;Вызвать обработчик прерывания Функция возвращает номер текущего диска по умолчанию в регистр AL: 0 - диск А, 1 - диск В и т.д. Этот номер может использоваться программой непосредственно для обращений к файлам на этом диске, хотя некоторые функции подразумевают, что номер 1 соответствует дисководу А, 2 - В и так далее. Функция 1FH прерывания INT 21Н: получить DPB - блок параметров накопителя по умолчанию Блок параметров накопителя - это область данных, содержащая следующую информацию о низкоуровневой структуре диска: 442 Часть V. Усовершенствованные операции ввода/вывода
Смещение ООН 01Н 02Н 04Н 05Н 06Н 08Н 09Н ОВН 0DH 0FH ПН 13Н 17Н 18Н 19Н 1DH 1FH Тип элемента данных Байт Значение Номер диска Байт Логическое устройство для драйвера Слово Размер сектора в байтах Байт Количество секторов на кластер минус 1 Байт Количество секторов на кластер (степени двух) Слово Первый относительный сектор FAT Байт Количество копий FAT Слово Количество записей в корневой директории Слово Первый относительный сектор первого кластера Слово Номер последнего кластера плюс1 Слово Сектора, занятые таблицами FAT Слово Первый относительный сектор структуры директорий Двойное слово Адрес драйвера устройства Байт Описание носителя Байт Флаг доступа (0, если к диску уже обратились) Двойное слово Указатель на следующий DPB Слово Последний выделенный кластер Слово Количество свободных кластеров Корректно выполненная операция устанавливает AL в 0 и помещает в пару DS:BX адрес, по которому в памяти размещается DPB для устройства по умолчанию. В случае возникновения ошибки AL будет установлен в FFH. Перед вызовом этой функции сохраните значение регистра DS инструкцией PUSH, a после вызова извлеките его из стека командой POP. См. также описание функции 32Н. Функция 2ЕН прерывания INT 21Н: установить/сбросить проверку диска при записи Эта функция позволяет программе контролировать операции записи на диск, т.е. проверять, были ли данные корректно записаны. Операция устанавливает Глава 18. Дисковые накопители: функции поддержки дисков... 443
флаг, указывающий системе проводить на уровне контроллера диска циклическую проверку избыточности (cyclic redundancy check - CRC, один из методов проверки четности). Выполнение записи в AL значения 00 перед вызовом функции отключает проверку, а значения 01 - включает ее. Флаг сохраняет свое состояние до следующего изменения. Вот пример: MOV АН,2ЕН /Установить/сбросить проверку записи на диск MOV AL,01 /Включить проверку INT 21H /Вызвать обработчик прерывания Операция не возвращает значений. Система будет соответственно реагировать на операции записи, при которых возникли ошибки. Поскольку жесткие диски редко дают сбои, а операция проверки существенно замедляет работу, ее целесообразно применять только при записи особо важных данных. С этой функцией связана функция 54Н - проверить, выполняется ли контроль записи. Функция 32Н прерывания INT 21Н: получить блок параметров накопителя (DPB) Для получения DPB для какого-либо из дисков системы загрузите номер этого диска (0 - диск по умолчанию, 1 - диск А, и т.д.) в регистр DX и вызовите эту функцию. Единственное отличие этой функции от функции 1FH - она позволяет прочитать DPB для произвольно вобранного диска. Функция 36Н прерывания INT 21Н: получить информацию об объеме свободного пространства на диске Функция 36Н возвращает информацию об объеме свободного пространства на диске. Загрузите номер требуемого диска (0 - диск по умолчанию, 1 - диск А, 2 - диск В и т.д.) в регцстр DL: MOV АН,3 6Н /Запросить объем свободного пространства на диске MOV DL,0 ; Выбрать диск по умолчанию INT 21H /Вызвать обработчик прерывания В случае успешного завершения операция возвращает следующие значения: АХ - число секторов на кластер, ВХ - число свободных кластеров, СХ - число байтов в секторе, DX - общее число кластеров в текущем разделе диска. Произведение содержимого АХ, ВХ и СХ дает общее число свободных байтов в разделе, а АХ, СХ и DX - общий объем раздела. В случае некорректного номера устройства в DL функция возвращает в АХ значение FFFFH. Операция не изменяет состояния флага переноса CF. 444 Часть V. Усовершенствованные операции ввода/вывода
Функции 44ххН прерывания INT 21Н: управление вводом/выводом устройств Эта сложная процедура, часто называемая IOCTL (от I/O Control), передает данные между программой и открытым устройством. Для ее использования в регистр AL загрузите код подфункции, чтобы выбрать определенное действие. Корректная операция устанавливает в 0 флаг CF. В случае ошибки, например, некорректного дескриптора, флаг CF устанавливается в 1, а в АХ помещается стандартный код возникшей ошибки. Далее рассматриваются основные процедуры IOCTL. Функция 4400Н: получить информацию об устройстве Эта процедура возвращает информацию о файле или устройстве: MOV АХ,4400Н /Запросить информацию об устройстве MOV ВХ,дескриптор /Выбрать устройство INT 21H /Вызвать обработчик прерывания Корректная операция устанавливает в 1 флаг CF и возвращает в DX значение, бит 7 которого, равный 0, указывает, что заданный дескриптор соответствует файлу. Если бит 7 равен 1, то дескриптор соответствует устройству. Другие биты имеют следующие значения: • для файлов (бит 7 равен 0): 0—5 - номер устройства (0 - А, 1 - В и т.д.); 6 - если равен 1, то файл не записывается на устройство; • , для устройств (бит 7 равен 1): 0 - стандартное устройство ввода с консоли; 1 - стандартное устройство вывода на консоль; 2 - устройство NULL; 3 - часы; 4 - специальное устройство; 5 - если 0, то ASCII-режим, если 1, то двоичный режим; 6 - для ввода: если 0, то получен конец файла. Ошибка при выполнении операции приводит к установке флага CF в 1 и помещает в АХ код ошибки - 01, 05 или 06. Функция 4401Н: установить информацию об устройстве Эта операция устанавливает определенную информацию об устройстве (так, как описано для предыдущей процедуры). Для ее использования загрузите Глава 18. Дисковые накопители: функции поддержки дисков... 445
дескриптор в ВХ и биты, которые необходимо установить, в DL. В случае ошибки флаг CF будет установлен в 1, а в регистр АХ будет загружен код ошибки 01, 05, 06 или 0DH. Функция 4404Н: прочитать управляющие данные с накопителя Эта операция считывает данные с дискового накопителя. Для ее использования загрузите нбмер устройства (0 - диск по умолчанию, 1 - А, 2 - В и т.д.) в регистр BL, число байтов, которые необходимо прочитать, в СХ, и адрес области данных в DX. Успешная операция помещает в АХ число переданных байтов. В случае возникновения ошибок флаг CF устанавливается в 1, а в АХ загружается код ошибки 01, 05 или 0DH. Функция 4405Н: записать управляющие данные на накопитель Эта операция выполняет запись управляющих данных на накопитель. Структура ее вызова точно такая же, как и у предыдущей процедуры. Функция 4406Н: проверить состояние ввода Эта процедура проверяет, доступен ли файл или устройство для ввода. Загрузите дескриптор в регистр ВХ. Корректная операция помещает в AL один из следующих кодов: • устройство: ООН - не готово, или FFH - готово; • файл: ООН - достигнут конец файла (EOF), или 0FFH - не достигнут. При ошибках флаг CF устанавливается в 1, а в регистр АХ помещается код ошибки 01, 05 или 06. Функция 4407Н: проверить состояние вывода Эта процедура проверяет, доступен ли файл или устройство для вывода. Загрузите дескриптор в регистр ВХ. Корректная операция помещает в AL один из следующих кодов: устройство: ООН - не готово, или FFH - готово. файл: ООН - не готов, или FFH - готов. При ошибках флаг CF устанавливается в 1, а в регистр АХ помещается код ошибки 01, 05 или 06. Функция 4408Н: определить, сменный ли носитель в устройстве Эта процедура определяет, является ли устройство сменным носителем, например, дискетой. Для использования этой процедуры загрузите в BL номер 446 Часть V. Усовершенствованные операции ввода/вывода
устройства (0 - по умолчанию, 1 - А, 2 - В и т.д.). Корректная операция сбрасывает флаг CF в 0 и помещает в АХ код ООН - для сменного носителя, и 01Н - для несменного. В случае возникновения ошибки флаг CF устанавливается в 1, а в регистр АХ помещается код ошибки 01Н или 0FH (некорректный номер накопителя). Функция 440DH, код подфункции 41Н: записать сектор диска Эта операция записывает данные из буфера в один или несколько секторов диска. Для ее использования загрузите в регистры следующие данные: MOV MOV MOV MOV LEA INT AX,440DH ВХ,диск CL,41H СН,08Н DX, 6mok_l 21Н диска (0 - по умолчанию, /Записать сектор диска ; Загрузите в ВХ номер : 1 - А, 2 - В и т.д.) ;Код подфункции 41Н - запись сектора диска ;категория устройства - 08Н DX,блок_устройства ;Адрес блока устройства ;Вызвать обработчик прерывания Адрес в регистре DX указывает на блок в памяти, содержащий следующие данные: Блок устройства: специальная функция (ноль) головка записи/чтения . цилиндр начальный сектор число записываемых секторов смещение буфера в памяти _DATA ; адрес сегмента данных Элемент данных Rwbuffer позволяет определять адрес буфера в форме сег- ментхмещение (DS:DX) - с переставленными словами. Оператор SEG указывает определение сегмента, в этом случае - сегмента данных, _DATA. Буфер определяет данные, подлежащие записи, и должен иметь длину, равную количеству записываемых секторов, умноженному на 512, например: WRBUFFER DB 1024 DUP (?) /Буфер*для вывода (2 сектора по 512 байтов) Успешная операция сбрасывает в 0 флаг CF и записывает на диск данные. При возникновении ошибок операция устанавливает флаг CF в 1 и возвращает код ошибки 01, 02 или 05 в регистре АХ. Функция 440DH, код подфункции 42Н: отформатировать дорожку Для использования функции форматирования дорожек загрузите в регистры следующие значения: Devblock Specfunc Rwhead Rwcyl Rwsectl Rwsects Rwbuffer LABEL DB DW DW DW DW DW DW BYTE 0 ? ? ? ? ? SEG Глава 18. Дисковые накопители: функции поддержки дисков... 447
(О - по умолчанию, MOV AX,440DH ; Код функции MOV ВХ,диск ;Загрузите в ВХ номер диска 1-А,2-Вит.д.) MOV CL,42H ;Код подфункции 42Н - форматирование дорожки MOV СН,08Н ;Категория устройства - 08Н LEA DX,блок_устройства /Адрес блока устройства (в DS:DX) INT 21H /Вызвать обработчик прерывания Адрес в регистре DX указывает на блок в памяти, содержащий следующие данные: Blockname LABEL Specfunc DB Diskhead DW Cylinder DW Tracks DW BYTE 0 ? ? ? Блок информации о диске: специальная функция (ноль) головка записи/чтения цилиндр число дорожек Успешная операция сбрасывает флаг CF в 0 и форматирует дорожки. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки 01, 02 или 05. Функция 440DH, код подфункции 46Н: установить идентификатор носителя Для использования этой функции загрузите в регистры следующие значения: MOV АХ,44СЮН;Код функции MOV ВХ,диск ;Загрузите в ВХ номер диска (0 - по умолчанию, 1 - А, 2 - В и т.д.) MOV CL,46H ;Код подфункции 46Н - установка идентификатора носителя MOV СН,08Н ;Категория устройства - 08Н LEA DX,блок_устройства ;Адрес блока устройства (в DS:DX) INT 21H ;Вызвать обработчик прерывания Адрес в регистре DX указывает на блок в памяти, содержащий следующие данные: Blockname LABEL BYTE Infolevel DW 0 Serialno DD ? Vol_label DB 11 DUP (?) Filetype DB 8 DUP (?) Блок информации о диске уровень информации - 0 серийный номер метка тома тип FAT Элемент данных Filetype содержит ASCII-строку FAT12 или FAT16, дополненную в конце тремя пробелами. Успешная операция сбрасывает в 0 флаг CF и устанавливает идентификатор носителя. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки 01, 02 или 05 (см. также подфункцию 66Н функции 440DH ). 448 Часть V. Усовершенствованные операции ввода/вывода
Функция 440DH, код подфункции 60Н: получить параметры устройства Для использования этой функции загрузите в регистры следующие значения: MOV AX,440DH MOV ВХ,диск MOV CL,60H MOV CH,08H LEA DX, блок_устройства INT 21H по умолчанию, ;Код функции ;Загрузите в ВХ номер диска (О 1-А/2-Вит.д.) ;Код подфункции 6 ОН - получить параметры устройства /Категория устройства - 08Н ;Адрес блока устройства (в DS:DX) /Вызвать обработчик прерывания Адрес в регистре DX указывает на блок в памяти, содержащий следующие данные: /Специальная функция (0 или 1) /Тип устройства /Атрибуты устройства /Число цилиндров /Тип носителя /Число байтов в секторе ;Число секторов в кластере /Число зарезервированных секторов /Число экземпляров FAT /Число записей в корневой директории /Общее число секторов /Описание носителя ;Размер одного экземпляра FAT в секторах ?Количество секторов на дорожку ;Количество головок чтения/записи /Число скрытых секторов /Число секторов, если sectors содержит О Если поле specfunct содержит 0, значит, информация относится к носителю по умолчанию для данного устройства; если 1 - информация о текущем носителе. Успешная операция сбрасывает флаг CF в 0 и передает сведения. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки 01, 02 или 05. Функция 440DH, код подфункции 61Н: прочитать сектор диска Эта процедура считывает данные из одного или более секторов диска в буфер. Установите в регистре CL код подфункции 61Н; за исключением этого, все остальное аналогично процедуре записи сектора с кодом подфункции 41Н, рассмотренной выше. В программе, приведенной на рис. 18.1 и рассмотренной далее, проиллюстрировано применение этой функции. specfunc devicetype devattrib cylinders mediatype bytesects seccluster ressectors fats rootentry sectors mediadescs fatsectors sectrack heads hiddensect exsects DB DB DW DW DB DW DB DW DB DW DW DB DW DW DW DD DD *? ? *? *? ? ? ? ? ? ? •p ? ? ? *? ? ? Глава 18. Дисковые накопители: функции поддержки дисков... 29 —5-741 449
Функция 440DH, код подфункции 66Н: получить идентификатор носителя Для использования этой подфункции загрузите в регистры следующие значения: по умолчанию, MOV AX,440DH ;Код функции MOV ВХ,диск ;Загрузите в ВХ номер диска (О 1-А, 2-Вит.д.) MOV CL,66H ;Код подфункции 66Н - чтение идентификатора носителя MOV СН,08Н ;Категория устройства - 08Н LEA DX,блок_устройства ;Адрес блока устройства (в DS:DX) INT 21H /Вызвать обработчик прерывания Адрес в регистре DX указывает на блок в памяти, содержащий следующие данные: Блок информации о диске: уровень информации - (J серийный номер метка тома тип FAT Элемент данных filetype содержит ASCII-строку FAT12 или FAT16, дополненную в конце тремя пробелами. Успешная операция сбрасывает флаг CF в О и читает идентификатор. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки 01, 02 или 05 (смотрите также функцию 440DH, код подфункции 46Н). blockname LABEL infolevel DW serialno DD vol_label DB filetype DB BYTE 0 ? 11 DUP (?) 8 DUP (?) Функция 440DH, код подфункции 68Н: распознать тип носителя Для использования этой функции загрузите в регистры следующие значения: MOV AX,440DH MOV ВХ,диск MOV CL,68H MOV CH,0 8H LEA DX,блок_устройства INT 21H по умолчанию, Код функции Загрузите в ВХ номер диска (0 1 = А, 2 = В и так далее) Код подфункции 68Н - распознать тип носителя: ;Категория устройства - 08Н ;Адрес блока устройства (в DS:DX) ;Вызвать обработчик прерывания Адрес в регистре DX указывает на двухбайтовый блок в памяти, содержащий следующие данные: defaultval DB ? mediatype DB ;01 - для устройства по умолчанию, ; 02 - для других устройств ;02 = 720 КБ, 07 = 1,44 МБ, 09 = 2,88 МБ 450 Часть V. Усовершенствованные операции ввода/вывода
Успешная операция сбрасывает флаг CF в 0 и распознает тип устройства. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки 01 или 05. Другие процедуры функции IOCTL, не рассматриваемые здесь, связаны с разделением доступа к файлам. Функция 54Н прерывания INT 21Н: получить состояние флага проверки записи Эта функция проверяет состояние флага контроля правильности записи на диск (См. функцию 2ЕН, изменяющую состояние этого флага). Функция возвращает в регистр AL значение 00, если проверка не ведется, и 01 - в противном случае. Функция не возвращает ошибок. Функция 59Н прерывания INT 21Н: получить дополнительную информацию об ошибках Эта операция предоставляет дополнительную информацию об ошибках, возникающих при выполнении функций прерывания ENT 21Н, устанавливающих флаг CF, и обработчиков ошибок прерывания INT 24H. Операция возвращает следующие значения: • АХ - расширенный код ошибки; • ВН - класс ошибки; • BL - предлагаемое действие; • СН - место возникновения ошибки. Кроме того, операция сбрасывает флаг CF в 0 и уничтожает содержимое регистров CL, DI, DS, DX, ES и SI. Поэтому перед вызовом этой функции следует сохранять все важные регистры в стеке командой PUSH и извлекать их оттуда после вызова функции командой POP. Далее рассматриваются наиболее распространенные ошибки: • АХ - расширенный код ошибки - существует около 90 различных кодов ошибок, а 00 означает, что последняя вызванная функция прерывания INT 21Н выполнилась без ошибок; • ВН - класс ошибки - предоставляет следующую информацию: • 01 - недоступен ресурс, например, канал передачи данных; • 02 - временная ситуация (не ошибка), например, временно нет доступа к файлу; • 03 - нет требуемой авторизации; Глава 18. Дисковые накопители: функции поддержки дисков... 451 29*
• 04 - ошибка системного программного обеспечения (не в выполняемой программе); 05 - ошибка аппаратуры; 06 - серьезная системная ошибка (не в выполняемой программе); 07 - ошибка в выполняемой программе, например, некорректный запрос; 08 - не обнаружен запрашиваемый объект; 09 - неправильный формат файла или диска; 0А - файл или устройство заблокированы; 0В - ошибка диска, например, ошибка CRC или не тот диск; ОС - файл или объект уже существуют; 0D - неизвестная ошибка; BL - предлагаемое действие - указывает на действие, которое будет выполнено: • 01 - повторить действие несколько раз; может запросить у пользователя завершения; 02 - повторить действие несколько раз после паузы; 03 - запросить у пользователя ввод правильного запроса; 04 - закрыть файлы и завершить программу; 05 - немедленно завершить программу; не закрывать файлы; 06 - игнорировать ошибку; 07 - попросить пользователя предпринять какое-то действие (например, заменить дискету) и повторить операцию; СН - место возникновения ошибки - предоставляет дополнительную информацию для локализации ошибки: 01 - неизвестно, невозможно локализовать; 02 - проблема с диском; 03 - проблема в сети; 04 - проблема с последовательным портом; 05 - проблема с памятью. Программа: чтение данных из секторов диска В программе, приведенной на рис. 18.1, продемонстрировано применение функции 440DH, код подфункции 61Н, прерывания INT 21H. Программа считывает данные из сектора на диске в буфер в памяти и выводит на экран 452 Часть V. Усовершенствованные операции ввода/вывода
шестнадцатеричное представление (две цифры) каждого байта. Значения в блоке элементов в сегменте данных RDBLOCK явно указывают номер головки, номер цилиндра и начальный сектор считываемых данных. Их можно изменять.' RDBUFFER содержит два адреса ячеек в памяти: 1. IOBUFFER - адрес (смещение) буфера для получаемых данных (для одного сектора размером в 512 байтов). 2. SEG _DATA использует оператор SEG, чтобы определить адрес сегмента данных для операций IOCTL. TITLE A18RDSCT(EXE) Чтение сектора диска, ; преобразование шестнадцатеричного в ASCII и отображение .STACK .DATA XLATAB DB READMSG RDBLOCK RDHEAD RDCYLDERR RDSECTOR RDNOSEC RDBUFFER DW IOBUFFER DISPAREA TOC. • JoD, A10MAIN A80: .MODEL 64 DB SMALL 3 0H/31H/32H,33H/34H/35H/3 6H/37H/3 8H/3 9H 1 41H,42H,43H,44H,45H,46H | DB DB DW DW DW DW DW '*** Ошибка чтения ***• 0 0 0 12 1 IOBUFFER SEG _DATA DB DB .CODE PROC MOV MOV MOV MOV INT CALL JC CALL JMP LEA MOV CALL 512 DUP(' ') 1024 DUP(' ') FAR AX,@data DS,AX ES,AX AX,0003H 10H BIOREAD A80 C10CONVRT. A90 BP, READMSG CX,18 D10DISPLY Структура блока Область сектора диска Область отображения Инициализация сегментных регистров Установка видеорежима и очистка экрана Получение данных из сектора При ошибке пропустить Корректные данные конвертировать и отобразить /Некорректные данные, отобразить сообщение об ошибке Рис. 18.1. Чтение секторов диска Глава 18. Дисковые накопители: функции поддержки дисков... 453
А90: A10MAIN ; BlOREAD MOV MOV MOV MOV LEA INT BlOREAD C10CONVRT C20: C10CONVRT ; D10DISPLY MOV INT ENDP PROC AX,4C00H 21H ;Конец обработки Чтение содержимого сектора диска: NEAR AX,440DH BX, 01 CH,08 CL,61H DX,RDBLOCK 21H RET ENDP ;Исполь ;IOCTL зуются АХ, ВХ7 СХ, DX для устройства ;Устройство А ;Категория устройства ;Чтение ;Адрес » сектора блока устройства Конвертирование данных сектора из шестнадцатеричных PROC LEA LEA MOV SHR LEA XLAT STOSB MOV AND XLAT STOSB INC LEA" CMP JB LEA MOV ' CALL RET ENDP PROC MOV MOV NEAR DI,DISPAREA SI,IOBUFFER AL,[SI] AL,04 BX,XLATAB AL,[SI] AL,OFH SI (один байт) в ASCII (два байта): BX,IOBUFFER+512 SI,BX C20 BP,DISPAREA СХД024 D10DISPLY Отображение NEAR AX,1301H BX,0016H данных: i /Используются AL, BP, СХ, DI /Инициализация адреса ; области данных ;Получение байта /Старшая цифра /Установка адреса таблицы /Конвертирование /Сохранение AL в DISPAREA /Младшая цифра / выделяется /Конвертирование /Сохранение AL в DISPAREA /Увеличение адреса IOBUFFER /Конец IOBUFFER? /Нет, повторить /Да, отобразить / шестнадцатеричные символы • ТА г* п г*\ тт L. отгтулгтю сг UY T3Y 'PlY , ylOllQJJ lb oyKJTLM /\Л , IDA, иЛ. ;ВР, СХ устанавливаются при вызове t /Запрос вьтода ;Страница:атрибуты Рис. 18.1 (Продолжение) 454 ' Часть V. Усовершенствованные операции ввода/вывода
D10DISPLY MOV INT RET ENDP END DX,0500H 10H A10MAIN ;Строка:столбец Рис. 18.1 (Окончание) Основные процедуры этой программы таковы. A10MAIN - вызывает процедуру В1 OREAD и, в случае корректного считывания с диска, вызывает С10CONVRT. В1 OREAD - использует операцию IOCTL для чтения данных из сектора диска в IOBUFFER (проверка корректности считывания производится при выходе из процедуры). C10CONVRT - использует инструкции XLAT для преобразования каждого полубайта в IOBUFFER в шестнадцатеричный символ. Получаемые символы последовательно сохраняются в DISPAREA. По завершении преобразования вызывается процедура D10DISPLY. D10DISPLY - выводит на экран символы из DISPAREA. Функция 13Н прерывания INT 10H автоматически переносит данные на следующую строку при достижении конца текущей. Программу можно улучшить, например, организовав ввод с клавиатуры номеров секторов, подлежащих чтению. Функции для работы со структурой директорий и FAT Функция 39Н прерывания INT21H: создать поддиректорию Эта функция создает поддиректорию так же, как и DOS-команда MKDIR. Для ее вызова загрузите в DX адрес ASCIIZ-строки, содержащей имя диска и путь к создаваемой директории: ASCstrg DB • С : \АРРЫС\ ' , ООН ; ASCIIZ-строка MOV АН,3 9Н /Запросить создание поддиректории LEA DX, ASCstrg ;Адрес ASCIIZ-строки (в паре DS:DX) INT 21H ;Вызвать обработчик прерывания Корректная операция сбрасывает флаг CF в 0; при возникновении ошибки CF устанавливается в 1, а в АХ помещается код ошибки 03 или 05. Глава 18. Дисковые накопители: функции поддержки дисков... 455
MOV LEA INT AH,3AH DX,ASCstrg 21H Функция ЗАН прерывания INT 21H: удалить поддиректорию Эта функция удаляет поддиректорию так же, как и DOS-команда RMDIR. Заметим, что нельзя удалить директорию, содержащую файлы, или текущую директорию. Для вызова этой функции загрузите в DX адрес ASCIIZ-строки, содержащей имя диска и путь к удаляемой директории: ASCstrg DB 'C:\APPLIC\',ООН ;ASCIIZ-строка ;Запросить удаление поддиректории ;Адрес ASCIIZ-строки (в паре DS:DX) /Вызвать обработчик прерывания Корректная операция сбрасывает флаг CF в 0; при возникновении ошибки CF устанавливается в 1, а в АХ помещается код ошибки 03, 05 или 10. Функция ЗВН прерывания INT21H: сменить текущую директорию Эта функция сменяет текущую директорию так же, как и DOS-команда CD. Для ее вызова загрузите в DX адрес ASCIIZ-строки, содержащей имя диска и путь к требуемой директории: ASCstrg DB 'С:\APPLIC\', ООН ;ASCIIZ-строка /Запросить переход в директорию ;Адрес ASCIIZ-строки (в паре DS:DX) ;Вызвать обработчик прерывания Корректная операция сбрасывает флаг CF в 0; при возникновении ошибки CF устанавливается в 1, а в АХ помещается код ошибки 03. Функция 47Н прерывания INT 21Н: получить текущую директорию Эта функция определяет текущую директорию для любого диска. Для ее вызова загрузите в SI адрес поля-буфера - достаточно длинного, чтобы вместить строку, указывающую любой путь, и укажите в регистре DL требуемый диск (0 - диск по умолчанию, 1 - диск А, 2 - диск В и т.д.): buffer DB 64 DUP(20H) ;Буфер для хранения пути MOV LEA INT АН, ЗВН DX,ASCstrg 21Н MOV АН,47Н MOV DL,1 LEA SI,buffer INT 21H Запросить текущую директорию например, для диска А Адрес ASCIIZ-буфера (в паре DS:SI) Вызвать обработчик прерывания 456 Часть V. Усовершенствованные операции ввода/вывода
Корректная операция сбрасывает флаг CF в 0 и помещает в буфер имя текущей директории (но не диска) в виде ASCIIZ-строки, например, PROGSYTEST, завершаемую байтом ООН, указывающим конец строки; если текущей является корневая директория, возвращается только байт ООН. С помощью этой функции можно получить текущий путь для обращения к любому файлу в поддиректории. Некорректный номер диска в регистре DL приводит к установке CF в 1, а в АХ помещается код ошибки 0FH. Функция 56Н прерывания INT 21Н: переименовать файл или директорию Эта функция описывается далее в параграфе "Функции для работы с файлами на дисках ". Программа: вывод на экран пути к текущей директории В программе, приведенной на рис. 18.2, продемонстрировано применение двух из только что описанных функций. PATH_LEN DRIVE PATHNAME / А20: EQU DB DB MOV INT ADD MOV MOV MOV LEA INT MOV MOV LEA REPNE CMP JE NEG ADD 64 ' ', 'Л' 64 DUP(' ') АНД9Н 21H - AL,41H DRIVE, AL AH,47H DL,00 SI, PATHNAME 21H AL,00H CX,PATH_LEN DI, PATHNAME SCASB CX,PATH_LEN A90 CX CX,PATH_LEN+2 Устройство Текущий путь Запрос устройства по умолчанию Цифры из 16-го формата в буквы: 0 - А, 1 - В, и т.д. Запрос пути устройства по умолчанию Поиск ООН в PATHNAME •Нет ограничителя? да, выход •Инверсия знака ; Длина DRIVE^ + путь Рис. 18.2. Получение текущей директории Глава 18. Дисковые накопители: функции поддержки дисков... 457
А90: MOV MOV LEA MOV INT MOV INT AX,1301H BX,0016H BP,DRIVE DX,OAOCH 10H AH,10H 16H ;Вывод пути ; страница:атрибуты ; начальный адрес ; строка:столбец ;Пауза до нажатия ; клавиши пользователем Рис. 18.2 (Окончание) Программа выводит на экран путь к текущей директории и выполняет следующие действия. • Использует функцию 19Н для помещения в регистр AL номера диска по умолчанию. Номер 0 соответствует дисководу А, 1 - В и т.д. Для преобразования этого номера в соответствующую букву программа просто прибавляет к нему 41Н - и 0 превращается в 41Н (А), 1 - в 42Н (В) и т.д. • Сохраняет имя диска в DRIVE, дополняя его двоеточием и косой чертой (в виде п:\). • Использует функцию 47Н, чтобы получить путь к текущей директории, и сохраняет полученный путь в PATHNAME. • Находит в PATHNAME ограничивающий символ ООН, чтобы определить длину полученного пути. • Использует найденную длину в процессе выводу имени диска (DRIVE) и пути (PATHNAME). • Ожидает нажатия клавиши и завершает работу. Функции для работы с файлами на дисках В этом параграфе описываются функции, работающие с файлами на дисках Функция 1АН прерывания INT 21Н: установить адрес для обмена с диском Адрес для обмена с диском (disk transfer address - DTA) указывает на область в памяти, в которую будут передаваться данные, считываемые с диска. Эта операция используется в основном с функциями 4ЕН и 4FH, например, так: \ 458 Часть V. Усовершенствованные операции ввода/вывода
DTA_ADDRESS DB nn DUP(?) MOV AH,1AH /Запросить установку DTA LEA DX,DTA_ADDRESS • ;Адрес DTA INT 21H /Вызвать обработчик прерывания Функция 29Н прерывания INT 21Н: преобразовать имя файла Эта функция преобразует командную строку, содержащую полное имя файла в форме п.'filename, ext в формат FCB. FCB (File Control Block - блок управления файлом) - это устаревшее средство доступа к файлам, замененное дескриптором файла. FCB - структура имени файла, состоящая из 11 байтов. 1—8 Имя файла, выравнивается по левому краю, в случае необходимости дополняется пробелами. 9—11 Расширение имени файла, выравнивается по левому краю, в случае необходимости дополняется пробелами. Функция может воспринимать имя файла от пользователя, например, для копирования или удаления файлов. Для ее использования загрузите в регистр SI (используемый в паре DS:SI) адрес требуемого имени файла, в DI (используемый в паре ES:DI) - адрес, по которому система должна создать блок в формате FCB, а в AL - сочетание битов, управляющих методом преобразования: /Запросить преобразование имени файла /Метод преобразования ;Адрес FCB (в паре ES:DI) ;Адрес имени файла (в паре DS:SI) /Вызвать обработчик прерывания Биты в регистре AL оказывают следующее влияние на метод преобразования: Бит Значение Оказываемое влияние 0 0 Означает, что имя файла начинается с первого байта 1 Означает, что необходимо просмотреть командную строку, пропуская разделительные символы (например, пробелы) и найти имя файла 1 0 Установить байт идентификации устройства в создаваемом FCB: нет устройства - 00, диск А - 01, диск В - 02, и т.д. 1 Изменить байт идентификации устройства в создаваемом FCB, если в обрабатываемом имени файла есть имя диска. В таком случае FCB может иметь собственное устройство по умолчанию MOV MOV LEA LEA INT АН,29Н AL, код DI, FCB_hmk SI, имя_файла 21H Глава 18. Дисковые накопители: функции поддержки дисков... 459
Бит Значение Оказываемое влияние 2 0 Изменять по требованию имя файла в FCB 1 Изменять имя файла в FCB, если только новое имя файла корректно 3 0 Изменять по требованию расширение файла в FCB 1 Изменять расширение файла в FCB, если только новое расширение файла корректно 4—7 0 Должны быть нулями При корректных входных данных функция 29Н создает стандартный FCB для заданных имени файла и расширения, с восьмисимвольным именем файла, при необходимости дополненным пробелами, и трехсимвольным расширением, также при необходимости дополненным пробелами. Точки между именем и расширением файла нет. Операция способна воспринимать стандартные знаки пунктуации и преобразует символы подстановок * и ? в строку из одного или более символов. Например, заданное имя файла PROG 12.* преобразуется в PROG12bb??? (два пробела b (от blank) для дополнения имени файла до 8 символов и три знака вопроса для индикации произвольного расширения). В AL возвращается один из следующих кодов: ООН - в имени файла не обнаружены символы подстановок, 01Н - обнаружены. Если в AL содержится FFH, значит, неправильно указано имя диска. После завершения операции пара DS:SI содержит адрес первого байта, следующего за обработанным именем файла, a ES:DI - адрес первого байта созданного FCB. В случае неудачи байт по адресу DI+1 будет пустым, хотя операция будет пытаться преобразовать почти все, что вы ей передадите. Чтобы результаты этой операции были пригодны для получения дескриптора, необходимо удалить пробелы и вставить точку между именем файла и расширением. Функция 41Н прерывания INT 21Н: удалить файл Эта функция позволяет программе удалить не защищенный от записи файл. Загрузите в регистр DX адрес ASCIIZ-строки, содержащей полное имя файла, без символов подстановки: ASCstring DB 'С:\APP\ACL.DAT',ООН ;ASCIIZ-строка MOV АН,41Н /Запросить удаление файла 460 Часть V. Усовершенствованные операции ввода/вывода
LEA DX,ASCstring ;Адрес ASCIIZ-строки (в паре DS:DX) INT 21H /Вызвать обработчик прерывания' Корректная операция сбрасывает флаг CF в 0, помечает данный файл в структуре директорий как удаленный и освобождает выделенное в FAT под данный файл место. При возникновении ошибки CF устанавливается в 1, а в АХ помещаются коды ошибок 02, 03 или 05. Функция 43Н прерывания INT 21Н: установить/прочитать атрибуты файла Эту функцию можно использовать как для чтения, так и для изменения атрибутов файла в структуре директорий. Функция использует адрес ASCIIZ-строки, содержащей полное имя требуемого файла (с именем диска, путем и, собственно, именем файла). Если путь в строке не задан, используется текущая директория. Чтение атрибутов файла Для получения атрибутов файла загрузите в AL код 00, как показано ниже: ASCstring DB 'С:\APP\ACL.DAT1,ООН ;ASCIIZ-строка MOV АН,43Н ;Запросить атрибуты файла MOV AL,00H ;Прочитать атрибуты LEA DX,ASCstring ;Адрес ASCIIZ-строки (DS:DX)' INT 21H ;Вызвать обработчик прерывания Корректная операция сбрасывает флаг CF в 0, очищает регистр СН и помещает текущие атрибуты в CL в следующем виде: Бит Атрибут Бит Атрибут 0 Только для чтения (Read-only) 3 Метка тома (Volume label) 1 Скрытый (Hidden) 4 Поддиректория (Subdirectory) 2 Системный (System) 5 Архивный файл (Archive) При возникновении ошибок флаг CF устанавливается в 1, а в АХ помещается код ошибки 02 или 03. Установка атрибутов файла Для установки атрибутов загрузите в AL код 01 и биты атрибутов, показанные выше, в регистр СХ. Можно изменять атрибуты "только для чтения", "скрытый", "архивный" и "системный", но не "метка тома" или "поддиректория". В следующем примере для файла устанавливаются атрибуты "скрытый" и "системный": Глава 18. Дисковые накопители: функции поддержки дисков... 461
ASCstring DB 'С:\APP\ACL.DAT',ООН ;ASCIIZ-строка MOV MOV MOV LEA INT AH,43H AL, 01 CX,22H DX,ASCstring 21H ;Запросить атрибуты файла ;Установить атрибуты ; "скрытый" и "системный" ;Адрес ASCIIZ-строки (в DS:DX) ;Вызвать обработчик прерывания Корректная операция сбрасывает флаг CF в 0 и заносит в структуру директорий запись об установленных атрибутах. При возникновении ошибок флаг CF устанавливается в 1, а в АХ помещается код ошибки 02, 03 или 05. Функция 45Н прерывания INT 21Н: создать дубликат дескриптора файла Эта функция дает возможность сопоставить одному файлу более чем один дескриптор одновременно. Возможности вновь созданного дескриптора аналогичны таковым у исходного - оба ссылаются на один и тот же файл, поддерживают один и тот же файловый указатель и ту же самую область в памяти. Дубликат дескриптора можно использовать, например, так: с помощью дескриптора вызвать функцию закрытия файла (при этом будет очищен его буфер в памяти и обновлена запись в структуре директорий), а затем с помощью другого дескриптора того же файла продолжать работать с ним. Вот пример вызова этой функции: MOV АН,45Н /Запросить дублирование дескриптора MOV ВХ,дескриптор ;Дескриптор, с которого создаем копию INT 21H ;Вызвать обработчик прерывания В случае успеха функция сбрасывает флаг CF в 0 и помещает в регистр АХ новый дескриптор файла. В случае неудачи CF будет установлен в 1, а в регистр АХ будет помещен код ошибки 04 или 06 (см. также функцию 46Н). Функция 46Н прерывания INT 21Н: принудительно создать дубликат дескриптора файла Эта функция аналогична предыдущей, за исключением того, что можно прямо указать номер создаваемого дескриптора. Эту функцию можно применить, например, чтобы перенаправить поток вывода в другой файл. Для вызова этой функции загрузите в регистр ВХ дескриптор, подлежащий дублированию, а в СХ - новый дескриптор. В случае успеха функция сбрасывает флаг CF в 0. При неудаче флаг CF устанавливается в 1, а в регистр АХ загружаются коды ошибок 462 Часть V. Усовершенствованные операции ввода/вывода
04 или 06. Некоторые сочетания дескрипторов могут не работать: например, дескриптор 00 - это всегда поток ввода с клавиатуры, дескриптор 04 - поток вывода на принтер, а дескриптор 03 (auxiliary - вспомогательный) нельзя перенаправить (см. также функцию 45Н). Функция 4ЕН прерывания INT 21Н: найти первый подходящий файл Функция 4ЕН может применяться для поиска в директории первого из (вероятно) связанных файлов, имеющих что-то общее в названиях. Для поиска последующих файлов, удовлетворяющих тем же критериям, применяйте функцию 4FH, рассмотренную ниже. Для функции 4ЕН необходимо определить 43-байтовый буфер, чтобы она могла вернуть найденную в директории запись. После определения буфера перед вызовом функции 4ЕН необходимо вызвать функцию 1 АН (установить DTA). DTA (Disk Transfer Address - адрес обмена с диском) - это просто блок в памяти, предназначенный для приема данных, считываемых с диска. Установка DTA указывает функции 4ЕН, куда передавать запрошенные данные. Чтобы начать поиск, загрузите СХ атрибутами тех имен файлов, которые вы ищете - произвольным сочетанием атрибутов "только для чтения" (бит 0), "скрыт" (бит 1), "системный" (бит 2), "метка тома" (3), "поддиректория" (4) и "архивный" (5). Загрузите в регистр DX адрес ASCIIZ-строки, содержащей имя искомой группы файлов; это имя может (и, вероятно, будет) содержать символы подстановки * и ?. Например, если указать имя файла в виде a:\ASMPROGS\Al2*.ASM, функция вернет имя первого файла, подходящего под заданный шаблон. Например: ASCstringDB ' ASCIIZ-строка ' , ООН ... # MOV АН,4ЕН ;Запросить поиск первого подходящего файла MOV СХ,00Н ;Без атрибутов LEA DX,ASCstring ;ASCIIZ-строка (адрес в паре DS:DX) INT 21H ;Вызвать обработчик прерывания При обнаружении подходящего под описание файла функция сбрасывает флаг CF в 0 и заполняет 43-байтовое (2ВН) DTA-поле следующими данными: FILEDTA FILEATTR FILETIME FILEDATE LOWSIZE LABEL DB DB DW DW DW BYTE 21 DUP(2 0H) 0 0 0 0 DTA для файла: Зарезервировано для последующих поисков Атрибуты файла Время из записи о файле Дата из записи о файле Младшее слово размера файла Глава 18. Дисковые накопители: функции поддержки дисков... 463
HIGHSIZE DW FILENAME DB 0 13 DUP(2 0H) Старшее слово размера файла Имя и расширение в виде ASCIIZ-строки (в конце ООН) В случае ошибки флаг CF устанавливается в 1, а в АХ помещается код ошибки 02, 03 или 12Н. Если вы планируете после этой функции использовать функцию 4FH, не изменяйте содержимого DTA. Одно из специфических применений функции 4ЕН - определить, указывает ли заданное имя на файл или на директорию. Например, если возвращаемый атрибут- ЮН, то полученный объект-директория. Операция также возвращает размер файла. Функцию 4ЕН можно использовать для определения размера файла, а затем применить функцию 36Н для определения места на диске и возможности записи файла. Функция 4FH прерывания INT 21Н: найти следующий подходящий файл Перед вызовом этой функции необходимо вызвать функцию 4ЕН, рассмотренную выше. После этого для продолжения поиска в директории можно вызывать функцию 4FH: MOV AH,4FH ;Запросить поиск следующего подходящего файла INT 21H /Вызвать обработчик прерывания Успешная операция сбрасывает флаг CF в 0 и помещает в АХ код ООН (файл найден) или 18Н (больше нет подходящих файлов). При ошибке CF устанавливается в 1, а в АХ помещаются коды ошибок 02, 03 или 12Н. В программе, приведенной далее в этой главе на рис. 18.3, показано применение этой функции и функции 4ЕН. Функция 56Н прерывания INT 21Н: переименовать файл или директорию Эта функция позволяет программе переименовать файл или директорию. Загрузите в регистр DX адрес ASCIIZ-строки, содержащей имя диска, путь и имя файла или директории, которые необходимо переименовать. Загрузите в DI (связанный в пару ES:DI) адрес ASCIIZ-строки, содержащей имя диска, новые путь и имя файла или директории, без символов подстановки. Номера дисков, если они используются, должны быть одинаковыми в обеих строках. Если пути отличаются, то переименование сопровождается перемещением между каталогами на том же диске. oldstr DB гп:\oldpath\oldname', ООН newstr DB гп:\newpath\newname', ООН 464 Часть V. Усовершенствованные операции ввода/вывода
MOV LEA LEA INT АН,56Н DX,oldstr DI,newstr 21H ;Запросить переименование файла/директории ;пара DS:DX ;пара ES:DI ;Вызвать обработчик прерывания Успешно выполненная операция сбрасывает флаг CF в 0; в случае ошибки флаг CF устанавливается в 1, а в регистр АХ записывается код ошибки 02, 03, 05илиИН. ^ Функция 57Н прерывания INT 21Н: получить/изменить дату и время файла Эта функция позволяет программе прочитать или изменить дату и время открытого файла. Форматы представления даты и времени в этой функции такие же, как в структуре директорий: Биты для времени Биты для даты ОВН—0FH Часы 09H—OFH Год (относительно 1980) 05Н—ОАН Минуты 05Н—№П Месяц ООН—04Н Секунды ООН—04Н День месяца Секунды записываются с шагом 2, и сохраняемые значения - от 0 до 29. Загрузите тип запроса (0 - чтение, 1 - запись) в регистр AL, а дескриптор требуемого файла - в ВХ. Если вы хотите изменить дату/время, загрузите в регистр СХ новое время, а в DX - дату. Ниже приведен пример: MOV АН,57Н /Запросить MOV AL,01 ; установку даты/времени файла MOV BX,дескриптор /Дескриптор требуемого файла MOV СХ,время ;Новое время (часы|минуты|секунды) MOV ТУХ, дата ; Новая дата (год |месяц | день ) INT 21H /Вызвать обработчик прерывания Успешная операция сбрасывает флаг CF в 0; операция чтения помещает полученное время в регистр СХ, а дату - в DX. Операция записи изменяет дату и время файла. Некорректная операция устанавливает флаг CF в 1 и возвращает в АХ код ошибки 01 или 06. Функция 5АН прерывания INT 21Н: создашь временный файл Программа, использующая временные файлы, может использовать эту функцию, например, в сетях, где имена существующих в заданной директории Глава 18. Дисковые накопители: функции поддержки дисков... 465 30 — 5-741
файлов могут быть неизвестны. Данная функция автоматически создает файл с корректным и уникальным именем по указанному адресу. Загрузите в регистр СХ требуемые атрибуты файла - "только для чтения" (бит 0), "скрытый" (бит 1), "системный" (бит 2), "метка тома" (3), "поддиректория"^) и "архивный" (5). Загрузите в DX адрес пути в виде ASCIIZ-строки, содержащей имя дирка (если это необходимо), путь (если он нужен), обратную косую черту (\) и ООН, а затем - 13 байтов, в которых будет сохранено имя созданного функцией файла: * ASCpath DB гп:\pathname\', ООН, 13 DUP(20H) MOV АН,5АН t ;Запросить создание временного файла MOV CX,атрибуты /Атрибуты создаваемого файла LEA DX,ASCpath ;Путь в виде ASCIIZ-строки INT 21H ;Вызвать обработчик прерывания Успешная операция сбрасывает флаг CF в 0, записывает в АХ дескриптор созданного файла и дополняет конец ASCIIZ-строки именем созданного файла, начиная с первого ООН-байта. Неудачная операция устанавливает флаг CF в 1 и записывает в АХ код ошибки 03, 04 или 05Н. Функция 5ВН прерывания INT 21Н: создать новый файл Эта функция создает новый файл, если только файл с таким именем еще не существует; во всех других отношениях она аналогична функции ЗСН (создать файл). Функцию 5ВН можно использовать, если вы не хотите случайно перезаписать уже существующий файл. Успешная операция сбрасывает флаг CF в 0 и записывает в АХ дескриптор созданного файла. При возникновении ошибок флаг CF устанавливается в 1, а в АХ записываются коды ошибок 03, 04, 05 или 50Н. Программа: избирательное удаление файлов В программе, текст которой приведен на рис. 18.3, показано применение нескольких функций: 4ЕН и 4FH - для последовательного перебора всех имен файлов в директории, и функции 41Н - для удаления выбранных файлов. Программа работает с дисководом А: и состоит из следующих процедур: • A10MAIN - вызывает B10FIRST, C10NEXT, D10MESSG и E10DELETE; • B10FIRST - создает DTA для функции 4ЕН и находит первый файл в директории; 466 Часть V. Усовершенствованные операции ввода/вывода
• C10NEXT - находит последующие файлы в директории; • D10MESSG выводит имена файлов на экран вместе с запросом на удаление; • E10DELETE воспринимает ввод с клавиатуры: Y - для положительного ответа на запрос, и N - для отрицательного; при положительном ответе текущий файл удаляется. В качестве меры предосторожности при тестировании используйте файлы, не представляющие ценности. TITLE CODESG MSSG_LEN ROW COL PATHNAME DELMSG ENDMSG ERRMSG1 ERRMSG2 PROMPT DISKAREA / A10MAIN A20: A18SELDL (EXE) Выбор и SEGMENT PARA 'Code' .MODEL .STACK .DATA EQU DB DB DB DB DB DB DB DB DB .CODE PROC MOV MOV MOV MOV INT CALL CMP JNE MOV LEA CALL INC CALL CALL CMP SMALL 64 32 00 10 ■ A:\*.*', ООН 'Delete? ' удаление файлов 'No more directory 'Invalid path/file 'Write-protected 'Y - Delete, N - 43 DUP(2 0H) NEAR AX,©data DS,AX ES,AX AX,0003H 10H B10FIRST AX,ООН A90 CX,MSSG_LEN BP,PROMPT F10DISPLY ROW D10MESSG E10DELETE AL,OFFH Строка экрана и столбец entries ' disk Keep ' i , Ent - Exit' Главная процедура Инициализация сегментных регистров Установка видеорежима и очистка экрана Первый файл Если нет, выход •Длина подсказки •Отображение подсказки •Отображение имени файла •Удаление, если заказано • Завершить? Рис. 18.3. Выборочное удаление файлов Глава 18. Дисковые накопители: функции поддержки дисков... 467 30*
А90: AlOMAIN ' BIOFIRST B90: BIOFIRST / ClONEXT C90: ClONEXT ' / DlOMESSG JE INC CALL CMP JE MOV INT ENDP PROC MOV LEA INT MOV MOV LEA INT JNC PUSH MOV LEA CALL POP RET ENDP PROC MOV INT CMP JE PUSH MOV LEA CALL POP RET ENDP PROC MOV A90 ROW ClONEXT AX,ООН A2 0 ~AX,4C00H 21H Поиск первого файла в NEAR АН, 1АН DX, DISKAREA 21Н АН,4ЕН СХ, 00 DX, PATHNAME 21Н В90 АХ - CX,MSSG_LEN BP,ERRMSG1 F10DISPLY АХ да, выход Установка следующей строки Получение следующего файла Еще есть? да, продолжить нет, конец обработки директории: Используются АХ, ВР, СХ, DX Получить DTA для вызова функции Найти первый файл в директории Адрес ASCIIZ-строки Корректная операция? нет, сообщение об ошибке Поиск последующих файлов в директории: NEAR AH,4FH 21Н АХ,ООН С90 АХ CX,MSSG_LEN ВР,ENDMSG F10DISPLY АХ Вычисление длины имеь NEAR COL,10 •Использование АХ, ВР, СХ •Чтение следующего файла в директории •Еще есть? да, пропустить нет, ; вывод сообщения ; о конце просмотра in файла и вывод; ;Используются AL, ВР, СХ, DI Рис. 18.3 (Продолжение) 468 Часть V. Усовершенствованные операции ввода/вывода
D10MESSG ; ElODELETE E80: E90: ElODELETE ; ' FIODISPLY FIODISPLY MOV LEA CALL MOV LEA MOV MOV REPNE NEG ADD LEA CALL RET ENDP PROC MOV INT CMP JE * OR CMP 1JNE МОУ LEA INT JNC MOV LEA CALL MOV RET ENDP PROC MOV MOV MOV MOV INT RET ENDP END CX,08 BP,DELMSG FIODISPLY COL,18 DI,DISKAREA+30 AL,ООН CX,13 SCASB CX CX,13 BP,DISKAREA+lEH FIODISPLY Удаление файла, если NEAR АН, ЮН 16Н AL,ODH E80 AL,00100000B AL,'у' E90 AH,41H DX,DISKAREA+lEH 21H E90 CX,MSSG_LEN BP,ERRMSG2 FIODISPLY AL,OFFH Программа вывода: NEAR АХД301Н BX,0016H DH,ROW DL,COL 10H AlOMAIN Длина сообщения Вывод сообщения об удалении Имя файла в DTA Сканирование 13 байтов в поисках ООН в области диска Вычисление длины имени файла Вывод имени файла запрошено: Используются АХ, BP, CX, DX •Ввод символа ответа (у/п) •Нажат Enter? да, выход •Нижний регистр •Запрошено удаление? нет, пропуск установить удаление файла • адрес имени файла •Корректное удаление? ; нет, ; вывод предупреждения ;Флаг завершения работы ;Используются АХ, EX, DX ; ВР, СХ установлены при вызове ;Запрос вывода строки ;Страница:атрибуты Рис. 18.3 (Окончание) Глава 18. Дисковые накопители: функции поддержки дисков... 469
Программу можно улучшить, например, реализовав прокрутку экрана. Ключевые моменты • Операции работы с дисками - это сброс состояния диска, выбор и получение диска по умолчанию, чтение информации о диске, получение сведений о наличии свободного места, и операции расширенного управления вводом/выводом (IOCTL). • Операции для работы со структурой директорий и FAT включают создание и переименование директорий, изменение и получение текущей директории. • Операции для работы с файлами (кроме открытия, создания, записи и чтения) - это их переименование, чтение/изменение атрибутов, поиск файлов и изменение их даты и времени. Вопросы и упражнения Воспользуйтесь DEBUG (или другим отладчиком) для выполнения следующих заданий. Введите команду А 100 и необходимые инструкции ассемблера. Изучите возвращаемые инструкциями значения. Вопросы 1—8 связаны с дисками, 9—11 - со структурой директорий, 12—16 - с файлами. 1. Вызовите функцию 19Н для определения текущего диска по умолчанию. 2. Вызовите функцию 1FH для получения информации о текущем DPB. 3. Вызовите функцию 36Н для определения объема свободного пространства на диске. 4. Вызовите функцию 4400Н для получения информации об используемом устройстве. 5. Вызовите функцию 4406Н для проверки состояния ввода. 6. Вызовите функцию 4408Н для определения, является ли используемый носитель заменяемым. 7. Вызовите функцию 440DH с кодом подфункции 60Н для получения параметров устройства. 8. Вызовите функцию 440DH с кодом подфункции 66Н для получения ID носителя. 9. Вызовите функцию 39Н, чтобы создать поддиректорию. В целях безопасности создавайте ее на дискете. Используйте любое корректное имя. 10. Вызовите функцию 56Н, чтобы переименовать поддиректорию. 470 Часть V. Усовершенствованные операции ввода/вывода
11. Вызовите функцию ЗАН, чтобы удалить поддиректорию. 12. Вызовите функцию 43Н, чтобы прочитать атрибуты файла на дискете. 13. Вызовите функцию 56Н, чтобы переименовать файл. * 14. Вызовите функцию 43Н, чтобы установить для файла атрибут "скрытый". 15. Вызовите функцию 57Н, чтобы прочитать дату и время файла. 16. Вызовите функцию 41Н, чтобы удалить файл. 17. Напишите в DEBUG небольшую программу, которая вызывает функцию 29Н (преобразовать имя файла) прерывания INT 21H. Задайте 81Н в качестве адреса задаваемого имени и 5СН в качестве адреса FCB. Эти адреса указывают в PSP непосредственно перед программой. Вводите различные имена файлов, например, ASMPR01.DOC, ASMPR02, ASMPR03.*, и n:*.ASM. Просмотрите FCB и объясните результаты выполнения программы. Глава 18. Дисковые накопители: функции поддержки дисков... 471
Глава 19 Дисковые накопители: функции BIOS прерывания INT 13H Цель: рассмотреть применение функций BIOS прерывания INT 13H для форматирования, проверки, чтения и записи на диски. Введение В главах 17 и 18 мы рассмотрели функции прерывания INT 21H для работы с дисками. Кроме них, к дискам можно обращаться с помощью функций прерывания DMT 13H, работающего непосредственно с BIOS, хотя BIOS не обеспечивает автоматическую поддержку FAT, структуры директорий и блокировки и разблокировки записей. BIOS-функции для работы с диском INT 13H воспринимают данные на диске в виде последовательности секторов и обращаются к ним по номерам дорожки и сектора. Функции INT 13H позволяют форматировать и проверять диски, записывать на них данные и считывать их, а также сбрасывать состояние диска. Большинство функций прерывания INT 13H предназначены для опытных программистов, представляющих себе возможные последствия неправильного их применения. Кроме того, функции прерывания INT 13H обеспечиваются BIOS, чья версия может различаться от процессора к процессору и от производителя к производителю. В ООН 01Н 02Н озн 04Н 05Н 08Н 09Н ОАН ОВН этой главе рассмотрены следующие функции прерывания INT 13H: Сбросить состояние диска Прочитать состояние диска Прочитать сектора Записать сектора Проверить сектора форматировать дорожки Получить параметры диска Инициализировать диск Прочитать расширенный буфер сектора Записать расширенный буфер сектора ОСИ 0DH ОЕН 0FH 15Н 16Н 17Н 18Н 19Н Перейти к цилиндру Альтернативный сброс состояния диска Прочитать буфер сектора Записать буфер сектора Получить тип диска/дискеты Изменение состояния дискеты Установить тип дискеты Установить тип носителя для форматирования Запарковать головки накопителя 472 Часть V. Усовершенствованные операции ввода/вывода
Байт состояния BIOS Большинство функций прерывания INT 13H устанавливают флаг CF (в 0 - при успешном, или 1 - неуспешном выполнении) и помещают код состояния в регистр АН. BIOS хранит информацию о каждом устройстве и его состоянии в своей области данных. Байт состояния, значения которого расписаны в следующей ниже таблице, состоит из битов индикации состояния, находящихся в BIOS по адресу 40:41Н - для дисководов, или 40:74Н - для жестких дисков (более детально этот вопрос рассмотрен в гл. 24). Значение байта состояния Смысл ООН 01Н 02Н ОЗН 04Н 05Н ОбН 07Н 08Н 09Н ЮН 20Н 40Н 80Н ААН ВВН ССН Нет ошибок Некорректная команда, не распознанная контроллером На диске не найдена метка адреса Попытка записи на защищенный от записи диск Некорректные дорожка/сектор Неудача при операции сброса Дискета извлечена после последнего обращения Некорректные параметры устройства Слишком быстрый DMA-доступ (данные переданы слишком быстро для восприятия) Попытка* использования DMA за 64К-границей при записи/чтении Ошибка CRC при чтении (проверка данных обнаружила повреждения) Неисправен контроллер (ошибка аппаратуры) Ошибка операции поиска (ошибка аппаратуры) Устройство не отвечает (если это дисковод, то отсутствует дискета, если жесткий диск, то превышение допустимого времени ожидания) Устройство не готово Неопределенная ошибка Ошибка записи Рис. 19.1. Коды состояния INT 13H Если при работе с диском возникает ошибка, программа обычно сбрасывает состояние диска (функция ООН) и повторяет операцию три раза. Если ошибка не исчезает, программа может, в частности, вывести сообщение и попросить, например, заменить дискету. Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 473
Основные функции прерывания INT 13H В этом параграфе рассмотрены основные функции прерывания INT 13H для работы с диском. Они вызываются загрузкой соответствующего кода функции в регистр АН. Функция ООН прерывания INT 13H: сброс состояния накопителя Программа может использовать эту функцию, если при выполнении предыдущей операции возникла серьезная ошибка. Операция сбрасывает состояние накопителя; при следующей попытке доступа к нему головки будут сначала перемещены на цилиндр 0. Установите в регистре DL номер устройства: ООН - для дисковода А, 01Н - для дисковода В, 80Н - для диска С, 81Н - для D и т.д., например: MOV АН,ООН ;Сброс состояния накопителя MOV DL,8ОН ;Диск С: INT 13H ;Вызов обработчика прерывания Успешная операция сбрасывает флаг CF в 0; а при возникновении ошибок флаг CF устанавливается в 1 и в АН помещается код ошибки. С этой функцией связана функция 0DH. Функция 01Н прерывания INT 13H: прочитать состояние диска Эта операция предоставляет еще одну возможность проверить результат предыдущей операции (смотрите коды состояния BIOS на рис. 19.1). Как и в предыдущей функции, установите в регистре DL значение ООН - для дисковода А, 01Н - для дисковода В, 80Н - для диска С, 81Н - для диска D и т.д. Эта операция, которая всегда должна исполняться корректно, вернет в регистр AL код, который вернула предыдущая операция в АН, сбрасывает флаг CF в 0 и помещает собственный код состояния, 00, в регистр АН. Функция 02Н прерывания INT 13H: прочитать сектора диска Эта операция считывает указанное количество секторов с одной дорожки в память. Заметьте, что номера цилиндров/дорожек начинаются с 0, а номера секторов - с 1. Занесите в регистры следующие значения: • AL - число секторов, вплоть до всех секторов дорожки; 474 Часть V. Усовершенствованные операции ввода/вывода
• СН - номер цилиндра/дорожки (младшие 8 битов); • CL - биты 7—6: старшие биты номера цилиндра/дорожки; биты 5—0: номер начального сектора; • DH - номер головки/стороны (для дискет - 0 или 1); • DL - номер устройства для дисководов (0 - дисковод А, и 1 - дисковод В) или жестких дисков (С - 80Н, D - 81Н и т.д.); • ES:BX - адрес буфера ввода/вывода в области данных, который должен быть достаточно вместительным, чтобы считать в него все заданные сектора. В следующем примере один сектор считывается в область памяти, обозначенную SECTOR: DB 512 DUP(?) MOV АН,02Н MOV AL,01 LEA BX,SECTOR MOV CH,05 MOV CL,03 MOV DH,00 MOV DL,00 INT 13H ;Область для хранения данных ;Запрос на считывание сектора ;Один сектор » ;Адрес буфера данных (в паре ES:BX) ;Дорожка 05 ;Сектор 03 ;Головка 00 ;0 - дисковод А /Вызвать обработчик прерывания Успешная операция сбрасывает флаг CF в 0 и помещает в AL число действительно прочитанных ею секторов. Содержимое регистров DS, BX, CX и DX сохраняется неизменным. При возникновении ошибок флаг CF устанавливается в 1, а в регистр АН помещается код состояния; в этом случае можно сбросить состояние накопителя и повторить операцию. В большинстве случаев задается чтение только одного сектора или всех секторов дорожки. Проинициализируйте СН и CL, а затем последовательно увеличивайте их значения для чтения последовательно расположенных секторов. Когда номер сектора превышает максимальный для данной дорожки, сбросьте его в 1 и увеличьте номер дорожки на 1, чтобы прочитать начало следующей дорожки, или увеличьте номер головки, чтобы перейти к следующей стороне. Проверка готовности дискеты Программа может попытаться обратиться к дискете, которая еще не вставлена в дисковод. Обычно делается три попытки обращения, после чего пользователю выдается сообщение. В следующем фрагменте программы делается попытка прочитать сектор данных с дискеты с помощью функции 02Н прерывания INT 13H. Попытайтесь в DEBUG ввести приведенные ниже инструкции ассемблера после директивы А100 (без номеров в начале строк и комментариев). Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 475
0100 0103 0104 0107 010А 010D 0110 0112 0113 0115 0116 0118 MOV CX,03 PUSH CX MOV MOV MOV MOV INT POP JNC CLC LOOP JMP AX,0201 BX,0200 CX,0001 DX,0000 13 CX 118 1 103 100 Проверьте работу данной программы со вставленной дискетой и без нее. Если дискета вставлена, программа должна считать загрузочную запись дискеты (512 байт) в память, начиная с адреса DS:200H. ;Число повторений для цикла ;Сохранить значение в СХ /Запросить чтение одного сектора ;Адрес области ввода в памяти ;Номера дорожки и сектора /Номера устройства и головки /Вызвать обработчик прерывания /Восстановить значение в СХ /Если ошибок нет, выйти /Если есть ошибки, повторить 3 раза /Перейти к адресу 0103 /Конец фрагмента Функция ОЗН прерывания INT 13H: записать сектора Эта операция противоположна по смыслу предыдущей - она записывает данные из области памяти (размер которой кратен 512 байтам) в указанные отформатированные сектора. Загрузите в регистры те же значения, что и для функции 02Н. Успешное завершение операции сбрасывает флаг CF в 0 и помещает в AL число действительно записанных ею секторов. Содержимое регистров DS, ВХ, СХ и DX сохраняется неизменным. При возникновении ошибок флаг CF устанавливается в 1, а в регистр АН помещается код состояния; в этом случае можно сбросить состояние накопителя и повторить операцию. Программа: использование функций прерывания INT 13H для чтения секторов Программа, приведенная на рис. 19.2, обращается к прерыванию INT 13H для считывания в память секторов диска. Заметьте, что программа не открывает каких-либо файлов и не использует дескрипторы. В программе объявляются следующие области данных. • BEGINADR содержит номера начальной дорожки (03) и сектора (01), которые программа изменяет в процессе работы. • ENDADR содержит номера конечной дорожки (04) и сектора (01). Общее число секторов, которые нужно прочитать, равно 9 (на третьей дорожке) х 2 (для двух сторон) =18. Программу можно улучшить, организовав ввод начальных и конечных секторов и дорожек с клавиатуры. 476 Часть V. Усовершенствованные операции ввода/вывода
• SECTORIN определяет 512-байтовую область данных, в которой будет храниться считываемый с диска сектор. В программе используются следующие процедуры. • A10MAIN вызывает B10ADDR, С1 OREAD и D10DISPLY и завершается после чтения последнего запрошенного сектора. • B10ADDR вычисляет адрес данных на диске в виде номеров стороны, дорожки и сектора. Когда номер сектора достигает 19, программа сбрасывает его в 1. Если номер текущей стороны равен 1, программа увеличивает номер дорожки; после этого изменяется номер, с 0 на 1 или с 1 на 0. Эта процедура работает только с двухсторонними дискетами с разметкой 18 секторов на дорожку. • С10READ считывает сектор с дискеты в SECTORIN и увеличивает номер сектора, чтобы обеспечить последующие операции чтения. • D10DISPLY выводит на экран содержимое только что считанного сектора (функция 13Н прерывания INT 10H реагирует на символы перевода строки и возврата каретки) и ожидает нажатия клавиши для продолжения. TITLE BEGINADR ENDADR ENDCODE READMSG SECTORIN SIDE A10MAIN A20: A19BIORD (EXE) Чтение секторов диска средствами BIOS .MODEL .STACK .DATA DW DW DB DB DB DB .CODE PROC MOV MOV MOV MOV INT CALL MOV MOV CMP JE SMALL 64 0301H 0401H 00 ■ * * * Read 512 DUP(' 00 FAR AX,©data DS,AX ES,AX AX,0003H 10H B10ADDR / Начало: дорожка-сектор Конец: дорожка-сектор Индикатор конца работы error ***' ') CX,BEGINADR DX,ENDADR CX,DX A90 ;Область читаемых данных Инициализация сегментных регистров Установка режима и очистка экрана •Вычисление адреса сектора начало конец •Последний сектор? да, выход Рис. 19.2. Использование INT 13H для чтения секторов диска Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 477
А90: A10MAIN '' BlOADDR B20: B90: BlOADDR ' ClOREAD C90: ClOREAD CALL ' CMP JNZ LEA MOV CALL JMP MOV INT ENDP PROC MOV CMP JNE MOV CMP JE INC XOR MOV RET ENDP PROC MOV MOV LEA MOV MOV MOV INT JNC MOV LEA MOV CALL INC RET ENDP ClOREAD ENDCODE,00 A90 BP,SECTORIN ; CX,512- D10DISPLY A20 AX,4C00H 21H . Чтение с диска Удачно? нет, выход Адрес сектора и длина Вывод сектора на экран Повтор Завершение работы Вычисление следующей дорожки/сектора: NEAR CX,BEGINADR CL,19 B90 CL,01 SIDE,00 B2 0 CH SIDE,01 BEGINADR,CX Чтение дисково NEAR АН,02Н AL, 01 BX,SECTORIN CX,BEGINADR DH,SIDE DL,00 13H C90 ENDCODE,01 BP,READMSG CX, 18 DIODISPLY BEGINADR Используется СХ Получить дорожку/сектор Последний сектор уже (18)? нет, выход да, установить № сектора 1 Пропуск, если сторона 0 Увеличить номер дорожки Сменить сторону го сектора: Используются АХ, ВР, ВХ, СХ, DX Запрос чтения Число секторов Адрес буфера Дорожка/сектор Сторона Накопитель А ;Если удачно прочитали, выход ;Иначе ; вывод сообщения ; об ошибке ;Увеличить номер сектора ♦ I Рис. 19.2 (Продолжение) 478 Часть V. Усовершенствованные операции ввода/вывода
; / D10DISPLY DlODISPLY Программа вывода на экран: PROC MOV MOV MOV INT MOV INT RET ENDP END NEAR AX,1301H BX,0016H DX,0300H 10H AH,10H 16H A10MAIN ;BP, CX установлены при вызове /Запрос вывода ;Страница и атрибуты /Строка: столбец /Ожидание ввода ; с клавиатуры Рис. 19.2 (Окончание) Попробуйте выполнить эту программу под управлением DEBUG и понаблюдать за исполнением инструкций, инициализирующих сегментные регистры. Попробуйте задать номера дорожек и секторов дискеты, содержащих FAT (см. гл. 16). Используйте команду G (Go - выполнить) для выполнения программы, и просмотрите содержимое FAT и структуры директорий дискеты в SECTORIN. Программа могла бы преобразовывать ASCII-байты в шестнадцатеричные значения и выводить их в том же виде, что и DEBUG (см. программу 14.5). При этом можно просматривать содержимое любых секторов (даже скрытых), разрешить пользователю вносить изменения в прочитанные данные и записывать измененные сектора обратно на диск. Когда файл записывается функцией прерывания INT 21H, записи вставляются в свободные кластеры, которые могут не следовать непосредственно друг за другом. Поэтому не следует ожидать, что INT 13H будет последовательно считывать такой файл, хотя в этом случае можно использовать FAT для поиска следующего кластера файла. Другие операции INT 13H для работы с диском В этом параграфе рассмотрены дополнительные функции работы с диском прерывания INT 1ЗН. Функция 04Н прерывания INT 13H: проверить сектора Эта операция просто проверяет, можно ли прочитать указанные сектора, и выполняет проверку контрольных сумм CRC. Когда программа записывает на Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 479
диск данные, контроллер вычисляет для каждого сектора его контрольную сумму CRC, основываясь на значениях его битов, и записывает ее на диск. Функцию 04Н можно использовать, чтобы прочитать сектор, вновь вычислить контрольную сумму, и сравнить ее с сохраненной. Проверка состоит только в вычислении и сравнении контрольных сумм; она не проверяет, совпадают ли байты в памяти с байтами в секторе на диске. Для увеличения надежности записи эту функцию можно применить после функции записи секторов (ОЗН), хотя это приведет к увеличению времени выполнения. Загрузите в регистры те же значения, что и для функции 02Н, но устанавливать ES:DX не нужно, поскольку сравнение содержимого сектора с памятью не выполняется. Успешное выполнение операции сбрасывает флаг CF в 0 и помещает в AL число проверенных ею секторов. Содержимое регистров DS, ВХ, СХ и DX сохраняется неизменным. При возникновении ошибок флаг CF устанавливается в 1, а в регистр АН помещается код состояния; в этом случае можно сбросить состояние накопителя и повторить операцию. Функция 05Н прерывания INT 13H: форматировать дорожки Операции записи и чтения требуют информации о формате дорожек, чтобы найти заданный сектор и обратиться к нему. Эта операция форматирует дорожки по одному из четырех стандартов. Перед вызовом функции форматирования для установки типа дискеты вызовите функцию 17Н, а для установки типа носителя - функцию 18Н, рассмотренные далее в этой главе. Для форматирования дискеты загрузите в регистры следующие значения: • AL - число дорожек, подлежащих форматированию; • СН - номер цилиндра/дорожки (номера начинаются с 0); • DH - номер головки/стороны (для дискет - 0 или 1); • DL - номер устройства для дисководов (0 - А и 1 - В) или жестких дисков (С-80Н,Б-81Нит.д.); • ES:BX - адрес в области данных, указывающий на группу полей адресов для дорожки; для каждого сектора на дорожке должна быть своя 4-байтовая запись, в форме T/H/S/B, где: • Т - номер цилиндра/дорожки; • Н - номер головки/стороны; • S - номер сектора; • В - число байт на сектор (ООН - 128, 01Н - 256, 02Н - 512, ОЗН - 1024). 480. Часть V. Усовершенствованные операции ввода/вывода
Например, если форматируется дорожка 03, головка 00, с 512 байтами на сектор, первому сектору должна соответствовать запись 03000102, с соответствующими записями для каждого из последующих секторов. Операция сбрасывает флаг CF в 0 (если успешна) или устанавливает в 1 (если неуспешна), а в регистр АН помещает код состояния. Функция 08Н прерывания INT 13H: получить параметры дискового накопителя Эта полезная функция выдает информацию о дисковом накопителе. Для дисководов установите в регистре DL значение ООН (А) или 01Н (В), для диска С - 80Н, для D - 81Н и т.д. Успешное выполнение операции возвращает в регистры следующие значения: • BL - тип дискеты (01Н - 360 Кбайт, 02Н - 1,2 Мбайт, 03Н - 720 Кбайт, 5 04Н-1,44 Мбайт); • СН - номер цилиндра/дорожки (старшие разряды); • CL - биты 7—6 - старшие 2 бита номера цилиндра; биты 5—0 - номер сектора; • DH - номер головки; • DL - число устройств, подсоединенных к контроллеру; • ES:DI - для дискет - адрес (в форме сегмент:смещение) 11-байтовой таблицы параметров дисковода; два существенных элемента этой таблицы: • смещение 3 - число байт на сектор (ООН - 128, 01Н - 256, 02Н - 512, ОЗН-1024); • смещение 4 - количество секторов на дорожке. Операция устанавливает флаг CF в 0 (если успешна) или в 1 (если неуспешна) и помещает в регистр АН код состояния. Попытайтесь воспользоваться в DEBUG командой D Е8:смещение (смещение из регистра DI) и просмотрите полученные значения. Функция 09Н прерывания INT 13H: инициализировать накопитель BIOS выполняет эту функцию при загрузке компьютера, используя собственную таблицу с данными о накопителях. DL содержит номер диска (80Н или больше). Операция сбрасывает флаг CF в 0, если успешна, или устанавливает в 1, если неуспешна, и помещает в регистр АН код состояния. Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 481 31— 5-741
Функция ОАН прерывания INT 13H: прочитать расширенный буфер сектора Буфер сектора на жестких дисках содержит 512 байт данных плюс 4 байта кода коррекции ошибок (error correction code - ЕСС), используемого для испраления данных при возникновении ошибок. Эта функция может прочитать весь сектор вместе с кодом ЕСС, а не только область данных. Чтобы считать расширенный буфер, загрузите в регистры следующие значения: • AL - число секторов (вплоть до максимального для диска); • ES:BX - адрес буфера ввода (в форме сегмент:смещение); • СН - номер цилиндра/дорожки; • CL - биты 7—6: старшие биты номера цилиндра/дорожки; биты 5—0: номер сектора (старшие биты); • DH - номер головки/стороны; • DL - номер устройства (80Н или больше). Успешная операция помещает в регистр AL число действительно прочитанных секторов. Операция сбрасывает флаг CF в 0 (если успешна) или устанавливает в 1 (если неуспешна) и помещает код состояния в регистр АН. Функция ОВН прерывания INT 13H: записать расширенный буфер сектора Эта функция противоположна предыдущей - она записывает буфер сектора, включая ЕСС-данные, на диск. Функция ОСН прерывания INT 13H: перейти к цилиндру диска Эта функция помещает головку чтения/записи диска на указанный цилиндр, но не передает никаких данных. Для ее вызова загрузите в регистры следующие значения: • СН - номер цилиндра/дорожки; • CL - биты 7—6: старшие биты номера цилиндра/дорожки; биты 5—0: номер сектора (старшие биты); • DH - номер головки/стороны; • DL - номер устройства (80Н - диск С и т.д.). 482 Часть V. Усовершенствованные операции ввода/вывода
Операция сбрасывает флаг CF в 0 (если успешна) или устанавливает в 1 (если неуспешна) и помещает э регистр АН код состояния. Функция ООН прерывания INT 13H: альтернативный сброс состояния диска Эта операция аналогична операции ООН, но работает только с жесткими дисками. Загрузите номер диска (80Н или больше) в DL. Операция перемещает головку чтения/записи на цилиндр 0. Операция сбрасывает флаг CF в 0 (если успешна) или устанавливает в 1 (если неуспешна) и помещает код состояния в регистр АН. Функция ОЕН прерывания INT 13H: прочитать буфер сектора Эта функция аналогична функции ОАН, за исключением того, что она считывает с диска только 512 байт данных сектора, и не считывает ЕСС-данные. Функция 0FH прерывания INT 13H: записать буфер сектора Эта функция аналогична функции ОВН, за исключением того, что она записывает на диск только буфер сектора (без ЕСС-данных). Функции ЮН (проверить готовность диска), 11Н (перекалибровать жесткий диск), 12Н (диагностика ПЗУ), 13Н (диагностика накопителя) и 14Н (диагностика контроллера) прерывания INT 13H Эти функции проводят внутреннюю диагностику и предоставляют полученную информацию BIOS и специальным сервисным программам. Они сбрасывают флаг CF в 0 (если успешны) или устанавливают в 1 (если неуспешны) и помещают в регистр АН код состояния. Функция 15Н прерывания INT 13H: проверить тип накопителя Функция возвращает информацию об указанном накопителе. Загрузите номер исследуемого накопителя в DL (0 - для дисковода А, 1 - для дисковода В, Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 483 31*
80Н - для диска С и т.д.). Корректная операция возвращает в АН один из следующих кодов: • ООН - нет диска/накопителя; • 01Н - дисковод, не замечающий замену дискет; • 02Н - дисковод, замечающий замену дискет; • ОЗН - жесткий диск. Если АН содержит код 03 (жесткий диск), то пара регистров CX.DX будет содержать общее количество секторов на диске. Операция устанавливает флаг CF в 0 (если успешна) или в 1 (если неуспешна) и помещает в регистр АН код состояния. Функция 16Н прерывания INT 13H: изменение состояния дискеты Эта функция обнаруживает замену дискеты в системах, где эту замену можно обнаружить (см. предыдущую функцию). Загрузите номер исследуемого накопителя в DL (0 - А и т.д.). Операция помещает в АН один из следующих кодов: • ООН - дискету не меняли (флаг CF = 0); • 01Н - некорректный параметр дискеты (флаг CF = 1); • 06Н - дискету заменили (флаг CF = 1); • 80Н - дисковод не готов (флаг CF = 1). Коды состояния 01Н и 80Н указывают на ошибки и приводят к установке флага CF в 1. Однако штатный код 06Н также устанавливает флаг CF в 1, и это может служить источником недоразумений. Функция 17Н прерывания INT 13H: установка типа дискеты Эта функция устанавливает определенное сочетание дисковод/дискета. Используйте ее вместе с функцией 05Н для выполнения форматирования дискет. Загрузите номер дисковода (0 - А и т.д.) в регистр DL, а требуемое сочетание - в AL. Сочетание может быть следующим: 01Н - дискета 360 Кбайт в дисководе на 360 Кбайт, 02Н - дискета 360 Кбайт в дисководе на 1,2 Мбайта, ОЗН - дискета 1,2 Мбайта в дисководе на 1,2 Мбайта, 04Н - дискета 720 Кбайт в дисководе на 720 Кбайт, и 05Н - дискета 1,44 Мбайта в дисководе на 1,44 Мбайта. Операция устанавливает флаг CF в 0, если успешна, или в 1, если неуспешна, и помещает в регистр АН код состояния. 484 Часть V. Усовершенствованные операции ввода/вывода
Функция 18Н прерывания INT 13H: установка типа носителя перед форматированием Используйте эту функцию непосредственно перед вызовом функции 05Н для форматирования. Для установки типа носителя загрузите в регистры следующие значения: • СН - число дорожек (младшие 8 разрядов); • CL - число дорожек (старшие 2 разряда в битах 7 и 6) и число секторов на дорожку (биты 5—0); • DL - номер дисковода (0 = А и так далее). Корректная операция возвращает в пару ES:DI указатель на 11-байтовую таблицу параметров дискеты (см. функцию 08Н). Операция устанавливает флаг CF в 0 (если успешна) или в 1 (если неуспешна) и помещает в регистр АН код состояния. Функция 19Н прерывания INT 13H: парковка головок диска Старые жесткие диски при выключении оставляли головки записи/чтения н&д рабочими областями диска. Поэтому рекомендовалось принудительно парковать головки. Современнее диски автоматически паркуют головки. Операция использует номер накопителя из DL (80H или более), устанавливает флаг CF в 0 (если успешна) или в 1 (если неуспешна) и помещает в регистр АН код состояния. Ключевые моменты • Прерывание BIOS INT 13H предоставляет непосредственный доступ к дорожкам и секторам. Функции этого прерывания включают в себя установку/чтение состояния диска, чтение/запись секторов и форматирование дорожек. • Прерывание INT 13H не предоставляет автоматической поддержки директорий, файлов, блокирования и разблокирования записей. • Операция проверки секторов выполняет простейшую проверку записанных данных ценой некоторого увеличения затрат процессорного времени. • Программа должна проверять байт состояния после каждого вызова функций прерывания INT 13H. Глава 19. Дисковые накопители: функции BIOS прерывания INT 13H 485
Вопросы и упражнения 1. Каковы два основных недостатка использования прерывания BIOS INT 13H? Почему обычно предпочитают использовать прерывание INT 21H. 2. При каких обстоятельствах программисту стоит использовать прерывание INT13H? 3. Большинство операций прерывания INT 13H возвращают код состояния, (а) Куда этот код помещается? (б) Что означает код ООН? (в) Что означает код ОЗН? 4. Какова стандартная процедура, выполняемая при ошибках выполнения функций прерывания INT 13H? Как проверить, не возникла ли ошибка, и какие действия предпринять, чтобы попытаться ее устранить? 5. Напишите инструкции для сброса состояния дисковода. 6. Напишите инструкции для чтения состояния дискеты. 7. Используя дисковод А, головку 0, дорожку 4 и сектор 6, напишите инструкции, которые с помощью прерывания INT 13H считают четыре последовательных сектора в область памяти, обозначенную SECTORS. 8. Используя адрес области памяти, обозначенную SECTOR, дисковод А, головку 0, дорожку 6 и сектор 4, напишите инструкции, которыес помощью прерывания INT 13H запишут один сектор. Предварительно убедитесь, что при исполнении данного задания не повредите на дискете ценные данные. 9. Как проверить при исполнении предыдущего задания, не ведется ли запись на защищенный от записи диск? 10. Напишите инструкции для проверки корректности записи данных, выполненных в задании 8. 486 Часть V. Усовершенствованные операции ввода/вывода
Глава 20 Устройства и методы печати Цель: описать способы печати с использованием прерываний. Введение По сравнению с управлением экраном и дисками печать выглядит довольно простым процессом. В нем задействованы всего несколько операций, использующих прерывания INT 21Н или BIOS INT 17H. Специальные команды для принтера - это перевод строки (Line Feed), вывод листа (Form Feed), табуляция (Tab) и возврат каретки (Carriage Return). Принтер должен воспринимать сигналы от процессора, например, для вывода листа, перевода строки и табуляции. Процессор, в свою очередь, должен воспринимать сигналы от принтера, например, сообщающие о том, что закончилась бумага или принтер занят. К несчастью, многие принтеры по своему реагируют на сигналы процессора, и одна из задач программистов - обеспечить правильное взаимодействие программ с принтерами. В этой главе рассмотрены следующие функции для выполнения печати. Функция 40Н прерывания INT 21H - печатать символы. Функция ООН прерывания ЕМТ 17Н - напечатать символ. Функция 01Н прерывания INT 17H - проинициализировать порт. Функция 02Н прерывания INT 17Н- получить состояние порта принтера. Общие символы управления принтерами Стандартные символы, управляющие Десятичный код Шестнадцате- ричный код Функция печатью на любом принтере для PC: 09 ' 09Н Горизонтальная табуляция 10 ОАН Перевод строки (переход на следующую строку) 12 ОСН Вывод страницы (переход к следующей странице) 13 0DH ' Возврат каретки (вернуться к левому краю области печати в текущей строке) Глава 20. Устройства и методы печати 487
Горизонтальная табуляция Горизонтальная табуляция (Tab, 09H) - это символ, выполняющий перемещение головки принтера на следующую позицию табуляции в этой строке (обычно эти позиции расставлены с шагом 8 знакомест). Эта команда работает только на принтерах, поддерживающих возможность табуляции. Если у какого- либо принтера такой возможности нет, можно заменить табуляцию соответствующим числом пробелов. Перевод строки Перевод строки (Line Feed, 0AH)'- это символ, выполняющий перевод головки принтера на следующую строку. Два последовательно идущих символа перевода строки создают между строками двойной интервал. Вывод страницы Инициализация страницы при включении принтера устанавливает верхушку страницы в том месте, где в данный момент находится головка принтера. Обычная длина страницы - 11 дюймов (для формата А4), что при печати с плотностью 6 строк на дюйм позволяет вывести на страницу 66 строк. Ни процессор, ни принтер сам по себе не выполняет проверки достижения конца страницы. Используете ли вы отдельные листы бумаги для печати на лазерном принтере или непрерывную ленту (рулон) для матричного принтера - выполнение перехода на следующую страницу организуется программистом. Для управления переходом со страницы на страницу обычно ведется счет выведенных строк, и при достижении определенного их количества (например, 60) переда- < ется символ вывода страницы (ОСН), а счетчик напечатанных строк сбрасывается в 0 или в 1. Возврат каретки Символ возврата каретки (Carriage Return, 0DH) обычно используется в сочетании с символом перевода строки и вызывает возврат головки принтера к левому краю текущей строки. Этот символ соответствует клавише <Enter> или <Return> на клавиатуре и символу CR на экране. Функция 40Н прерывания INT 21Н: печать символов ' Мы уже использовали дескрипторы файлов в предыдущих главах - для работы с экраном и файлами на диске. Для выполнения печати с помощью функции 40Н прерывания INT 21H загрузите в регистры следующие значения: 488 Часть V. Усовершенствованные операции ввода/вывода
АН - 40Н (функция) СХ - количество печатаемых символов ВХ - 04 (дескриптор устройства печати) DX - адрес данных, которые нужно напечатать В следующем примере на печать выводятся 27 символов из элемента данных, названного HEADING, и печать начинается с начала строки. Символы возврата каретки (ОСН) и перевода строки (ОАН), идущие за текстом в HEADING, заставляют принтер перейти к первой позиции в следующей строке: EQU EQU DB MOV MOV MOV LEA INT ODH ОАН 'Mountain Outf AH,40H BX,04 CX,27 DX,HEADING 21H ;Возврат каретки /Перевод строки itting Corp.', CR, LF ;Запрос функции печати ;Дескриптор устройства печати /Напечатать 27 символов /Адрес данных, подлежащих печати /Вызвать обработчик прерывания Успешная операция печатает текст, сбрасывает флаг CF в 0 и помещает в АХ число напечатанных символов. При возникновении ошибок флаг CF устанавливается в 1, а в АХ помещается код ошибки 05 (в доступе отказано) или 06 (некорректный дескриптор). Символ конца файла (ОАН, вставляемый сочетанием клавиш Ctrl+Z), встреченный в передаваемой на печать последовательности символов, также вызывает завершение операции. При печати необходимо обрабатывать две возможные ситуации: 1. Не включено питание принтера. Система выводит сообщение "Write Fault Error Writing Device - Abort, Retry, Ignore, Fail? " 2. В принтере закончилась или замялась бумага. Система сообщит "Printer out of paper. Error writing device PRN" Программа: печать с переполнением страниц и заголовками Программа, приведенная на рис. 8.2 (рассмотренная в гл. 8), воспринимала ввод имен с клавиатуры и выводила их на экран. Программа, рассмотренная далее (рис. 20.1), воспринимает имена с клавиатуры и выводит их на принтер. Каждая печатаемая страйица содержит заголовок, отделенный от остального текста двойным интервалом. Имена печатаются по одному в строке, примерно так: List of customer names Page 01 Annie Hall Глава 20. Устройства и методы печати 489
Fanny Hill Danny Rose Программа подсчитывает выведенные на печать строки и при достижении конца листа выводит лист и переходит к печати на следующем. Ниже перечислены процедуры программы: • A10MAIN - вызывает B10INPUT и C10PRINT и завершает работу, если вместо ввода очередного имени пользователь просто нажмет <Enter>; • B10INPUT - выводит на экран приглашение и воспринимает имя, вводимое с клавиатуры; • C10PRINT - вызывает D ЮР AGE (если достигнут конец страницы) и печатает имя (его длина основывается на длине, указанной в списке параметров); • D ЮР AGE - выполняет переход к следующей странице, печатает заголовок, сбрасывает счетчик строк и увеличивает значение в счетчике страниц; • P10OUT - обрабатывает запросы на печать. TITLE NAMEPAR MAXNLEN NAMELEN NAMEFLD HEAD_LEN HEADING PAGECTR BOTTPAGE FORMFEED LINEFEED LINECTR PROMPT ROW *}Pfi . jod / A10MAIN A2 0PRTNM (EXE) .MODEL .STACK .DATA LABEL DB DB DB EQU, DB DB EQU DB DB DB DB DB .CODE PROC MOV MOV SMALL 64 BYTE 20 ? 20 DUP( ' 37 •List of Ввод и печать имен ;Список параметров клавиатуры: ; макс. длина имени ; реальная длина 1 ) ; введенное имя ;Строка заголовка: Customer Names Page ' •01', ODH, OAH, OAH 1 60 OCH ODH, OAH 01 •Name? ' 00 FAR AX,©data DS,AX /Прогон листа ;возврат каретки, перевод строки ;Число напечатанных строк ;Строка экрана /Инициализация ; сегментных ; регистров Рис. 20.1. Печать на нескольких страницах с заголовками MOV ES,AX ;Установка режима и MOV АХ,0003Н ; очистка экрана 490 Часть V. Усовершенствованные операции ввода/вывода
А20: АЗО: AlOMAIN / BIOINPUT BIOINPUT ; ClOPRINT C20: CALL INT CALL CALL CMP JE CALL JMP MOV LEA CALL MOV INT ENDP PROC MOV MOV LEA MOV MOV MOV INT INC MOV LEA INT RET ENDP PROC CMP JB CALL MOVZX LEA P10OUT MOV LEA 10H D10PAGE BIOINPUT NAMELEN,00 A30 ClOPRINT A20 CX,01 DX,FORMFEED P10OUT AX,4C00H 21H Заголовок страницы Ввод имени Имя введено? нет, выход да, подготовить печать Конец обработки один символ прогона бумаги, выход Ввод имени с клавиатуры: NEAR АХД301Н ВХ,0016Н ВР,PROMPT СХ,06 DH,ROW DL,10 ЮН ROW AH,0AH DX,NAMEPAR 21H Подготовка име NEAR LINECTR,BOTTPA C20 D10PAGE CX,NAMELEN DX,NAMEFLD CX,02 DX,LINEFEED Используются АХ, ВР, ВХ, СХ, Отображение подсказки •Страница и атрибуты •Адрес подсказки •Длина строки Строка экрана •Столбец •Следующая строка •Запрос ввода с клавиатуры ни к печати: s GE /Конец страницы? ; нет, пропуск DX ; да, печать заголовка ;Число символов ;Адрес имени /Печать имени /Запрос CR, LF Рис. 20.1 (Продолжение) CALL PI0OUT Глава 20. Устройства и методы печати 491
CIOPRINT / D10PAGE D30: D90: D10PAGE / PIOOUT P10OUT INC RET ENDP PROC CMP JE MOV LEA CALL MOV MOV LEA CALL INC CMP JNE MOV INC RET ENDP PROC MOV MOV INT RET ENDP END LINECTR Печать заголовка NEAR WORD PTR PAGECTR, D30 CX,01 DX,FORMFEED PIOOUT LINECTR,03 CX,HEAD_LEN DX,HEADING PIOOUT PAGECTR+1 PAGECTR+1,3AH D90 PAGECTR+1,ЗОЙ PAGECTR Программа печати NEAR AH,40H BX,04 21H A10MAIN ;Счетчик строк + 1 и номера страницы: ;Используются СХ, DX 313ОН ; Первая страница? ; да, пропустить ; нет, выполнить j прогон листа, ; сбросить счетчик стр. ;Длина заголовка ;Адрес заголовка ;Счетчик страниц +1 /Число стр. =10? ; нет, пропустить ; да, обнулить, ; увеличить десятки . ТА г4* гт г\ ттт- гз \ tv\tt\ г** а Л TJ *D*V , ylCIlUJlb ЗуЮТСЯ ±\Г1, DA ;СХ и DX уст. при вызове ;Запрос печати /Дескриптор файла Рис. 20.1 (Окончание) В начале выполнения программа должна напечатать заголовок первой страницы и не прогонять лист. Для этого D ЮР AGE пропускает вывод страницы, если PAGECTR содержит 01 (свое начальное значение). PAGECTR определен как DB ЮГ и представляется в памяти в виде ASCII-значения 3031Н. Процедура увеличивает PAGECTR на 1, так что он последовательно будет содержать значения 3032Н, 3033Н и т.д. Значения вплоть до 3039Н будут корректны, но при достижении 10-й страницы значение будет равно 303АН, что было бы выведено на печать как 0 и двоеточие. Поэтому при достижении этого значения разряд 492 Часть V. Усовершенствованные операции ввода/вывода
единиц сбрасывается в 0 (с ЗАН в ЗОН), а разряд десятков увеличивается на 1 (код 303АН преобразуется в 31 ЗОН) - на печати это число 10. Максимальное значение PAGECTR- 99. Проверка достижения конца страницы перед печатью имени (а не после) обеспечивает то, что на последней странице кроме заголовка будет напечатано хотя бы одно имя. Программу можно улучшить, например, реализовав возможность прокрутки. Программа: печать ASCII-файлов с обработкой символов табуляции Замена символов табуляции (09Н) пробелами вплоть до следующей позиции в строке, чей номер кратен 8, - обычная процедура, выполняемая, например, видеосистемой . Поэтому, например, если табуляции соответствуют позиции 8, 16, 24 и т.д., то пробелами заполнялись бы пустые места в промежутках от 0 до 7, от 8 до 15, от 16 до 23 и т.д. Однако некоторые принтеры игнорируют символы табуляции. Программа, предназначенная для печати ASCII-файлов, например; листингов программ на ассемблере, может проверять каждый римвол, передаваемый на печать. Если этот символ - 09Н (табуляция), то программа заменяет его нужным числом пробелов. Программа, приведенная на рис. 20.2, запрашивает у пользователя имя файла и выводит указанный файл на печать. Программа аналогична приведенной на рис. 17.3, выводящей на экран записи, но реагирует на символы табуляции, заменяя их пробелами. Вот пример того, как программа отреагирует на символы табуляции, встреченные в строке в позициях 1, 9 и 21, и вычислит очередную позицию табуляции: Текущая позиция в строке Двоичное значение Очистить три младших бита в 0 Прибавить 8 Следующая позиция табуляции Программа имеет следующую структуру. • Процедура A10MAIN вызывает B10PROMPT, C10OPEN, Dl 0READ и E10XFER. • B10PROMPT приглашает пользователя ввести имя файла. Нажатие только клавиши <Enter> указывает на завершение ввода данных. • C10OPEN открывает файл на диске для чтения. Если операция успешно выполнена, процедура использует функцию 42Н прерывания INT 21Н для 1 00000001 00000000 00001000 8 9 00001001 00001000 00010000 16 21 00010101 00010000 00011000 24 Глава 20. Устройства и методы печати 493
определения его размера. Воспринимаются только младшие разряды результата, и максимальное значение размера равно 65535 байтов. • Dl OREAD считывает сектор файла. v • E10XFER просматривает считанные данные в поисках признаков конца сектора, конца файла, конца области вывода, символов перевода строки и табуляции. Практически, процедура передает принтеру обычные символы и выполняет обработку символов табуляции. Процедура также определяет момент достижения конца файла, прибавляя к переменной-счетчику 1 с каждым воспринятым символом. • P10PRINT печатает строки и заполняет отпечатанные строки пробелами. TITLE NAMEPAR MAXLEN NAMELEN FILENAME FILEDTA FILESIZE PRINT_LEN COUNT ENDCODE FORMFEED HANDLE OPENMSG PRNTAREA PROMPT ROW SECTOR 7Я6 A10MAIN A20PRTAS (EXE) Чтение .MODEL .STACK .DATA LABEL DB DB DB LABEL DB DW DW DB EQU DW DW DB DW DB DB DB DB DB .CODE PROC MOV MOV MOV MOV INT SMALL 64* BYTE 32 ? 32 DUP(' ') BYTE 26 DUP(20H) 0 0 13 DUP(20H) 120 00 00 0CH 0 '* * * Open error 120 DUP(■ ') 'Name of file? 0 512 DUP (' ') FAR AX,©data DS,AX ES,AX AX,0003H ЮН и печать ASCII-записей ;Список параметров для I ; ввода ; имени файла ;DTA дискового файла /Зарезервировано ;Размер файла (мл.) размер файла (ст.) ;Остаток DTA ^~ ;Флаг конца работы *** i /Область печати i /Строка экрана /Область ввода файла /Главная процедура /Инициализация / сегментных / регистров /Установка режима, / очистка экрана Рис. 20.2. Печать ASCII-файла 494 Часть V. Усовершенствованные операции ввода/вывода
А20: А80: А90: A10MAIN / BIOPROMPT BIOPROMPT / C10OPEN MOV CALL CMP JE CALL CMP JNE CALL CMP JE CALL MOV MOV INT JMP MOV INT ENDP PROC LEA MOV CALL MOV LEA INT MOVZX MOV RET ENDP PROC MOV LEA INT JNC LEA MOV ENDCODE,00 BIOPROMPT NAMELEN,00 A90 C10OPEN ENDCODE,00 A20 D10READ ENDCODE,00 A80 E10XFER AH,3EH BX,HANDLE 21H A20 AX,4C00H 21H Запрос имени ф NEAR BP,PROMPT CX,13 F10DISPLY AH,0AH DX,NAMEPAR 21H BX,NAMELEN айла у Инициализация Запрос имени файла Есть ответ? нет, выход Открыть файл Получилось? нет, повторить запрос Чт. 1-го сектора диска Конец файла, данных нет? да, завершить Чтение/печать Закрыть файл Продолжение работы Конец работы пользователя: Используются AH, BP, BX, CX, DX i FILENAME[BX],ООН Открытие файла NEAR AX,3D00H DX,FILENAME 21H C20 BP,OPENMSG CX,18 : ■Адрес подсказки Длина строки •Получение имени файла с клавиатуры Вставка нуля в конце имени файла Используются AX, BP, CX, DX Открыт ь только для чтения Проверка флага переноса, если установлен, вывод сообщения Рис. 20.2 (Продолжение) Глава 20. Устройства и методы печати 495
С20: С90: ClOOPEN ' DIOREAD DIOREAD / ElOXFER E20: E30: 4 E40: CALL MOV JMP MOV MOV LEA INT MOV MOV LEA INT RET ENDP PROC MOV MOV MOV LEA INT MOV RET ENDP PROC . CLD LEA LEA MOV LEA CMP JNE CALL CMP JE LEA MOV CMP JB FIODISPLY ENDCODE,01 C90 HANDLE,AX ; AH,1AH DX,FILEDTA 21H AH,4EH CX,0 DX,FILENAME 21H Чтение сектора NEAR AH,3FH BX,HANDLE CX,512 DX,SECTOR 21H ENDCODE,AX Преобразование NEAR SI,SECTOR DI,PRNTAREA COUNT,00 DX,SECTOR+512 SI,DX E40 DIOREAD ENDCODE,00 E90 SI,SECTOR BX,COUNT BX,PRINT_LEN E50 об ошибке и выход Сохранение дескриптора Установка DTA Поиск файла и получение его размера диска: Используются АН, BX, CX, DX Запрос чтения устройства Длина Буфер данных в строку печати: •Используются AL, BX, DI, DX, SI •Слева направо •Инициализация ;Конец сектора? ; нет, пропуск ; да, чтение следующего ;Конец файла? ; да, выход ;Конец'Области печати? ; нет, пропуск Рис. 20.2 (Продолжение) 496 Часть V. Усовершенствованные операции ввода/вывода
Е50: Е60: Е70: Е90: ElOXFER ' FIODISPLY FIODISPLY ' PI0PRINT MOV CALL LEA MOV LODSB MOV MOV INC DEC JZ CMP JNE CALL JMP CMP JNE DEC MOV AND ADD MOV JMP MOV MOV CALL RET ENDP PROC MOV MOV MOV MOV INT INC RET ENDP PROC MOV [DI+BX], ODOAH Pi0PRINT DI,PRNTAREA COUNT,00 BX,COUNT [DI+BX],AL BX FILESIZE E90 AL,0AH E60 PI0PRINT E2 0 AL,09H E7 0 BX BYTE PTR [DI+BX] BX,0FFF8H BX, 08 COUNT,BX E3 0 BX,COUNT BYTE PTR [DI+BX] PI0PRINT Программа вывода NEAR АХД301Н BX,0016H DH,ROW DL, 10 10H ROW Печать строки NEAR AH,40H и , , - 2 ОН , ОСН да, установить CR/LF Реинициализация [SI] в AL, INC SI Символ в строку печати Все символы? да, выход Прогон строки? нет, пропуск да, печать Символ табуляции? нет, пропуск да, сброс ВХ Симв. таб. -> пробел Очистка 3-х младших битов, +8 для позиции табуляции Конец файла Прогон листа •Печать последней строки ;Используются АХ, BX, DX ;ВР, СХ уст. при вызове ;Запрос вывода ;Атрибуты стр. ;Строка ;Столбец ;След. строка экрана заполнение ее пробелами: ;Исполь ;Запрос зуются АХ, ВХ, СХ, DI, DX печати Рис. 20.2 (Продолжение) Глава 20. Устройства и методы печати 497 32 — 5-741
PI0PRINT MOV MOV INC LEA INT MOV MOV LEA REP RET ENDP END BX,04 CX, COUNT CX DX,PRNTAREA 21H AL,20H CX,PRINT_LEN DI,PRNTAREA STOSB A10MAIN ;Длина строки ;Очистка строки Рис. 20.2 (Окончание) Можно модифицировать программу, добавив счет отпечатанных строк и вывод листа при достижении, например, 60-й строки. Кроме того, с помощью редактора можно вставить символы прогона листа непосредственно в печатаемый файл с некоторыми промежутками, например, в концах процедур. Это можно сделать, например, нажав и удерживая клавишу Alt, набрать 012 на цифровой клавиатуре. Специальные символы управления печатью В предыдущем параграфе было рассмотрено применение обычных символов управления печатью: табуляции (Tab), перевода строки (LF - Line Feed), возврата каретки (CR - Carriage Return) и вывода (прогона) страницы (FF - Form Feed). Есть еще несколько символов, воспринимаемых большинством принтеров: Действие Забой (Backspace) Вертикальная табуляция (Vertical Tab) Включить расширенный (expanded) шрифт Включить сжатый (condensed) шрифт Выключить расширенный шрифт Выключить сжатый шрифт Некоторые команды принтера (Escape-последовательности) представляют собой символ Esc (1BH), за которым следует символ команды: Десятичный код 08 11 14 15 18 20 Шестнадцате- ричный код 08 ов 0Е 0F 12 14 498 Часть V. Усовершенствованные операции ввода/вывода
1ВН ЗОН Установить межстрочный интервал (line spacing) 8 строк на дюйм 1ВН 32Н Установить межстрочный интервал (line spacing) 6 строк на дюйм 1ВН 45Н Включить режим печати с выделением (emphasized mode) 1ВН 46Н Выключить режим печати с выделением Эти символы можно передавать принтеру двумя способами: 1. Определять их в области данных. Следующая строка устанавливает сжатый шрифт с 8 строками на дюйм, печатает заголовок, вызывает перевод строки и возврат каретки: HEADING DB OFH, 1BH, ЗОН, 'Mountain Outfitting Corp.', ODH, OAH 2. Воспользоваться функцией 40Н, чтобы передать принтеру управляющие символы: CONDMODE DB 0FH ; Сжатый шрифт MOV АН,40Н /Запросить печать MOV ВХ, 04 /Дескриптор устройства MOV СХ,01 /Количество печатаемых символов MOV DX,CONDMOD /Сжатый шрифт INT 21H /Вызвать обработчик прерывания Все последующие символы будут печататься в сжатом шрифте, пока программа не пошлет команду на отмену этого режима. Функции печати BIOS прерывания INT17Н Прерывание INT 17Н предоставляет функции выполнения печати на уровне BIOS. Порты принтера LPT1 (используемый по умолчанию), LPT2 и LPT3 обозначаются соответственно 0, 1 и 2. INT 17H предусматривает три функции, выбираемые по значению в регистре АН. 1. Сначала вызывается функция 02Н, определяющая состояние принтера, подключенного к порту с указанным номером. Используйте эту функцию каждый раз перед попыткой печати. 2. Если принтер доступен, то вызывается функция 01Н, инициализирующая порт принтера. 3. Затем вызывается функция ООН, передающая символы принтеру. Эти функции возвращают байт состояния принтера в регистре АН. Структура этого байта следующая: Глава 20. Устройства и методы печати 499 32*
Бит Значение Бит Значение О Тайм-аут (Time out) 5 Нет бумаги (Out of paper) 3 Ошибка ввода/вывода 6 Подтверждение от принтера (Input/output error) (Acknowledged from printer) 4 Выбран (Selected) 7 He занят (Not busy) Если принтер включен и готов к печати, операция вернет в АН код 90Н (двоичное 10010000): принтер не занят и выбран, т.е. находится в рабочем состоянии. Об ошибках принтера свидетельствуют установленные в 1 биты 5 (нет бумаги) и 3 (ошибка ввода-вывода). Если принтер не включен, операция возвращает ВОН или двоичное 10110000, индицируя "отсутствие бумаги". Функция ООН прерывания INT17H: напечатать символ Эта функция выводит на печать один символ и позволяет обращаться к портам принтерным 0, 1 или 2. Загрузите подлежащий печати символ в регистр AL, а номер принтерного порта - в DX: MOV АН,ООН /Запросить печать MOV AL,символ ;Печатаемый символ MOV DX, 00 ;Принтерный порт 0 INT 17H ;Вызвать обработчик прерывания Эта операция возвращает байт состояния в регистре АН. Обычно перед ее вызовом рекомендуется использовать функцию 02Н для проверки состояния принтера. Функция 01Н прерывания INT17H: проинициализировать принтерный порт Эта операция выбирает порт, сбрасывает состояние принтера и инициализирует принтер для приема данных. В следующем фрагменте используется порт 0: MOV АН,01Н ;Запросить инициализацию принтера MOV DX, 00 ;Принтерный порт 0 INT 17H ;Вызвать обработчик прерывания Поскольку операция посылает на принтер символ Form Feed (прогон листа), ее можно использовать для установки принтера на начало нового листа, хотя большинство принтеров делают это автоматически при включении. Операция возвращает байт" состояния в регистре АН. Функция 02Н прерывания INT17H: прочитать состояние принтерного порта Эта операция предназначена для определения состояния принтера. В следующем фрагменте используется порт 0: 500 Часть V. Усовершенствованные операции ввода/вывода
MOV AH,02H ;Запросить чтение из порта MOV DX,00 ;Принтерный порт 0 INT 17H /Вызвать обработчик прерывания TEST АН,00101001В /Готов к печати? JNZ сообщение ; нет, вывести сообщение Операция возвращает состояние устройства так же, как и функция 01Н. При выполнении программы, если принтер изначально не включен, BIOS не может автоматически сообщить об этом - программа должна самостоятельно проверить состояние принтера и действовать соответственно результатам проверки. Если программа не проверяет состояние, единственная индикация ее работы - мигание курсора. Если вы включите принтер в момент, когда программа пытается вести печать, часть переданных на принтер данных может быть утеряна. Следовательно перед выполнением операций печати с помощью BIOS нужно проверить состояние порта; если есть ошибки, нужно вывести сообщение об этом (обработчик прерывания ЕМТ 21Н выполняет такую проверку автоматически, но выдаваемое им сообщение "Out of paper" относится к различным ситуациям). Когда принтер включен, сообщение не появляется и печать начинается обычным образом и без потерь данных. В любой момент в принтере может кончиться бумага или он может быть случайно выключен. Если вы пишете программу, предназначенную для использования другими людьми, включайте в нее проверку состояния принтера перед печатью. Ключевые моменты • Обычные символы управления принтером - горизонтальная табуляция (Horizontal Tab), перевод строки (Line Feed), возврат каретки (Carriage Return) и прогон листа (Form Feed). • После завершения печати используйте символы перевода строки или прогона листа для очистки буфера принтера. • Функция 40Н прерывания INT 21H печатает строку символов, а функции BIOS INT 17H позволяют печатать символы по одному. • При возникновении ошибок печати система выводит сообщение, хотя BIOS возвращает только код состояния принтера. При использовании функций ENT 17H примените функцию 02Н, чтобы проверить состояние принтера перед тем, как печатать. Глава 20. Устройства и методы печати 501
Вопросы и упражнения 1. Укажите, какие символы выполняют функцию (а) возврата каретки, (б) перевода строки, (в) прогона листа, (г) горизонтальной табуляции. 2. Напишите программу, использующую функцию 40Н прерывания INT 21H для выполнения следующих действий: (а) прогона листа для начала печати с нового листа, (б) печати вашего имени, (в) выполнения возврата каретки и перевода строки, а затем печати вашего адреса, (г) возврата каретки, перевода строки и печати ваших города и страны, (д) прогона листа. 3. Модифицируйте программу из предыдущего вопроса так, чтобы имя печаталось в разреженном режиме, адрес - в уплотненном, а город и страна - с выделением. 4. Модифицируйте программу из вопроса 2 так, чтобы она печатала тексты соответственно пунктам (б), (в) и (г) по 5 раз. 5. Модифицируйте программу из вопроса 3 так, чтобы печатаемые строки одновременно выводились на экран. 6. Определите строку заголовка, которая позволяет выполнить перевод строки и возврат каретки, устанавливает уплотненный режим печати, определяет заголовок (произвольное имя) и сбрасывает уплотненный режим печати. 7. Функции печати прерывания INT 17H возвращают байт состояния принтера в регистре АН. Что означают байты (а) 08Н? (б) ЮН? (в) 90Н? 8. Перепишите программу из вопроса 2 с использованием INT 17H. Включите в нее проверку состояния принтера. 502 Часть V. Усовершенствованные операции ввода/вывода
Часть VI Специальные темы Глава 21. Определение и использование макросов Глава 22. Связь с подпрограммами Глава 23. Загрузка программы и оверлеи
Глава 21 Определение и использование макросов Цель: рассмотреть порядок определения и использования макросов. Введение Для каждой символьной инструкции в программе на языке ассемблера транслятор генерирует одну инструкцию машинного кода. Однако в языках высокого уровня - например, С или BASIC - одной инструкции соответствует несколько инструкций машинного кода. С этой точки зрения языки высокого уровня можно рассматривать как набор макро команд. Ассемблер содержит механизмы, позволяющие вам создавать и использовать в программах макросы. Вы указываете для макроса уникальное имя, а также набор инструкций ассемблера, составляющих его тело. После этого вместо использования в программе всего этого набора инструкций просто укажите имя макроса, и ассемблер автоматически подставит на его место соответствующую последовательность инструкций. Макросы применяются: • для упрощения создания программ и уменьшения необходимости многократно повторять одни и те же последовательности инструкций: • для уменьшения количества ошибок, неизбежно возникающих при многократном повторении одних и тех же фрагментов; • для упрощения понимания программ на ассемблере. С помощью макросов можно реализовать, например, операции ввода/вывода, загружающие значения в регистры и вызывающие обработчики прерываний, а также функции, преобразующие данные из ASCII-формата в двоичный, циклические арифметические операции с многоразрядными операндами, инициализацию устройств и процедуры обработки строк. Макросы можно сохранить в единой библиотеке, из которой они позже будут вызываться программами. Программа может использовать макрос один единственный (например, для инициализации сегментных регистров) или множество раз (например, для вывода данных на дисплей). Библиотека обычно также содержит процедуры. Выбирая способ оформления некоторого повторяющегося фрагмента программы (в виде макроса или в виде процедуры), следует руководствоваться 504 Часть VI. Специальные темы
следующими соображениями: макрос обычно исполняется быстрее аналогичной ему процедуры, поскольку в нем отсутствуют инструкции вызова и возврата. Использование процедур, однако, приводят к созданию более компактного кода, поскольку однажды созданная процедура вызывается множество раз из различных точек программы. Обычно макросы используются в случаях, если закодированная в нем последовательность инструкций не используется много раз и довольно проста. Вот общий формат определения макроса: Имя макроса MACRO [список параметров] | [инструкции] ENDM /Определение макроса ;Тело макроса ;Конец определения макроса Директива MACRO в первой строке указывает ассемблеру на то, что следующие инструкции, вплоть до директивы ENDM, принадлежат к телу макроса. Директива ENDM (от "END Macro") завершает определение макроса. Инструкции между директивами MACRO и ENDM составляют тело макроса. Для включения макроса в программу сначала необходимо его определить или скопировать из библиотеки макросов. Определения макросов располагаются в тексте программы перед кодом всех сегментов. Простые определения макросов Рассмотрим определение простейшего макроса, инициализирующего сегментные регистры для программы типа .ЕХЕ: INITZ MACRO MOV AX,©data MOV DS,AX MOV ES,AX ENDM Определение макроса Тело определения макроса Конец определения макроса Имя этого макроса - INITZ. Элементы данных, используемые в этом макросе - АХ, DS, ES и @data - должны быть определены где-то в программе или доведены до сведения ассемблера каким-то другим способом. В дальнейшем макроинструкцию INITZ можно использовать в сегменте кода каждый раз, когда вам нужно проинициализровать сегментные регистры. Когда ассемблер обнаруживает в листинге макроинструкцию INITZ, он просматривает список символьных инструкций и, не обнаружив в нем такой инструкции, проверяет макроинструкции. Поскольку программа содержит определение макроса INITZ, ассемблер подставляет вместо имени тело макроса, создавая макрорасширение. Макрос INITZ используется в программе единственный раз, но, вообще, макросы могут использоваться любое количество раз, и каждый раз, обнаруживая их в листинге, ассемблер будет генерировать макрорасширение. Глава 21. Определение и использование макросов 505
Вот определение еще одного макроса, названного FINISH, выполняющего нормальное завершение программы: FINISH MACRO /Определение макроса MOV АХ,4С00Н ;Запросить завершение работы INT 21H /Вызвать обработчик прерывания ENDM ;Конец определения макроса На рис. 21.1 приведен листинг оттранслированной программы, определяющей и использующей макросы INITZ и FINISH. Использовавшаяся версия ассемблера указывает, что данная инструкция представляет собой часть макрорасширения, указывая 1 слева от инструкции. 0000 0000 0000 0003 0005 0007 0009 000D 000F 0012 0014 54 66 6F В8 8Е 8Е В4 8D CD В8 CD 65 73 74 20 20 6D 61 63 0D 0А 24 R 1 D8 1 СО 1 09 16 0000 R 21 4С00 1 21 1 TITLE INITZ FINISH / 6F MESSGE 72 BEGIN BEGIN A21MACR1 Простые макроопределения MACRO MOV MOV MOV ENDM MACRO MOV INT ENDM ; AX,©data ; DS,AX ES, AX ; ; AX,4C00H ; 21H / .MODEL SMALL .STACK 64 .DATA DB .CODE PROC INITZ MOV MOV MOV MOV LEA INT Определение макроса Инициализация сегментных регистров Конец макроопределения Определение макроса Конец работы Конец макроопределения Test of macro?, 13, 10, '$' | FAR AX,©data DS,AX ES,AX АН,09Н DX,MESSGE 21H FINISH MOV INT ENDP END AX,4C00H 21H BEGIN ,\Вызов макроса /Инициализация ; сегментных ; регистров ;Запрос вывода ; сообщения ;Конец работы Рис. 21.1. Простые макрокоманды 506 Часть VI. Специальные темы
Заметьте, что сами по себе определения макросов при трансляции не приводят к созданию какого-либо объектного кода. Кроме того, макрорасширение не выводит на печать директивы вроде ASSUME или PAGE, закодированные в определении макроса. Вряд ли стоит возиться с определением макроса, который будет использоваться только раз, но этот макрос можно ввести в состав библиотеки, которую вы будете использовать при создании программ. Далее в главе будет рассмотрено создание библиотек макросов и их использование в программах. Использование параметров в макросах Для того чтобы сделать макрос более универсальным, можно определить параметры в операндах в виде формальных параметров. Ниже приведено определение макроса PROMPT, предназначенного для вывода на экран сообщений с помощью функции 09Н прерывания INT 21H: PROMPT MACRO MESSGE /Формальный параметр MOV АН,09Н LEA DX,MESSGE INT 21H ENDM ;Конец макроса При использовании этой макроинструкции вы должны задать имя сообщения, указывающее на строку, завершающуюся знаком доллара ($). Кроме того, макрос мог бы содержать инструкции, сохраняющие в стеке содержимое регистров АН и DX и извлекающие его из стека. Формальный параметр в определении макроса указывает ассемблеру, что любое обращение к этому имени в тексте макроса должно рассматриваться ассемблером, как обращение к соответствующему параметру. Например, в последнем примере имя MESSGE есть в операнде инструкции LEA. Предположим, что программа содержит определение строки MESSAGE2 следующего вида: MESSAGE2DB 'Enter the date as iran/dd/yy','$' Если вы хотите вывести эту строку на экран с помощью макроса PROMPT, задайте имя MESSAGE2 в качестве параметра при вызове макроса: PROMPT MESSAGE2 Параметр (MESSAGE2) в макровызове заменяет формальный параметр (MESSGE) в исходном определении макроса: Определение макроса: PROMPT MACRO MESSGE (формальный параметр или аргумент) Вызов макроса: PROMPT MESSAGE2 (параметр) Глава 21. Определение и использование макросов 507
Ассемблер уже обнаружил упоминание формального параметра в операнде инструкции LEA. Теперь он подставит параметр макроинструкции MESSAGE2 на место формального параметра в макрорасширении, созданном при обнаружении вызова макроса с этим параметром. Определение макроса и примеры его вызовов показаны на рис. 21.2. Про-, грамма также определяет и использует ранее рассматривавшиеся макросы INITZ и FINISH. TITLE A21MACR2 (EXE) Использование INITZ MACRO PROMPT FINISH MESSG1 MESSG2 BEGIN PROC BEGIN MOV MOV MOV ENDM MACRO MOV LEA INT ENDM MACRO MOV INT ENDM .MODEL .STACK .DATA DB DB .CODE FAR INITZ параметров Макроопределение AX,©data J DS,AX ES,AX MESSGE АН,09Н DX,MESSGE 2lH AX,4C00H 21H SMALL 64 'Name? ' , ' 'Address? ' , ' PROMPT MESSG1 PROMPT MESSG2 FINISH ENDP END BEGIN Инициализация сегментных регистров Конец макроопределения Макроопределение Запрос вывода сообщения Конец макроопределения Макроопределение Конец работы -Конец макроопределения $' $' Рис. 21.2. Использование параметров в макросах В качестве формального параметра можно использовать любое корректное имя, включая и имена регистров, например, СХ. В определение макроса можно включать любое количество формальных параметров, разделяя их запятыми, 508 Часть VI. Специальные темы
пока не будет достигнута максимальная длина строки (она зависит от версии ассемблера). Ассемблер заменяет формальные параметры параметрами макровызова последовательно, слева направо. Использование комментариев в макросах В состав макросов можно включать комментарии, определяющие их назначение. Точка с запятой или директива COMMENT указывает на строку комментария. В следующем фрагменте для выделения комментариев используются точки с запятыми: PROMPT MACRO MESSGE ; Этот макрос позволяет выводить на экран сообщения MOV АН,09Н ;Запросить вывод LEA DX,MESSGE ; сообщения INT 21Н ENDM Поскольку по умолчанию в листинг включаются только инструкции, генерирующие объектный код, ассемблер не включает в листинг макрорасширения комментарий, содержащийся в определении макроса. Чтобы комментарий включался в каждое макрорасширение, используйте директиву управления листингом .LALL (List ALL - вывести все) перед вызовом макроинструкций: .LALL PROMPT MESSAGE1 Определение макроса может содержать произвольное количество комментариев, но можно сделать так, что в листинг макрорасширения будут включены только некоторые из них. Так же используйте директиву .LALL, но ставьте две точки с запятой (;;) перед комментариями, которые всегда должны подавляться при выводе (по умолчанию ассемблер использует директиву .XALL, вызывающую вывод в листинг только инструкций, для которых генерируется объектный код). С другой стороны, вы можете захотеть, чтобы в листинг не включалось само тело макроса на месте макровызова, особенно в случае, если этот макрос вызывается в программе несколько раз. Тогда используйте директиву .SALL (Suppress ALL - подавить все), уменьшающую размер листинга, но не оказывающую влияния на размер создаваемого объектного кода. Директива управления листингом действует до тех пор, пока в тексте программы не встречается другая директива. В программе можно разместить несколько таких директив, чтобы некоторые макросы добавляли в листинг только инструкции, создающие объектный код (.XALL), некоторые - и объектный код Глава 21. Определение и использование макросов 509
и комментарии (.LALL), а некоторые - ни объектного кода, ни комментариев (.SALL). В MASM 6.0 появились синонимы .LISTMACROALL, .LISTMACRO и .NOLISTMACRO для .LALL, .XALL и .SALL, соответственно. TITLE A21MACR3 (EXE) Использование .LALL & .SALL INITZ MACRO PROMPT ; ; FINISHMACRO MESSG1 MESSG2 / BEGIN PROC BEGIN MOV MOV MOV ENDM MACRO Макроопределение AX,@data DS,AX ES,AX MESSGE Этот макрос выводит Данные MOV LEA INT ENDM MOV INT ENDM .MODEL .STACK .DATA DB DB .CODE FAR .SALL INITZ (•Инициализация сегментных ; регистров ;Конец макроопределения любое сообщение следует завершать $-ограничителем AH,09H DX,MESSGE 21H AX,4C00H 21H SMALL 64 'Name? 'Address? ' , PROMPT MESSG1 .LALL PROMPT MESSG2 .SALL FINISH ENDP END BEGIN /Запрос вывода ; сообщения ;Макроопределение ;Конец работы ;Конец макроопределения у •$• ■$■ Рис. 21.3. Управление печатью макрорасширений В программе, приведенной на рис. 21.3, продемонстрировано сказанное выше. Она использует макросы INTTZ, PROMPT и FINISH. В сегменте кода содержится 510 Часть VI. Специальные темы
директива .SALL, подавляющая вывод в листинг расширений INITZ и FINISH и первого расширения PROMPT. Для второго расширения PROMPT использована директива .LALL, и ассемблер включит в листинг и комментарии, и инструкции этого расширения. Однако заметьте, что в определении макроса перед одним из комментариев стоят две точки с запятой и, следовательно, он не добавляется в листинг. Вложенные макросы Определение макроса может содержать ссылку на макросы, определенные ранее. Это называется вложением (nesting) макросов. Рассмотрим следующие два определения макросов SET_CURSOR и DISPLAY: SET_CURSOI DISPLAY Я. MACRO MOV MOV MOV MOV INT ENDM MACRO MOV AH LEA DX ROW,COL AH,02H BX,0 DH,ROW DL,COL 10H MESSGE , 09H ,MESSGE INT- 21H ENDM Третий макрос, названный CURS_DISPLAY, может вызывать макросы SETCURSOR и DISPLAY. CURS_DISPLAY определяет параметры для обоих вложенных макросов следующим образом: CURS_DISPLAY MACRO M_ROW,M_COL,MESSGE PUSHA SET_CURSOR M_ROW,M_COL DISPLAY MESSGE POPA ENDM Макрос CURS_DISPLAY можно вызывать с константами, переменными или регистрами в качестве параметров, например, CURS_DISPLAY 6,15,ERROR_MSSGE ;Константы CURS_DISPLAY ROW,COLUMN,ERROR_MSSGE ;Переменные CURS_DISPLAY CH, CL, ERRORJtfSSGE ; Регистры Глава 21. Определение и использование макросов 511
Макродирективы Ассемблер поддерживает ряд директив для работы с макросами, включая LOCAL, PURGE, директивы повторения и условные директивы. Директива LOCAL Некоторые макросы требуют определения элементов данных и меток перехода непосредственно в определении макроса. Однако если вы вызовете такой макрос более одного раза в одной и той же программе, дублирование меток и определений элементов данных в листинге вызовет ошибки трансляции. Чтобы все создаваемые при вызовах таких макросов имена были уникальными, укажите директиву LOCAL непосредственно после директивы MACRO (если в макроопределении есть комментарии, то директива LOCAL должна быть расположена перед ними): LOCAL имя!, имя2, В фрагменте программы, представленном на рис. 21.4, показано применение директивы LOCAL. Макрос DIVIDE выполняет деление методом последовательных вычитаний. Макрос вычитает делитель из делимого и прибавляет 1 к частному, пока делимое не станет меньше делителя. В макросе используются две метки: СОМР для перехода в цикле и OUT для выхода из процедуры по завершении. И СОМР, и OUT определяются как LOCAL и могут представлять собой любые корректные имена. Программа вызывает макрос DIVIDE дважды. В первый раз для СОМР генерируется символьная метка ??0000, а для OUT - ??0001. Во второй раз - соответственно ??0002 и ??0003. Таким образом, обеспечивается уникальность всех генерируемых в программе меток. Подключение макросов из библиотек Обычно в программе макросы объявляются однажды, а затем многократно вызываются из текста программы. Поэтому часто макросы объединяются в ^отдельный файл библиотеки под некоторым именем, например, MACRO.LBY. Просто объедините определения всех ваших макросов в один или несколько файлов на диске: INITZ MACRO ENDM PROMPT MACRO MESSGE ENDM 512 Часть VI. Специальные темы
TITLE DIVIDE / COMP: OUT: DIVDNDl DIVSORl QUOTNT1 DIVDND2 DIVSOR2 QUOTNT2 A21MACR4 (EXE) MACRO LOCAL LOCAL DIVIDEND COMP OUT Использование директивы LOCAL ,DIVISOR,QUOTIENT | AX - делимое, ВХ - MOV MOV SUB CMP JB SUB INC JMP MOV ENDM .DATA DW DW DW DW DW DW .CODE DIVIDE DIVIDE AX,DIVIDEND BX,DIVISOR CX,CX AX,BX OUT AX,BX CX COMP QUOTIENT 150 27 •p 265 34 ? DIVDNDl, DIVDND2, ,CX делитель, CX - частное Установка делимого Установка делителя Очистка частного Делимое < делитель? да, выход Делимое - делитель Частное +1 •Сохранение частного •Конец макроопределения • Делимое •Делитель •Частное •Делимое •Делитель ;Частное DIVSORl, QUOTNT1 DIVSOR2,QUOTNT2 Рис. 21.4. Использование директивы LOCAL Для создания такого файла можно использовать редактор или текстовый процессор, но убедитесь, что файл будет записан в неотформатированном ASCII-виде. В последующих примерах подразумевается, что файл библиотеки ' называется MACRO.LBY. Программы могут вызвать макросы, хранящиеся в этом файле, без необходимости определять их директивой MACRO в начале текста, если файл будет подключен к программе директивой INCLUDE: INCLUDE путь : \MACRO . LBY Ассемблер обращается к файлу MACRO.LBY и включает в текст программы все макросы, определенные в этом файле, хотя в программе могут быть задействованы только некоторые из них. Оттранслированный листинг будет содержать копию определений макросов. Некоторые версии трансляторов обозначают их буквой С в 30-м столбце LST-файла. Глава 21. Определение и использование макросов 513 33 —5-741
Для двухпроходных ассемблеров, чтобы вызвать выполнение директивы INCLUDE только при первом проходе (вместо обоих), можно вызвать директиву следующим образом: IF1 INCLUDE путь:\MACRO.LBY ENDIF IF1 и ENDIF - это условные директивы. IF1 указывает транслятору обращаться к указанной библиотеке только во время первого прохода трансляции. ENDIF завершает блок, на который оказывает влияние последняя директива IF. При этом копия определений макросов уже не включается в листинг - это уменьшает как его размер, так и время трансляции. Однако MASM с версии 6.0 уже не требует наличия директив, различающих два прохода. Место расположения директивы INCLUDE не критично, но она должна предшествовать любому вызову макроса из подключаемого ей файла. Директива PURGE Выполнение директивы INCLUDE заставляет ассемблер включить в листинг определения всех макросов из указанной библиотеки. Предположим, однако, что библиотека содержит макросы INITZ, FINISH, PROMPT и DIVIDE, но в текущей программе требуются только макросы INITZ и FINISH. Директива PURGE позволяет вам удалить ненужные макросы PROMPT и DIVIDE из листинга: IF1 INCLUDE - путь : \MACRO.LLBY /Включить всю библиотеку ENDIF PURGE PROMPT,DIVIDE ;Удалить ненужные макросы INITZ ;Использовать оставшийся макрос Директива PURGE влияет только на программу, в которой используется, а не на файл библиотеки. Конкатенация Символ амперсанта (&) указывает ассемблеру слить (конкатенировать, присоединить) текст или символы. В следующем примере макроса амперсант позволяет создавать инструкции MOVSB, MOVSW или MOVSD: STRMOVE MACRO TAG REP MOVS&TAG ENDM Пользователь может вызывать этот макрос как STRMOVE В, STRMOVE W или STRMOVE D. При этом ассемблер соединит параметр В, W или D с 514 Часть VI. Специальные темы
инструкцией REP MOVS, и получится инструкция REP MOVSB, REP MOVSW или REP MOVSD, соответственно. (Этот довольно примитивный пример включен в иллюстративных целях). Директивы повторения Директивы повторения REPT, ГОР и IRPC заставляют ассемблер повторять блок инструкций вплоть до завершающей определение директивы ENDM (в MASM 6.0 появились синонимы REPEAT, FOR и FORC для REPT, IRP и IRPC, соответственно). Эти директивы не обязательно присутствуют в определении MACRO, но если они есть, то одна директива ENDM обозначает завершение повторяемого участка, а вторая - завершение тела макроса в целом. Директива REPT: повторение Директива REPT (или REPEAT) указывает ассемблеру повторять блок инструкций вплоть до директивы ENDM указанное количество раз: REPT выражение В следующем примере инструкция DEC повторяется 4 раза: REPT 4 DEC SI ENDM В следующем примере значение элемента данных N сначала инициализируется нулем, после чего генерирование инструкции DB N повторяется 5 раз: N = 0 REPT 5 N = N + 1 DB N ENDM Операция генерирует 5 выражений DB: DB .1, DB 2, DB 3, DB 4 и DB 5. Директиву REPT можно использовать, например, чтобы определять таблицу или ее часть. Ниже приведен макрос, использующий директиву REPT для пятикратной подачи звукового сигнала: BEEPSPKR MACRO MOV АН,02Н ;Запросить вывод MOV DL,07 ;Символ звукового сигнала REPT 5 INT 21H ;Вызвать обработчик прерывания ENDM ;Конец участка REPT ENDM ;Конец тела макроса Глава 21. Определение и использование макросов 515 зз*
Директива IRP: повторение подстановки Эта директива заставляет ассемблер повторять блок выражений вплоть до ENDM. Ее формат таков: IRP параметр, Выражения ENDM <аргументы> Аргументы, заключенные в угловые скобки, состоят из любого числа корректных символов, включая строковые или числовые константы. Ассемблер заменяет параметр выражением для каждого аргумента. Например: IRP N,<3,9,17,25,28> DB N ENDM Ассемблер сгенерирует DB 3, DB 9, DB 17, DB 25 и DB 28. Еще один пример: IRP REG <АХ, ВХ, СХ, DX> PUSH REG ENDM Ассемблер сгенерирует инструкцию PUSH для каждого указанного регистра. Директива IRPC: повторение подстановки символа Эта директива заставляет ассемблер повторять блок выражений вплоть до ENDM. Ее формат таков: IRPC параметр, Выражение ENDM строка , Ассемблер сгенерирует выражение для каждого символа строки. Например: IRPC N,345678 DW N ENDM Ассемблер сгенерирует 6 определений - от DW 3 до DW 8. Условные директивы Ранее в этой главе условная директива IF1 использовалась, чтобы заставить ассемблер подключать файл библиотеки только при первом проходе трансляции. Условные директивы наиболее полезны при определении макросов, но их применение не ограничивается только этой областью. Каждой условной директиве должна соответствовать директива ENDIF, завершающая блок, на который 516 Часть VI. Специальные темы
оказывает влияние директива IFnn. Внутри блока необязательная единственная директива ELSE может указывать действие при невыполнении условия. Условия проверяются в значительной степени так же, как и в языке С. Вот формат условных директив семейства IFnn: IFnn {условие) ELSE (необязательно) Г условный блок ENDIF {конец условного блока) Отсутствие соответствующей директивы ENDIF вызовет сообщение об ошибке транслятора - что-нибудь вроде "Unexpected end of conditional block" ("Неожиданный конец условного блока"). Если ассемблер сочтет, что условие, заданное в начале блока, выполняется, он обработает инструкции вплоть до директивы ELSE, а если директива ELSE отсутствует - то до директивы ENDIF. Если условие не выполняется, то обрабатывается блок от ELSE до ENDIF; если ELSE отсутствует, не обрабатываются никакие инструкции. Далее перечислены различные условные директивы и их значение. IF выражение: если выражение возвращает истинное (ненулевое) значение, то выражения в условном блоке подлежат трансляции. IFE выражение: если выражение возвращает не истинное (нулевое) значение, то выражения в условном блоке подлежат трансляции. EF1 (выражения нет): инструкции в условном блоке обрабатываются только при первом проходе транслятора. IF2 (выражения нет): инструкции в условном блоке обрабатываются только при втором проходе транслятора. IFDEF символ: если символ определен в тексте программы или объявлен со спецификатором EXTRN, обработать инструкции в условном блоке. IFNDEF символ: если символ не определен в тексте программы или не объявлен со спецификатором EXTRN, обработать инструкции в условном блоке. IFB <аргумент>: если аргумент пуст, обработать инструкции из условного блока; аргумент заключается в угловые скобки. IFNB <аргумент>: если аргумент не пуст, обработать инструкции из условного блока; аргумент заключается в угловые скобки. IFIDN <аргумент1>,<аргумент2>: если аргументы равны, обработать инструкции из условного блока; аргументы заключаются в угловые скобки. IFDIF <аргумент1>,<аргумент2>: если аргументы не равны, обработать инструкции из условного блока; аргументы заключаются в угловые скобки. Глава 21. Определение и использование макросов 517
FINISH MACRO MOV IFNB MOV ELSE MOV ENDIF INT ENDM RETCODE AH,4CH <RETCODE> AL,RETCODE AL,00H 21H Директивы IF и IFE используют операторы соотношений EQ (BQual - равно), NE (Not Equal - не равно), LT (Less Than - меньше, чем), LE (Less than or Equal - меньше или равно), GT (Greater Than - больше, чем) и GE (Greater than or Equal - больше или равно), например, в выражении IF выражение1 EQ выражение2 Вот простой пример использования директивы IFNB (если не пусто). Функция 4СН прерывания INT 21H позволяет программе завершить работу и вернуть код выхода в регистре AL. В приведенном ниже фрагменте ранее созданный макрос FINISH модифицирован так, чтобы возвращать код выхода: ;Запросить завершение выполнения ;Код возврата ;Код возврата по умолчанию ;Вызвать обработчик прерывания Далее рассмотрен еще один пример использования IFNB. Довольно часто в процедурах с помощью инструкции PUSH сохраняются в стеке начальные значения регистров, а по завершении процедуры они извлекаются из стека инструкциями POP. Число и размер сохраняемых регистров зависят от процедуры. Приведенные ниже макросы PUSHMAC и РОРМАС обрабатывают один или два регистра, но их легко адаптировать под другое количество: PUSHMAC MACRO REGl,REG2 IFNB <REG1> PUSH REGl ENDIF IFNB <REG2> PUSH REG2 ENDIF ENDM POPMAC MACRO REGl,REG2 IFNB <REG1> POP REGl ENDIF IFNB <REG2> POP REG2 ENDIF ENDM Эти макросы вызываются как 518 Часть VI. Специальные темы
PUSHMAC AX,BX POPMAC BX,AX Директива EXITM Определение макроса может содержать условную директиву, которая проверяет выполнение критически важного условия. Если это условие выполняется, ассемблер может выходить из любого дальнейшего расширения данного макросе с помощью директивы EXITM: IFxx [условие] (не выполняется) EXITM ENDIF Если транслятор встречает в расширении макроса директиву EXITM, он прерывает расширение и продолжает трансляцию текста за директивой ENDM. Директиву EXITM можно также использовать для завершения директив REPT, IRP и IRPC, даже если они содержатся в определении макроса. Макросы с использованием условий IF и IFNDEF Фрагмент программы, приведенный на рис. 21.5, содержит определение макроса DIVIDE, выполняющего деление методом последовательных вычитаний. Пользователь может использовать этот макрос, указав в качестве параметров имена делимого, делителя и частного, причем, именно в такой последовательности. IFNDEF используется в макросе, чтобы проверить, действительно ли объявлены в программе указанные в качестве параметров элементы. Если какой- либо элемент не объявлен, то макрос увеличивает значение элемента данных CNTR (вместо CNTR можно использовать любое корректное имя - этот элемент используется только временно в определении макроса). После проверки трех параметров макрос проверяет, не равен ли нулю CNTR: IF CNTR ;Расширение макроса прервано EXITM ENDIF Если значение CNTR отличается от 0, ассемблер генерирует указанный в приведенном фрагменте комментарий и выходит (EXITM) из расширения макроса. Заметьте, что первая инструкция макроса устанавливает CNTR в 0, а блок IFNDEF может просто устанавливать CNTR в 1, а не увеличивать его значение раз за разом. Глава 21. Определение и использование макросов 519
TITLE DIVIDE / / ; ; ; COMP: OUT: 90c . . DIVDEND DIVISOR QUOTENT A21MACR5 (EXE)Использование IF и IFNDEF| MACRO LOCAL LOCAL CNTR DIVIDEND,DIVISOR,QUOTIENT | COMP OUT = 0 AX = делимое, ВХ = IFNDEF CNTR END IF IFNDEF CNTR END IF IFNDEF CNTR ENDIF IF EXITM ENDIF PUSHA MOV MOV SUB CMP JB SUB INC iTMP MOV POPA ENDM .DATA DW DW DW DIVIDEND делитель, СХ = частное Делимое не ука = CNTR + 1 DIVISOR Делитель не = CNTR + 1 QUOTIENT зано указан 1 Частное не указано = CNTR + 1 CNTR - Макрорасширение прервано АХ,DIVIDEND ВХ,DIVISOR СХ,СХ АХ,ВХ OUT АХ,ВХ СХ СОМР QUOTIENT,CX 150 27 •р i i i i Сохранение регистров Делимое Делитель Очистка частного Делимое < делитель? да, выход Делимое - делитель Частное +1 Сохранение частного Восстановление регистров ■ Делимое ■Делитель •Частное Рис. 21.5. Использование директив IF и IFNDEF 520 Часть VI. Специальные темы
.' CODE .LALL DIVIDE DIVIDE DIVDEND DIDND DIVISOR DIVISOR QUOTENT QUOT Рис. 21.5 (Окончание) Если все проверки прошли успешно, ассемблер генерирует макрорасширение. В сегменте кода второй вызов DIVIDE содержит неправильное указание имен делимого и частного, поэтому вместо расширения будет сгенерирован только комментарий. Макрос можно улучшить, например, встроив в него проверку делителя на отличие от нуля и совпадение знаков делимого и делителя. Для этого придется использовать инструкции ассемблера, а не директивы, поскольку эти проверки нужно проводить во время выполнения программы, а не ее трансляции. Макрос с директивой IFIDN Фрагмент программы, приведенный на рис. 21.6, содержит определение макроса MOVIF, генерирующего инструкции MOVSB или MOVSW в зависимости от заданного параметра. При вызове макроса пользователь должен задавать параметр В (для байтов) или W (для слов), чтобы указать, нужно ли преобразовать MOVS в MOVSB или MOVSW. В макросе директива IFIDN применяется два раза: IFIDN <&TAG>,<B> REP MOVSB IFIDN <&TAG,<W> REP MOVSW Первый блок IFIDN генерирует инструкцию REP MOVSB в случае, если макрос вызван как MOVIF В, а второй - инструкцию REP MOVSW в случае, если макрос вызван как MOVIF W. Если макрос вызвали, не указав в параметре ни В, ни W, ассемблер генерирует комментарий и по умолчанию создает инструкцию REP MOVSB (для выполнения слияния используется оператор &). Три вызова макроса MOVIF в сегменте кода демонстрируют все возможные случаи. Не пытайтесь выполнять программу в том виде, в котором она приведена в книге, поскольку не определены соответствующие элементы данных и не проинициализированы регистры SI и DI. Практически этот макрос совершенно бесполезен и создан в демонстративных целях. Однако с помощью условных директив можно создавать полезные и довольно сложные макросы. Глава 21. Определение и использование макросов 521
TITLE A21MACR6 (EXE) Использование IFIDN MOVIF MACRO TAG Макроопределение IFIDN <&TAG>,<B> REP MOVSB ;Пересылка байтов EXITM ENDIF IFIDN <&TAG>,<W> REP MOVSW /Пересылка слов ELSE ; Ни В ни W не указаны, по умолчанию используем В REP MOVSB /Пересылка байтов ENDIF ENDM ;Конец макроса .CODE MOVIF В MOVIF W MOVIF Рис. 21.6. Использование директивы IFIDN Ключевые моменты • Определение макроса требует использования директивы MACRO, за которой будет следовать блок из одной или более инструкций, известный как тело макроса. Оно завершается директивой ENDM. • Макроинструкция - это применение макроса в программе. Код, генерируемый для макроинструкции - это расширение макроса. • Директивы .SALL, .LALL и .XALL управляют включением в листинг комментариев и объектного кода, генерируемого в макрорасширении. • Директива LOCAL позволяет использовать имена в определении макроса и должна фигурировать непосредственно после строки с директивой MACRO. • Использование формальных параметров в определении макроса позволяет пользователям передавать макросам параметры, делая их более гибкими. • Библиотека макросов содержит упорядоченные определения макросов, доступные для других программ. • Условные директивы позволяют программам, например, проверять параметры макросов. 522 Часть VI. Специальные темы
Вопросы и упражнения 1. При каких условиях лучше использовать (а) макрос, (б) процедуру? 2. Напишите первую и последнюю строки определения простейшего макроса MACARONI. 3. Объясните различие между телом определения макроса и телом расширения макроса. 4. Что такое формальный параметр? 5. Напишите директивы для следующих действий: (а) включать в листинг только инструкции, генерирующие объектный код, (б) подавить включение в листинг всех инструкций, генерируемых макросами. 6. Напишите определения двух макросов, генерирующих код для умножения: (a) MPYBYTE - для умножения байта на байт, (б) MPYWORD - для умножения слова на слово. Включите множители и произведения в определение макроса в виде формальных параметров. Проверьте макросы на работоспособность, применив их в программе, определяющей соответствующие данные. 7. Поместите макросы из вопроса 6 в библиотеку макросов. Модифицируйте программу из того же вопроса так, чтобы макросы подключались к ней директивой INCLUDE при первом проходе транслятора. 8. Напишите макрос с именем BIOSPRINT, использующий функции прерывания BIOS INT 17H для печати. Макрос должен проверять состояние принтера перед печатью и должен позволять печатать строки любой длины на принтере, подключенном к любому порту. 9. Перепишите макрос, приведенный на рис. 21.4, так, чтобы он генерировал код, позволяющий пропускать операцию деления при нулевом делителе (на стадии выполнения программы). 10. Напишите, оттранслируйте и проверьте работу программы, использующей макросы MPYBYTE, MPYWORD и BIOSPRINT. (а) Определите два элемента данных размерности байт и два байта (слово), заполнив их числовыми данными, (б) Используйте макрос MPYBYTE для перемножения байтов, и MPYWORD - для перемножения слов, (в) Преобразуйте результаты в ASCII-форму и воспользуйтесь макросом BIOSPRINT, чтобы вывести их на печать. 11. Определите, какие условные директивы надо применить, чтобы выполнить инструкции в условном блоке при следующих условиях: (а) если аргумент пуст, (б) если выражение равно 0, (в) если два аргумента равны, (г) если выражение не равно 0. Глава 21. Определение и использование макросов 523
Глава 22 Связь с подпрограммами Цель: рассмотреть методы совместной трансляции, компоновки и выполнения раздельных программ. Введение , Все программы, рассматривавшиеся в этой книге ранее, состояли из одного автономного исполняемого модуля. Однако можно создать программу, состоящую из основной программы, связанной с одной или несколькими отдельно существующими подпрограммами. Вот основные цели разделения программы на отдельные исполняемые модули: • для упрощения создания программы на нескольких языках программирования одновременно - например, для облегчения написания основной части программы на языке высокого уровня, а критичных по скорости выполнения элементов - на языке ассемблера; • для упрощения параллельной разработки отдельных частей программы различными людьми; • для разбиения большой программы на части и облегчения ее загрузки и выполнения. Каждая программа транслируется отдельно и создает собственный объектный файл .OBJ. После этого компоновщик собирает эти объектные файлы в общий исполняемый файл .ЕХЕ. Обычно основная программа - это та, с которой начинается исполнение. Из нее вызываются остальные модули. На рис. 22.1 показаны два примера иерархии, состоящей из основной программы и трех подпрограмм. На рис. 22.1(a) основная программа непосредственно обращается к каждой из трех подпрограмм. На рис. 22.1(6) основная программа обращается непосредственно только к двум подпрограммам, а одна из этих подпрограмм вызывает третью подпрограмму. Есть множество способов организации подпрограмм, но все эти способы должны быть понятны транслятору и компоновщику. Необходимо также отслеживать ситуации, в которых, например, подпрограмма 1 вызывает подпрограмму 2,'вызывающую подпрограмму 3, а подпрограмма 3 вызывает подпрограмму 1. Этот механизм называется рекурсией и иногда применяется, #но ошибки при его реализации могут привести к трудно устранимым ошибкам. 524 Часть VI. Специальные темы
1 П/прогр ' >амма-1 Главная программа 4 П/программа-2 1 ' П/программа-3 Главная программа г^ П/программа-1 j П/программа-3 4 П/программа-2 Рис. 22.1. Иерархия программ Директива SEGMENT В этом параграфе рассматривается применение директивы SEGMENT и назначение некоторых ее опций. Полный формат директивы SEGMENT таков: Имя сегмента SEGMENT [Выравнивание] [Совмещение] ['класс'] Выравнивание (align), совмещение (combine) и типы классов описаны ниже. Выравнивание Опция выравнивания (если она задана) указывает на то, что сегмент должен выравниваться по определенной границе в памяти. • BYTE - по границе байта (произвольный адрес). Это выравнивание используется, если подпрограмма должна сочетаться с другими. • WORD - по границе слова (четный адрес). Это выравнивание используется, если подпрограмма должна сочетаться с другими. • DWORD - по границе двойного слова (адрес нацело делится на 4). Это выравнивание обычно используется для программ, использующих процессор 80386 или более новые. • PARA - по границе параграфа (адрес нацело делится на 16 или ЮН). Это выравнивание используется по умолчанию и наиболее распространено. Оно используется и в основных программах, и в подпрограммах. • PAGE - по границе страницы (адрес нацело делится на 256 или 100Н). Если опция выравнивания отсутствует, то сегмент выравнивается по границе параграфа. Отсутствие его в последующих инструкциях объявления сегментов также приводит к выравниванию по границе параграфа, если имя сегмента уникально; если имя сегмента уже встречалось, то используется тип выравнивания, ранее указанный для сегмента с этим именем. Глава 22. Связь с подпрограммами 525
Совмещение Опция совмещения (если она присутствует) указывает ассемблеру и компоновщику, следует ли объединять сегменты, или оставить их раздельными (вы уже использовали тип совмещения STACK для программ типа .ЕХЕ). В этой главе также рассматриваются типы совмещения NONE, PUBLIC и COMMON. • NONE. Этот сегмент должен быть логически отделен от остальных, хотя физически они могут оказаться совпадающими. Этот тип используется по умолчанию в полных сегментных директивах. • PUBLIC. Компоновщик должен объединить этот сегмент со всеми другими сегментами с этим типом совмещения и имеющими то же имя и класс. Ассемблер вычисляет смещения от начала первого сегмента. Фактически объединенный сегмент содержит ряд разделов, каждый из которых начинается с директивы SEGMENT, и заканчивается директивой ENDS. Тип PUBLIC используется по умолчанию в упрощенных сегментных директивах. • COMMON. Если сегменты этого типа имеют одно и то же имя и класс, компо- , новщик укажет для них один и тот же начальный адрес. При выполнении второй сегмент перекроет первый. Сегмент наибольшего размера определит общий размер выделенной области. Класс Вы уже использовали имена классов "Stack", "Data" и "Code". Можно назначать одно и то же имя класса связанным сегментам, чтобы транслятор и компоновщик сгруппировали их вместе. При этом они будут следовать друг за другом в памяти, но не будут объединены, если для них не указан тип совмещения PUBLIC. Имя класса может быть любым корректным с точки зрения ассемблера, но имя "Code" рекомендуется использовать для сегмента кода. Два последующих независимых выражения SEGMENT дают одинаковый результат - независимый сегмент кода, выровненный по границе параграфа: CODESGl SEGMENT PARA NONE 'Code' CODESG2 SEGMENT •Code' Директивы идентичны, поскольку по умолчанию выбирается выравнивание по параграфам (PARA) и совмещение NONE. Полностью определенные сегментные директивы рассматривались в гл. 4, но в последующих главах применялись упрощенные сегментные директивы. Поскольку полные сегментные директивы обеспечивают лучший контроль над трансляцией и компоновкой программ, они используются в большинстве примеров этой главы. 526 Часть VI. Специальные темы
Примеры программ в этой и последующих главах иллюстрируют различные применения опций выравнивания, совмещения и классов. Вызовы внутри сегмента Инструкции CALL, использовавшиеся до этого момента, выполняли вызовы внутри сегмента, т.е. вызываемая ими процедура находилась в том же сегменте, что и вызывавшая. Вызов инструкцией CALL внутри сегмента принадлежит к типу near, если вызываемая процедура определена или обозначена как NEAR (находится в пределах 32К-смещения). Программы, определенные моделями Tiny, Small и Compact, для обращений к внутренним процедурам используют по умолчанию пеаг-вызовы. Инструкция CALL при near-вызове сохраняет текущее значение IP в стеке и помещает в регистр IP смещение, по которому начинается вызываемая процедура. Поэтому вызов near-процедуры может производиться только в том же сегменте: CALL имя ;Вызов типа near - сохраняем в стеке IP и ... ; переходим к процедуре имя PROC NEAR RET/RETN ;Возврат из процедуры типа near - извлекаем имя ENDP ; из стека IP и возвращаемся к точке вызова Рассмотрим внутрисегментный вызов процедуры инструкцией CALL. Соответствующий машинный код - Е8 20 00. Здесь Е8 - код инструкции, а 20 00 (00 20) - смещение вызываемой процедуры. Операция сохраняет текущее значение IP в стеке и помещает 20 00 в виде 00 20 в IP. После этого процессор, сочетая текущий адрес сегмента в регистре CS со смещением в IP, получает адрес следующей исполняемой инструкции. При выходе из вызванной процедуры типа near инструкция RET извлекает из стека сохраненное значение обратно в IP - при этом после возвращения из процедуры выполняется следующая за CALL инструкция. Внутрисегментные вызовы могут быть типа near или far - если процедура определена как far в том же сегменте. Инструкция RET будет типа near, если процедура, в которой она используется, сама принадлежит к этому типу, и типа far, если процедура - типа far. Можно явно указывать инструкцию RETN или RETF. Глава 22. Связь с подпрограммами 527
Межсегментные вызовы Инструкция CALL будет отнесена к типу far, если вызванная процедура определена как FAR или EXTRN. Обычно (но не всегда) такие процедуры расположены в других сегментах кода. Программы, созданные с использованием моделей Medium и Large, используют по умолчанию вызовы типа far. Вызов типа far сначала сохраняет в стеке значение регистра CS и помещает в CS адрес начала нового сегмента кода. Затем в стек помещается значение регистра IP, а в регистр IP помещается новое значение смещения (сохраненные в стеке значения CS и IP вместе указывают на адрес инструкции, следующей за выполненной инструкцией CALL). Так сохраняются значения CS и IP для дальнейшего восстановления при возврате из процедуры. Вызов, направленный в другой сегмент - это всегда межсегментный вызов типа far: CALL имя ;Вызов типа far - Сохраняем в стеке CS и'1Р, и ... ; переходим к процедуре имя PROC FAR RET/RETF /Возврат из процедуры типа far - извлекаем имя ENDP ; из стека CS и IP и возвращаемся к точке вызова Рассмотрим инструкцию CALL типа far. Ее объектный код следующий: 9А 00 02 AF 04. Шестнадцатеричный код инструкции 9АН соответствует инструкции CALL типа far, 00 02 (или 02 00) - адресу смещения в новом сегменте, а AF 04 (или 04 AF) - адресу начала нового сегмента. Операция помещает в стек значение IP, а затем помещает в регистр IP значение 0002 из памяти, переставив его байты в виде 02 00. Затем в стек помещается значение из CS, а в регистр CS из памяти заносится слово AF 04 в виде 04 AF. После этого процессор вычисляет адрес следующей подлежащей исполнению инструкции, суммируя адрес из регистра CS с адресом из регистра IP, сдвигая первый на 4 двоичных разряда влево: Адрес в сегменте кода (регистр CS): 04AF0H Смещение (регистр IP): ч + 0200Н Эффективный адрес: 04CF0H При выходе из вызванной процедуры межсегментная инструкция RET (или RETF) типа far проводит обратные действия, извлекая значения IP и CS из стека обратно в соответствующие регистры. После этого пара CS:IP будет указывать адрес инструкции, следующей за выполненной инструкцией CALL. Выполнение программы продолжится с этой точки. Основное различие между вызовами процедур типа near и типа far заключается в том, что вызов типа near заменяет только значение в регистре IP, а типа far - и в CS, и в IP. Инструкция RETN используется для возврата из процедур типа near, a RETF - типа far. 528 Часть VI. Специальные темы
Атрибуты PUBLIC и EXTERN На рис. 22.2 основная программа (MAINPROG) вызывает подпрограмму (SUBPROG). Два модуля транслируются раздельно. Здесь необходим межсегментный вызов процедуры. MAINPROG MAINPROG SUBPROG SUBPROG EXTRN PROC CALL ENDP PUBLIC PROC RETF ENDP SUBPROG:FAR FAR SUBPROG SUBPROG FAR Рис. 22.2. Межсегментный вызов CALL в MAINPROG должен знать, что имя SUBPROG определено за пределами MAINPROG (в противном случае транслятор выдаст сообщение, что SUBPROG - неопределенный символ). Директива EXTRN SUBPROG:FAR указывает ассемблеру, что любой вызов SUBPROG относится к метке типа FAR, которая в данном случае определена в отдельном модуле. Поскольку ассемблер не может узнать, какой адрес будет соответствовать этой метке во время выполнения программы, он создает пустой операнд в объектном коде для инструкции CALL (созданный при трансляции файл листинга .LST будет содержать нули в качестве смещения и дефисы (—) вместо имени сегмента: 9А0000 Е ;Е означает External (внешний адрес) Компоновщик в дальнейшем должен заполнит эти пробелы. В свою очередь SUBPROG содержит директиву PUBLIC, указывающую транслятору и компоновщику, что другие модули должны знать адрес SUBPROG. В дальнейшем, когда и MAINPROG и SUBPROG уже будут оттранслированы в объектные файлы, их можно будет совместно скомпоновать в единый исполняемый файл командой LINK n:MAINPROG+n:SUBPROG,n:,CON Компоновщик устанавливает соответствия между директивами EXTERN в одном модуле и PUBLIC в другом и вставляет в места, где это необходимо, соответствующие адреса смещений. После этого он совмещает два объектных Глава 22. Связь с подпрограммами 529 34 — 5-741
модуля в один исполняемый. Если какие-либо ссылки остались незаполненными, компоновщик выдаст соответствующие сообщения. Прежде чем пытаться выполнить полученный при компоновке исполняемый модуль, лучше убедиться в отсутствии таких сообщений. Директива EXTRN/EXTERN Директива EXTRN указывает ассемблеру, что соответствующий элемент - элемент данных, метка процедуры или метка в программе - определены в другом файле (в MASM 6.0 для EXTRN появился синоним EXTERN). Ее формат следующий: EXTRN имя:тип [, ...] В одной строке можно указать более одного имени (вплоть до максимально допустимой длины строки) или применить несколько директив EXTRN. В другом ассемблерном модуле им должны соответствовать такие же объявления в директиве PUBLIC. Тип должен быть корректен с точки зрения действительных определений имен: • ABS определяет константное значение; • BYTE, WORD и DWORD определяют элементы данных, к которым обращаются из этого модуля, но определенные в другом модуле; • NEAR и FAR обозначают имена процедур, к которым обращаются из этого модуля, но определены они в другом модуле; • имя может определяться с помощью директивы EQU. Директива PUBLIC Директива PUBLIC указывает транслятору и компоновщику, что адрес указанного символа в текущем файле должен быть доступен для других модулей. Формат этой директивы таков: 'PUBLIC СИМВОЛ [, В одной строке можно указать более одного символа (вплоть до максимально допустимой длины строки) или применить несколько директив PUBLIC. Символ может быть меткой (включая метки процедур PROC), переменной или числом. В качестве символов нельзя использовать имена регистров и определять (директивой EQU) значения размером больше двух байт. Вызовы процедур типа far и использование директив EXTR>J и PUBLIC не должны порождать особых трудностей, хотя необходима осторожность при определении в модуле данных, которые будут доступны другим модулям. 530 Часть VI. Специальные темы
В следующих параграфах рассмотрены два способа предоставления данных другим программам: использование EXTRN и PUBLIC и передача параметров. Использование EXTRN и PUBLIC для точки входа На рис. 22.3 показаны две взаимосвязанные, но отдельно транслируемые, программы - основная (A22MAIN1) и подпрограмма (A22SUB1). Обе эти программы используют полные сегментные директивы. В основной программе определены сегменты кода, данных и стека. В сегменте данных определены элементы QTY и PRICE. Инструкции в сегменте кода загружают величину PRICE в регистр АХ, a QTY - в ВХ. После этого управление передается подпрограмме. Директива EXTRN в основной программе указывает точку входа в подпрограмму как A22SUB1 :FAR. 0000 0000 0080 0000 0000 0040[????] 0140 0002 ^500 0004 0000 0000 0000 0003 0005 0008 ооос ООН 0014 0016 0016 В8 8Е А1 8В 9А В8 CD R D8 0002 R IE 0000 R оооо 4С00 21 TITLE STACKSEG STACKSEG DATASEG QTY PRICE DATASEG A22MAIN1 (EXE) Вызов подпрограммы EXTRN A2 2 SUB1:FAR SEGMENT PARA STACK 'Stack' DW 64 DUP(?) ENDS SEGMENT PARA 'Data' DW 0140H DW 2500H ENDS / 1 CODESEG SEGMENT PARA 'Code' BEGIN PROC FAR ASSUME CS:CODESEG,DS:DATASEG,SS:STACKSEG MOV MOV MOV MOV E CALL MOV INT BEGIN ENDP CODESEG ENDS END AX,DATASEG DS,AX AX,PRICE /Установка цены BX,QTY ; и количества A22SUB1 /Вызов подпрограммы АХ,4С00Н /Конец обработки 21Н BEGIN Рис. 22.3. Вызов подпрограммы: использование EXTRN и PUBLIC Глава 22. Связь с подпрограммами 531 34*
Segments and Name CODESEG DATASEG STACKSEG ... Symbols: Name A22SUB1 BEGIN PRICE QTY 0000 0000 0000 F7 E3 0002 CB 0003 0003 Segments and Name CODESG . . . Symbols: Name A22SUB1 . . Groups: Length 0016 0004 0080 Type L FAR F PROC L WORD L WORD TITLE CODESG Align PARA PARA PARA Value 0000 0000 0002 0000 Combine Class NONE 'CODE' NONE 'DATA' STACK 'STACK' Attr External CODESEG Length =0016 DATASEG DATASEG A22SUB1 Вызываемая подпрограмма SEGMENT PARA 'Code' A22SUB1 PROC FAR ASSUME CS:CODESG PUBLIC A22SUB1 | IMUL RETF A22SUB1 ENDP CODESG Groups: Length . . . 0003 Type . . F PROC ENDS END Align PARA Value 0000 BX ;AX - цена, ВХ - количество ;DX:AX - продукт A22SUB1 Combine Class NONE 'CODE' Attr CODESG Global Length = 0003 Рис. 22.3 (Окончание) Подпрограмма A22SUB1 после ASSUME содержит директиву PUBLIC, указывая компоновщику, что ее имя должно быть доступно другим программам в качестве входной точки для начала выполнения. Сама подпрограмма просто умножает содержимое АХ на содержимое ВХ, помещая в пару регистров DX:AX произведение в форме 00 2Е 40 ООН. Поскольку в подпрограмме не определяются никакие элементы данных, нет нужды определять сегмент данных. Если бы такой сегмент был определен, данные из него были бы доступны только этой подпрограмме. Подпрограмма использует адреса в регистрах SS и SP, помещенные в них основной программой. В результате подпрограмма не содержит определения стека, пользуясь стеком, определенным в основной программе. Поскольку 532 Часть VI. Специальные темы
компоновщику в программе типа .ЕХЕ необходим хотя бы один определенный стек, в качестве такового выступает стек, определенный в A22MAIN1. Теперь рассмотрим таблицы символов для обеих программ, приведенные на рис. 22.3. Заметьте, что в таблице символов для программы A22MAIN1 символ A22SUB1 указан как Far и External. Среди символов программы A22SUB1, приведенной после ее листинга, этот символ указан как F (т.е. Far) и Global (глобальный). Глобальный - это символ, имя которого доступно для ссылок из других программ. Ниже приведена карта связей для скомпонованных модулей, показывающая организацию программы в памяти. Заметьте, что в программе используется один сегмент данных и один - стека, но два сегмента кода (по одному из каждого использованного объектного файла). У этих сегментов кода разные начальные адреса, поскольку для них указан тип совмещения NONE. Эти сегменты размещаются в той последовательности, в какой их объектные файлы были указаны в командной строке компоновщика. В данном примере сегмент кода основной программы (обычно первый) начинается со смещения 00090Н, а сегмент кода подпрограммы - со смещения 000B0H: Объектные модули: A22MAIN1 + A22SUB1 Начало ОООООН 00080Н 00090Н ооовон Конец 0007FH 00083Н 000А5Н 000В2Н Длина 00080Н 00004Н 00016Н 00003Н Имя STACKSEG DATASEG CODESEG CODESG Класс STACK DATA CODE CODE Точка входа программы - 0009:0000 Отслеживание выполнения программы выявило, что регистр CS для программы A22MAIN1 содержал 0F20[0]H, а инструкции CALL A22SUB1 соответствовал машинный код 9А 00 00 22 0F (у вас значение адреса сегмента может отличаться от этого). Машинный код для межсегментной инструкции CALL равен 9АН. Операция помещает значение из IP в стек и помещает в IP значение 0000 (первый операнд инструкции CALL). После этого в стек помещается значение CS, а в CS записывается 220FH в виде 0F22H (здесь содержимое регистров показывается в виде с непереставленными байтами). Пара CS:IP указывает на следующую подлежащую исполнению инструкцию - ее смещение 0F22[0]H плюс 0000Н. Что расположено по этому адресу? Это входная точка подпрограммы A22SUB1, ее первая исполняемая инструкция. Основная программа начиналась со значением в CS, равным 0F20[0]H. Согласно карте связей, смещения в основном сегменте кода начинаются с точки 00090Н, а смещения в сегменте кода подпрограммы - с точки ОООВОН - расстояние Глава 22. Связь с подпрограммами 533
между этими двумя точками равно 20Н байтов. Суммирование значения регистра CS и 20Н дает нам реальный адрес в сегменте кода подпрограммы: Адрес в сегменте кода A22MAIN1 (регистр CS): 0F200H Размер сегмента кода A22MAIN1: +00020Н Реальный адрес для подпрограммы: 0F22 0H Именно так загрузчик определяет эффективный адрес и помещает его в качестве операнда в инструкцию CALL. Подпрограмма A22SUB1 перемножает значения в регистрах АХ и ВХ, помещает произведение в пару регистров DX:AX, и инструкцией RETF возвращает управление в A22MAIN1 (эта инструкция выполняет возврат управления из процедуры типа far). Определение сегмента кода как PUBLIC На рис. 22.4 приведены программы, уже рассмотренные нами в предыдущем параграфе (рис. 22.3), но несколько модифицированные. Одно из изменений касается основной программы, теперь обозначенной A22MAIN2, а второе - подпрограммы A22SUB2. В обеих программах в директиве SEGMENT, объявляющей сегменты кода, используется ключевое слово PUBLIC: CODESEG SEGMENT PARA PUBLIC 'Code' Такая модификация существенно изменяет таблицу связей результирующего исполняемого файла и машинный код используемых инструкций CALL. 0000 0000 0080 0000 0000 0002 0004 0000 0000 0000 0003 0005 0008 0040 0140 2500 В8 8Е А1 8В [ ????] / R D8 0002 R IE 0000 R TITLE STACKSEG STACKSEG DATASEG QTY PRICE DATASEG CODESEG BEGIN A22MAIN2 (EXE) Вызов подпрограммы EXTRN A22SUB2:FAR SEGMENT PARA STACK 'Stack' DW ENDS 64 DUP(?) SEGMENT PARA 'Data' DW DW ENDS 0140H 2500H SEGMENT PARA PUBLIC 'Code' PROC ASSUME MOV MOV MOV MOV FAR CS:CODESEG,DS:DATASEG,SS:STACKSEG AX,DATASEG DS,AX AX, PRICE /Установка цены BX.QTY ; и количества Рис. 22.4. Вызов подпрограммы: сегмент кода типа PUBLIC 534 Часть VI. Специальные темы
ОООС 9А ООН В8 0014 CD 0016 0016 Segments Nam CODESEG DAT AS EG STACKSEG Symbols: Nam A22SUB2 . BEGIN . PRICE . . QTY . . . 0000 0000 0000 F7 0002 CB 0003 0003 Segments Nam CODESEG Symbols: Nam A22SUB2 0000 E 4C00 21 BEGIN CALL MOV INT ENDP CODESEG ENDS and Groups: e Length 0016 0004 0080 e Type ... L FAR ... F PROC ... L WORD ... L WORD TITLE CODESEG A22SUB2 E3 A22SUB2 CODESEG and Groups: e Length , 0003 e Type . . . . F PROC END Align PARA PARA PARA Value 0000 0000 0002 0000 A22SUB2 AX,4C00H 21H BEGIN Combine PUBLIC NONE STACK Attr External CODESEG DATASEG DATASEG A22SUB2 Вызываемая Вызов подпрограммы Конец обработки Class 'CODE' 'DATA' 'STACK' Length = 0016 подпрорамма SEGMENT PARA PUBLIC 'Code' PROC FAR ASSUME CS:CODESEG PUBLIC A22SbB2 IMUL RETF ENDP ENDS END ^lign PARA tfalue 0000 BX ;AX - цена, ВХ - количество ;DX:AX A22SUB2 - произведение Combine Class PUBLIC 'CODE' Attr CODESEG Global Length = 0003 Рис. 22.4 (Окончание) В таблице символов, приведенной вслед за каждым листингом, для сегмента CODESEG указан тип совмещения PUBLIC, в то время как на рис. 22.3 показан тип NONE. Кроме того, в карте связей указан только один сегмент кода. Поскольку в обеих программах сегменты кода названы CODESEG, имеют одинаковый класс ('Code') и атрибут PUBLIC, компоновщик совмещает два логических сегмента кода в один физический сегмент кода. Вот карта связей: Глава 22. Связь с подпрограммами 535
Объектные модули: A22MAIN2 + A22SUB2 Начало Конец .Длина 00000Н 0007FH 00080Н 00080Н 00083Н 00004Н 00090Н 000В2Н 00023Н Точка входа программы - 0009:0000 Имя STACKSEG DATASEG CODESEG Класс STACK DATA CODE Кроме того, отслеживание выполнения программы выявило, что CALL соответствует машинная инструкция 9АН - это вызов типа far, хотя его точка назначения и расположена в том же самом сегменте. Машинный код инструкции CALL - 9А 20 00 20 0FH (в вашем случае адрес сегмента кода, вероятно, будет другим). Эта инструкция сохраняет значение 20 ООН из IP в стеке как 00 20Н, а 20 0FH из CS - как OF 20H. Поскольку подпрограмма использует тот же сегмент кода, что и основная программа, в CS помещается тот же адрес начала сегмента, что и для основной программы - OF 20H. Но в целом пара CS:IP для A22SUB2 будет указывать на следующий реальный адрес: Адрес в сегменте кода A22MAIN2 и A22SUB2 (регистр CS): 0F200H Смещение в IP для начала A22SUB2: +0QQ20H Реальный адрес для подпрограммы: 0F220H Поэтому сегмент кода подпрограммы предположительно начинается с 0F220H. Так ли это в действительности? Из карты связей это не ясно, но этот адрес можно выяснить из листинга трансляции программы, завершающегося адресом 0015Н (указанный адрес 0016Ц относится к следующей, пустой, ячейке). Поскольку сегменты кода определялись с выравниванием PARA, они начинаются с ближайшей доступной границы параграфа (адреса, нацело делящегося на 16 (или ЮН), так что младший шестнадцатеричный разряд равен 0): основная программа ... (не используется) подпрограмма 0F200 0F216 0F220 Компоновщик размещает подпрограмму в памяти по адресам, начинающимся с первой свободной границы параграфа, следующей за основной программой - в данном случае адрес этой границы 00020Н. Поэтому, как и было подсчитано, подпрограмма начинается с адреса 0F200H плюс 00020Н, то есть 0F220H. В следующем параграфе рассматриваются те же программы, написанные с использованием упрощенных сегментных директив. 536 Часть VI. Специальные темы
Использование упрощенных сегментных директив На рис. 22.5 показаны ранее рассмотренные программы, но переписанные с использованием упрощенных сегментных директив. Программы, приведенные на рис. 22.4, использовали явное объявление сегментов как PUBLIC, а в программах, рассмотренных ниже (рис. 22.5) используется объявление сегментов как PUBLIC по умолчанию. Поэтому и в тех, и в других программах в итоге генерируется единый сегмент код. Однако использование упрощенных сегментных директив приводит к некоторым существенным изменениям. Во-первых, сегменты (как видно на карте связей) теперь переставлены в порядке код, данные, стек, хотя это и не оказывает влияния на выполнение программы. Во-вторых, согласно таблице сегментов и групп (Segments and Groups table), сегмент кода подпрограммы (_ТЕХТ) выравнивается по четному адресу (по границе слова, а не параграфа, как было раньше). 0000 0002 0000 0000 0003 0005 0008 ооос ООН 0014 0016 TITLE / 0140 2500 BEGIN B8 R 8E D8 Al 0002 R 8B IE 0000 R 9A 0000 E B8 4C00 CD 21 BEGIN Segments and Groups : N a m e Length DGROUP GROUP DATA 0004 STACK 0040 TEXT 0016 A22MAIN3 (EXE) Вызов подпрограммы .MODEL .STACK EXTRN .DATA QTY PRICE .CODE PROC MOV MOV MOV MOV CALL MOV INT ENDP END WORD PARA WORD SMALL 64 A22SUB3:FAR DW 0140H DW 2500H FAR AX,©data DS,AX AX,PRICE BX,QTY A22SUB3 AX,4C00H 21H BEGIN Align Combine PUBLIC ' DAT, STACK 'STA PUBLIC 'COD Установка цены и количества Вызов подпрограммы Конец обработки Class * V СК' Е' Рис. 22.5. Вызов подпрограммы: упрощенные сегментные директивы Глава 22. Связь с подпрограммами 537
Symbols: Nam A22SUB3 BEGIN PRICE QTY 0000 e 0000 F7 E3 0002 CB 0003 Segments Nam DGROUP . DATA . TEXT . Symbols: Nam A22SUB3 and e e Type . . L FAR . . F PROC . . L WORD . . L WORD TITLE Value 0000 ,0000 0002 0000 Attr External _TEXT Length = 0016 _DATA _DATA A22SUB3 Вызываемая подпрограмма .MODEL SMALL .CODE A22SUB3 PROC FAR PUBLIC A22SUB3 MUL RETF A22SUB3 ENDP Groups: Length . . . 'GROUP . . . 0000' . . . 0003 Type . . . F PROC END WORD WORD Value 0000 BX ;AX - цена, ВХ ~ количество ;DX:AX - произведение A22SUB3 Align Combine Class PUBLIC 'DATA' PUBLIC ' 'CODE' Attr _TEXT Global Length = 0003 Рис. 22.5 (Окончание) Наблюдение за выполнением машинного кода показывает, что инструкции CALL соответствует код 9А 1600 170FH (в вашем случае адрес сегмента, вероятно, изменится). На этот раз новая величина смещения равна 16Н, а адрес сегмента - 0F17H. Поскольку подпрограмма использует тот же сегмент кода, что и основная программа, CS содержит один и тот же начальный адрес, 0F17H, для. обеих программ. Реальный адрес начала A22SUB3 можно вычислить так же, как и раньше: Адрес в сегменте кода A22MAIN3 и A22SUB3 (регистр CS): 0F170H Смещение в IP для начала A22SUB2: +QQQ16H Реальный адрес для подпрограммы: 0F186H Этот адрес можно выяснить из листинга трансляции программы, завершающегося адресом 0015Н (указанный адрес 0016Н относится к следующей пустой ячейке). Поскольку карта связей показывает 00000Н в качестве начала основного сегмента кода, следующая граница слова за адресом 00015Н - 00016Н. С этого адреса и начинается A22SUB3, выравниваемая по границе слова. Ниже приведена карта связей: 538 Часть VI. Специальные темы
Объектные модули: A22MAIN3 + A22SUB3 Начало Конец Длина 00000Н 00018Н 00019Н 0001АН 0001DH 00004Н 00020Н 0005FH 00040Н Точка входа программы - 0000:0000 Имя _ТЕХТ _DATA STACK Класс CODE DATA STACK Передача параметров подпрограмме Один из способов сообщения данных подпрограмме - передача их через параметры. Эти параметры сохраняются вызывающей программой в стеке и извлекаются из стека вызываемой подпрограммой. Используя этот способ, убедитесь, что каждая инструкция PUSH ссылается на слово (или двойное слово) в памяти или на регистр. Как указано в гл. 7, данные можно передавать по значению (непосредственно сохраняя его в стеке) или по ссылке (сохраняя в стеке адрес, по которому расположены данные). Ниже даны примеры использования обоих методов. При передаче параметров используется некоторая часть стека, к которой обращаются и вызывающая, и вызываемая программы. Вызываемая подпрограмма также может временно хранить в стеке собственные данные. Регистр ВР используется в качестве вспомогательного указателя, а подпрограмма использует как ВР, так и SP. Передача параметров по значению Программа A22MAIN4, приведенная на рис. 22.6, помещает в стек значения элементов данных PRICE и QTY, прежде чем передать управление подпрограмме A22SUB4. Изначально регистр SP содержал размер стека, т.е. 80Н. Каждое слово, помещенное в стек, уменьшает значение SP на 2. 1. Первая инструкция PUSH сохраняет PRICE (2500H) в ячейке стека со смещением 7ЕН. 2. Вторая инструкция PUSH сохраняет QTY (0140H) в ячейке стека со смещением 7СН. 3. CALL помещает содержимое CS (0F20H при выполнении) в ячейку стека со смещением 7АН. Поскольку подпрограмма объявлена как PUBLIC, компоновщик совмещает два сегмента кода, и CS содержит одно и то же значение адреса для обоих сегментов. 4. Затем CALL помещает значение из IP (0012H) в ячейку стека со смещением 78Н. Теперь SP содержит 78Н, а верхушка стека выглядит так: Глава 22. Связь с подпрограммами 539
Смещение 7ЕН 0025Н (PRICE) 7СН 4001Н (QTY) 7AH 200FH (содержимое CS) 78H 1200H (содержимое IP) Вызванная программа будет использовать ВР, чтобы извлечь параметры из стека. Первым ее действием будет сохранение значения ВР для вызывавшей программы, поэтому значение ВР помещается в стек. В данном случае ВР содержит 0, и PUSH сохраняет этот ноль в ячейке 76Н стека. После этого программа помещает в ВР значение из SP (0076H) поскольку ВР, в отличие от SP, можно использовать как индексный регистр. Поскольку ВР теперь содержит 0076Н, PRICE содержится по адресу ВР+8 (смещение 7ЕН), a QTY - по адресу ВР+6 (смещение 7СН). Мы знаем эти адреса, поскольку в стек было помещено 3 слова (6 байт) после QTY. Программа извлекает PRICE и QTY из стека в АХ и ВХ, соответственно, и производит умножение. TITLE STACKSEG STACKSEG DATASEG QTY PRICE DATASEG CODESEG BEGIN BEGIN CODESEG A22MAIN4 (EXE) Перед EXTRN A22SUB4:FAR ача параметров SEGMENT PARA STACK 'Stack' | DW ENDS 64 DUP(?) SEGMENT PARA 'Data' DW DW ENDS 0140H 2500H SEGMENT PARA PUBLIC ' PROC ASSUME MOV MOV PUSH PUSH CALL MOV INT ENDP ENDS END FAR CS:CODESEG,DS: AX,DATASEG DS,AX PRICE QTY A22SUB4 AX,4C00H 21H BEGIN Code' DATASEG,SS:STACKSEG Сохранение цены и количества Вызов подпрограммы Конец обработки Рис. 22.6. Передача параметров по значению 540 Часть VI. Специальные темы
TITLE CODESEG A22SUB4 A22SUB4 CODESEG A22SUB4 Вызываемая SEGMENT PARA PUBLIC PROC ASSUME PUBLIC PUSH MOV MOV MOV MUL POP RETF ENDP ENDS END FAR CS:CODESEG A22SUB4 BP BP,SP AX,[BP+8] BX,[BP+6] BX BP 4 подпрограмма 'Code' ;Получение цены /Получение количества ;DX:AX - произведение ;Возврат к точке вызова Рис. 22.6 (Окончание) Перед возвратом к вызывавшей программе подпрограмма извлекает из стека ВР (помещая в ВР ноль), и увеличивает значение в SP на 2, с 76Н на 78Н. Последняя инструкция, RETF, выполняет возврат типа far к основной программе, выполняя следующие действия: • извлекает слово, в данный момент находящееся на вершине стека (1200Н) в IP, и увеличивает значение SP на 2, с 78Н до 7АН; • извлекает слово, в данный момент находящееся на вершине стека (0F20H) в CS, и увеличивает значение SP на 2, с 7АН до 7СН. Поскольку передается два параметра, находящихся в ячейках 7СН и 7ЕН, инструкция возврата кодируется как RETF 4. Непосредственное значение 4 - это число байтов, хранящих переданные параметры (в данном случае - два слова). RETF добавляет значение к SP, увеличивая его до 80Н. Практически, поскольку параметры в стеке больше не нужны, операция "отбрасывает" их и корректно передает управление вызывавшей программе. Заметьте, что инструкции POP и RET только изменяют значение SP, не изменяя само содержимое стека. Карта связей для рассмотренного случая имеет вид: Объектные модули: A22MAIN4 + A22SUB4 Начало Конец Длина 00000Н 0007FH 00080Н 00080Н 00083Н 00004Н 00090Н 000BEH 0002FH Точка входа программы - 0009:0000 Глава 22. Связь с подпрограммами 541 Имя Класс STACKSEG STACK DATASEG DATA CODESEG CODE
Передача параметров по ссылке TITLE STACKSEG STACKSEG DATASEG QTY PRICE DATASEG CODESEG BEGIN BEGIN CODESEG TITLE CODESEG A22SUB5 A22SUB5 CODESEG A22MAIN5 (EXE) Передача параметров EXTRN A22SUB5:FAR SEGMENT PARA STACK 'Stack' | DW ENDS 64 DUP(?) SEGMENT PARA 'Data' DW DW ENDS 0140H 2500H SEGMENT PARA PUBLIC ' PROC ASSUME MOV MOV LEA LEA PUSH PUSH CALL MOV INT ENDP ENDS END FAR CS:CODESEG, DS: AX,DATASEG DS,AX CX,PRICE DX,QTY CX DX A22SUB5 AX,4C00H 21H BEGIN Code' DATASEG,SS:STACKSEG Сохранение адреса цены, количества Вызов подпрограммы Конец обработки A22SUB5 Вызываемая подпрограмма SEGMENT PARA PUBLIC ' PROC ASSUME PUBLIC PUSH MOV MOV MOV MOV IMUL POP ..-_ RETF ^ ENDP ENDS END FAR CS:CODE A22SUB5 BP BP,SP DI,[BP+8] BX,[BP+6] AX,[DI] WORD PTR [BX] BP 4 Code' SEG •Адрес цены и количества • Получение цены •Умножение на количество •Возврат в точку вызова Рис. 22.7. Передача параметров ссылке 542 Часть VI. Специальные темы
Программа, приведенная на рис. 22.7, передает параметры по ссылке, в данном случае она сохраняет в стеке адреса QTY и PRICE. Основное отличие в вызываемой подпрограмме, A22SUB5 - то, что адрес PRICE помещается из стека в регистр DI, а адрес QTY - в ВХ: MOV DI, [BP+8] ;Адрес элемента данных PRICE MOV ВХ, [ВР+6] ;Адрес элемента данных QTY После этого адрес из DI используется для загрузки в АХ значения PRICE из памяти: MOV AX,[DI] ;Значение PRICE из памяти Затем адрес из ВХ используется инструкцией IMUL, чтобы получить второй множитель: IMUL WORD PTR [ВХ] ; Умножить на QTY Остальные элементы программы разъяснений не требуют. Инструкции ENTER и LEAVE Инструкция ENTER, впервые появившаяся в процессоре 80286, используется для выделения участка стека для вызываемой процедуры, которой нужно передать параметры. Инструкция LEAVE выполняет противоположное действие - освобождает выделенный участок стека. Их форматы: [метка: ] [метка:] ENTER LEAVE размер, уровень вложенности [операндов нел1] 1 =Р* J • Операнд размер указывает число байтов, которое нужно выделить под создаваемый участок стека. Хотя в стеке обычно сохраняются слова или двойные слова, инструкция ENTER позволяет использовать параметры байтного формата. • Операнд уровень вложенности определяет, собственно, уровень вложенности операции. Для компиляторов BASIC, С и FORTRAN этот уровень всегда равен 0. Например, инструкция ENTER 4,0 эквивалентна инструкциям PUSH ВР MOV SP,BP SUB SP,4 В результате этой операции в стеке выделяется участок, на вершину которого указывает значение в регистре ВР, а на дно - SP. Программа может использовать этот участок для передачи параметров вызываемой процедуре. Глава 22. Связь с подпрограммами 543
Инструкция LEAVE отменяет выделение участка стека, выполняя обратные действия. Она эквивалентна инструкциям MOV SP,BP POP ВР Эта операция восстанавливает начальные значения в регистрах SP и ВР. TITLE ROW COL 9 Я£ BEGIN BEGIN SET_CURSOR SET_CURSOR A22ENTER (EXE) Исполь .MODEL .STACK .DATA EQU EQU .CODE PROC MOV MOV ENTER MOV .MOV CALL LEAVE MOV INT ENDP PROC PUSHA MOV MOV MOV MOV INT POPA RET ENDP END SMALL 64 5 8 FAR AX,©data DS,AX 2,0 BYTE PTR [BP-1] BYTE PTR [BP-2] SET_CURSOR AX,4C00H 21H NEAR АН,02Н BX,0 DH,[BP-1] DL,[BP-2] 10H BEGIN зование ENTER/LEAVE ,ROW ,COL SP - 40, BP - 0 SP - 3C, BP - 3E Строка и столбец в стек SP - ЗА, ВР - ЗЕ SP - 40, ВР - 0 Конец обработки •SP - 2А, ВР - ЗЕ ■Установка курсора: страница строка столбец •SP - ЗА, ВР - ЗЕ ■SP - ЗС, ВР - ЗЕ Рис. 22.8. Использование Enter и Leave Программа, приведенная на рис. 22.8, демонстрирует применение инструкции ENTER для выделения 2-байтового участка стека. Через этот участок процедуре SET_CURSOR передаются 2 параметра - строка и столбец. Заметьте, что основная программа записывает их в стек и извлекает из стека напрямую, без использования инструкций PUSH и POP. В комментариях показано содержимое регистров SP и ВР, исходя из того, что в начале они содержат 40Н и ООН, соответственно. 544 Часть VI. Специальные темы
Если вы следуете общим правилам, изложенным в этой главе, вам удастся скомпоновать и программу, состоящую из более чем двух исходных модулей. Однако убедитесь, что размеры стека достаточны для нормального функционирования: в больших программах с большим количеством инструкций PUSH и CALL может стать необходимым определение стека размером 64 слова или больше. В гл. 23 рассмотрены некоторые важные вопросы выделения памяти и выполнения оверлеев. В гл. 25 описаны некоторые дополнительные свойства сегментов, включая определение более чем одного сегмента кода или данных в одном исходном модуле и использование GROUP для их совмещения в один сегмент. Компоновка программы на языке C/C++ с программой на ассемблере Хотя в программу на языках C/C++ можно встроить изрядное количество ассемблерных инструкций, есть некоторые операции, которые можно выполнять только в ассемблерных программах. Это: • выполнение инструкций PUSH и POP; • прямое обращение к регистрам ВР и SP; • инициализация конкретных сегментных регистров; • выполнение критичных по времени выполнения процедур, например, вывод графики или ввод/вывод через порты. Ниже перечислены требования к взаимодействию программ на языках C/C++ с ассемблерными программами. Этот материал взят из руководств к компиляторам С и C++ фирмы Microsoft. Модели памяти И вызывающая, и вызываемая программы должны использовать одну и ту же модель памяти. Программы, использующие модели памяти Tiny, Small и Compact, обращаются к вызываемым подпрограммам вызовами типа near, сохраняя в стеке только значение регистра IP. Программы моделей Medium и Large используют вызовы типа far, сохраняя в стеке значения и CS, и IP. Модель памяти Flat, используемая программами, которые выполняются в защищенном режиме в среде Windows, использует вызовы только типа near. Директива ассемблера .MODEL указывает на совместимость с C/C++, например, .MODEL SMALL,C. Глава 22. Связь с подпрограммами 545 35 — 5-741
Соглашения об именах Ассемблерные модули должны использовать имена сегментов и переменных, корректные с точки зрения синтаксиса C/C++. Все внешние имена в программе на языке C/C++ начинаются с символа подчеркивания, например, _column. Ассемблерные ссылки на функции и переменные в модуле, написанном на C/C++, также должны начинаться со знака подчеркивания (__). Кроме того, поскольку C/C++ различает строчные и заглавные буквы, ассемблерный модуль должен использовать в названиях переменных в точности те же буквы (строчные или заглавные), что и в соответствующих именах в модуле C/C++. В некоторых случаях можно заставить компилятор C/C++ игнорировать регистр символов, задавая в командной строке определенные ключи, например: /тх для Microsoft версии 5.0 и Borland версии 4.0 /Сх для Microsoft версии 6.0 Регистры Ассемблерный модуль должен сохранять исходные значения регистров ВР, SP, CS, DS, SS, DI и SI; это делается посредством инструкций PUSH. При выходе из модуля восстанавливайте их соответствующими инструкциями POP. Передача параметров Существует три метода передачи параметров. 1. По значению (by value). Вызов из C/C++ модуля помещает копию переменной в стек. Вызванный ассемблерный модуль может изменить переданное значение, но не исходное значение в вызвавшем модуле. Если число параметров больше 1, C/C++ помещает их в стек, начиная с первого справа параметра списка параметров. 2. По ближней ссылке (by near reference). Вызывающая программа передает смещения требуемых элементов данных. Вызванный ассемблерный модуль должен использовать тот же сегмент данных, что и вызвавший модуль. 3. По дальней ссылке (by far reference). Вызывающая программа передает смещения и адреса сегментов (сначала адрес сегмента, затем - смещение) требуемых элементов данных. Считается, что вызванный ассемблерный модуль должен использовать другой сегмент данных, нежели вызвавший модуль. Вызванная программа может использовать инструкции LDS или LES для инициализации сегментных адресов. Программы на C/C++ передают параметры в стек в порядке, обратном принятому в большинстве других языков. Рассмотрим, например, выражение Adds (value_l, value_2); 546 Часть VI. Специальные темы
PUSH MOV MOV MOV POP RET BP BP, DH, DL, BP SP [BP+4] [BP+6] Это выражение помещает в стек значение value_2, а затем - value_l. Затем вызывается Adds. При выходе из вызванного модуля модуль C/C++ (не ассемблерный) увеличивает значение SP, чтобы отбросить переданные параметры. Типичная процедура обращения к переданным параметрам в ассемблерном модуле такова: Сохранить значение ВР Использовать адрес из SP в качестве базового указателя Получаем из стека переданные значения (байты) /Восстанавливаем исходное значение ВР После инструкции PUSH BP вершина стека выглядит следующим образом: ВР+6 ВР+4 ВР+2 ВР+0 По возвращении из вызванного модуля вызовите инструкцию RET без непосредственных операндов, поскольку вызывавшая программа C/C++ отвечает за очистку стека. Совместимость типов данных Далее перечислены типы данных C/C++ и соответствующие им типы элементов данных ассемблера. value. value. адрес _2 _1 во Смещение звра из 1та ВР из подпрограммы Тип данных С char unsigned short/int int, short unsigned long long Тип MASM 5.X DB DW DW DD DD Тип MASM 6.X BYTE WORD SWORD DWORD SDWORD Возвращаемые значения Вызванный ассемблерный модуль возвращает значения в следующих регистрах. Глава 22. Связь с подпрограммами 547 35*
Тип данных С Регистр char AL short, near int (16 бит) АХ short, near int (32 бита) ЕАХ long, far (16 бит) DX:AX long, far (32 бита) EDX:EAX Пример программы: компоновка С с ассемблером Простой пример на рис. 22.9 демонстрирует компоновку программы на С с подпрограммой на ассемблере. Программа на С компилируется, чтобы получить объектный файл. После этого два объектных файла с помощью компоновщика объединяются в общий исполняемый файл. Программа на С содержит определения двух переменных (temprow и tempcol) и воспринимает ввод значений этих переменных с клавиатуры. В программе определено имя ассемблерной подпрограммы set_curs. Программа передает адреса temp_row и temp_col в подпрограмму в качестве параметров. Подпрограмма выполняет установку курсора. Выражение set_curs (temp_row, temp_col); вызывает подпрограмму и передает ей параметры. #include <stdio.h> int main (void) { int temp_row/ temp_col; printf ("Enter cursor row: "); scanf ("%d", &temp row); printf ("Enter cursor column: "); scanf ("%d", &temp_col); set_curs (temp_row, temp_col); printf ("New cursor location\n"); } Рис. 22.9. Компоновка С и ассемблера 548 Часть VI. Специальные темы
; Используется ; Используются _DATA row col _DATA _TEXT DGROUP ,set_curs _set_curs _TEXT small-модель памяти С: стандартные сегментные segment equ equ ends SEGMENT GROUP ASSUME PUBLIC PROC PUSH MOV MOV MOV MOV MOV INT POP RETF ENDP ENDS END word 'DATA [bp+4] [bp+6] BYTE _DATA CS:_TEXT, _set_curs NEAR BP BP,SP AH,02H BX,0 DH,ROW DL,COL 10H BP i near код, near данные имена, и директива group • Параметры ; (аргументы) PUBLIC 'CODE' D£ :DGROUP, S S:DGROUP ; ВР-регистр вызова ;Указывает на параметры /Установить курсор на /видеостраницу 0 /строку из ВР+4 /столбец из ВР+б /Запрос прерывания /Восстановление ВР /Возврат к вызывавшей программе Рис. 22.9 (Окончание) В стек помещаются значение регистра SP, адрес смещения для возврата и адреса двух передаваемых параметров. Первый передаваемый параметр, temp_row, расположен в участке стека со смещением 04Н, и его считывают по адресу ВР+04Н. Второй параметр, temp_col, расположен в участке стека со смещением 06Н, и его считывают по адресу ВР+06Н. Подпрограмма использует значения, загруженные в регистр DX как номера строки и столбца, вызывая функцию прерывания INT 10H для установки курсора в точку пересечения этих строки и столбца. При выходе из подпрограммы восстанавливается исходное значение ВР. Инструкция RET возвращает управление вызывавшей программе, очищающей участок стека. Получаемый при компоновке этой простой программы исполняемый модуль имеет размер более 20 Кбайт. Языки высокого уровня часто помещают в исполняемый модуль множество дополнительной информации, следовательно, размер получаемых файлов резко возрастает. Глава 22. Связь с подпрограммами 549
Другие версии C/C++ не обязательно подчиняются указанным выше соглашениям. За деталями обращайтесь к рудоводствам по конкретным компиляторам. Ключевые моменты • Опция выравнивания указывает транслятору, по какому адресу выравнивать сегмент в памяти. / • Опция совмещения указывает транслятору и компоновщику, совмещать ли различные сегменты в памяти, или размещать их раздельно. • Присвоение группе родственных сегментов одного и того же имени класса указывает ассемблеру и компоновщику на необходимость сгруппировать их вместе. • Инструкция CALL генерирует внутрисегментный вызов, если вызываемая ей процедура определена как NEAR явно или по умолчанию (до 32Кбайт смещения). Внутрисегментный вызов будет типа far, если вызываемая процедура, расположенная в том же сегменте, имеет тип far или объявлена с помощью EXTRN. • Если основная программа вызывает подпрограмму, имя подпрограммы определяется как EXTRN. В подпрограмме точка входа объявляется как PUBLIC. • Для компоновки двух сегментов кода в один оба определяются с одинаковыми типом совмещения PUBLIC, именем и классом. Вопросы и упражнения 1. Укажите причины, по которым программы делятся на подпрограммы. Следующие три вопроса относятся к формату директивы SEGMENT: имя сегмента SEGMENT [выравнивание] [совмещение] ['класс'] 2. (а) Какой тип выравнивания сегментов используется по умолчанию? (б) Какое выравнивание указывается опцией BYTE (т. е. какие действия выполняет транслятор?) 3. (а) Какой тип совмещения сегментов используется по умолчанию? (б) Какое совмещение указывается опцией PUBLIC? (в) Для чего используется опция COMMON? 4. (а) Какая опция класса сегмента используется по умолчанию для сегмента кода в директиве SEGMENT? (б) Если два сегмента используют одно и то 550 Часть VI. Специальные темы
же имя класса, но не объявлены с опцией PUBLIC, каков будет результат трансляции? (в) Если два сегмента используют одно и то же имя класса и ^ объявлены с опцией PUBLIC, каков будет результат трансляции? 5. Объясните разницу между внутрисегментным вызовом и межсегментным. 6. Программа MAINPROG должна вызвать подпрограмму SUBCALC. (а) Какое выражение в MAINPROG сообщает транслятору, что имя SUBCALC определено вне данного файла? (б) Какое выражение в SUBCALC сообщает транслятору, что имя SUBCALC должно быть доступно другим программам? 7. Напишите и оттестируйте программы, описанные в предыдущем вопросе. Определите в MAINPROG три элемента данных формата DW: STOCKQTY (количество единиц товара), UNITCOST (цена единицы товара) и STOCKVALUE (сумма закупки). MAINPROG передает все три элемента данных в качестве параметров подпрограмме SUBCALC. Эта подпрограмма должна делить STOCKVALUE на STOCKQTY и сохранять частное в UNITCOST. Заметьте, что SUBCALC должна возвращать вычисленное значение в виде параметра. 8. Модифицируйте программу из предыдущего вопроса так, чтобы она использовала инструкции ENTER и LEAVE. 9. Дополните программу из предыдущего вопроса так, чтобы MAINPROG воспринимала ввод количества товара и суммы с клавиатуры. Добавьте подпрограмму SUBBINRY, преобразующую ASCII-число в двоичный формат. Подпрограмма SUBCALC должна вычислять цену, подпрограмма SUBASCII- преобразовывать двоичную цену в ASCII-формат, a MAINPROG - выводить результат на экран. Глава 22. Связь с подпрограммами 551
Глава 23 Загрузка программы и оверлеи Цель: рассмотреть процесс загрузки программ и оверлейных модулей для выполнения. Введение В этой главе рассматривается префикс сегмента программы (PSP), загрузчик программ, оверлеи и резидентные программы. Рассматривается функция 4АО 111 прерывания INT 2FH - мультиплексное прерывание (Multiplex Interrupt) и следующие функции прерывания INT 21H: . 25Н Установить вектор 4АН Изменить выделенный блок памяти прерывания 31Н Сохранить программу 4ВН Загрузить или выполнить программу 35Н Получить вектор 51Н Получить адрес текущего PSP прерывания 48Н Выделить память 58Н Получить/установить стратегию выделения памяти 49Н Освободить выделенную память, Префикс сегмента программы Загрузчик программ загружает программы .СОМ и .ЕХЕ на выполнение в сегмент программы и создает PSP в начале этого сегмента. Сама программа начинается со смещения 100Н в сегменте программы. PSP содержит следующие существенные элементы (перечислены в порядке возрастания адреса): 00—01Н Инструкция INT 20H (CD20H) для выполнения возврата в систему. 02—03Н Адрес сегмента для последнего параграфа памяти, выделенного для программы - в виде nnnnO. Например, 640 Кбайт будет показано как 00A0H, что означает А000[0]Н. 0А—0DH Адрес завершения (адрес сегмента для INT 22H). 0Е—11Н Адрес выхода по Ctrl+Break (адрес сегмента для INT 23H). 12—15Н Адрес выхода при критической ошибке (адрес сегмента для INT 24H). 552 Часть VI. Специальные темы
18—2ВН Таблица дескрипторов файлов по умолчанию. 2С—2DH Адрес сегмента среды программы. 32—ЗЗН Длина таблицы дескрипторов файлов. 34—37Н Указатель типа far на таблицу дескрипторов. 50—51Н Вызов функций прерывания INT 21H (DSfT 21H и RETF). 5С—6ВН Первый аргумент командной строки (FCB #1). 6С—7FH Второй аргумент командной строки (FCB #2). 80—FFH Буфер для DTA (области обмена с диском) по умолчанию. FCB (File Control Block - блок управления файлом) - это устаревший метод доступа к файлам на диске. В данной таблице FCB обозначает просто формат имени, в котором оно записано в памяти: D:nnnnnnnn.eee (одна буква D - имя диска, восемь nnnnnnnn - имя файла, три еее -расширение). В следующих параграфах описываются существенные элементы данных PSP. PSP 18—2ВН: Таблица дескрипторов файлов по умолчанию Каждый байт в 20-байтовой таблице дескрипторов файлов по умолчанию ссылается на запись в системной таблице, определяющей соответствующее устройство или драйвер. Изначально таблица содержит 0101010002FF.. .FF, где первый 01 соответствует клавиатуре, второй 01 -дисплею, и т.д.: Запись в таблице Дескриптор устройства 01 - консоль 0 - клавиатура (стандартный ввод) 01 - консоль 1 - экран (стандартный вывод) 01 - консоль 2 - экран (стандартная ошибка) 00 - СОМ1 3 - вспомогательный (последовательный порт) 02 - LPT1 4 - стандартный принтер FF - не назначен 5 - не назначен Обычно слово в PSP со смещением 32Н содержит длину таблицы (14Н или 20), а по смещению 34Н расположен адрес ее сегмента в форме IP:CS, где значение для IP равно 18Н (смещение в PSP), a CS - адресу сегмента программы hPSP. Программы, которым необходимо держать открытыми более 20 файлов одновременно, должны освобождать память (функцией 4АН прерывания INT21H) и вызывать функцию 67Н того же прерывания: ;Запросить дополнительные дескрипторы ;Новое число (от 20 до 65535) ;Вызвать обраОотчик прерывания MOV MOV INT АН,67Н ВХ,число 21Н Глава 23. Загрузка программы и оверлеи 553
Объем требуемой памяти - один байт на дескриптор, округляется до следующего параграфа в памяти плюс 16 байтов. Операция создает новую таблицу дескрипторов за пределами PSP и обновляет значения в PSP по адресам 32Н и 34Н (длина таблицы дескрипторов и ее адрес в памяти). При неудачном завершении операция устанавливает флаг CF в 1 и помещает в АХ код ошибки. PSP 2C—2DH: Адрес сегмента среды Программы, загруженные для выполнения, имеют соответствующую среду, которую система сохраняет в памяти. Среда начинается с границы параграфа перед сегментом программы. Размер среды по умолчанию"- 160 байт, а максимальный размер - 32 Кбайт. Среда содержит такие системные команды, как COMSPEC, PATH, PROMPT и SET. PSP 80—FFH: Буфер DTA по умолчанию Загрузчик программы инициализирует эту область полным текстом, введенным после запрошенного имени программы, например, после MASM или COPY. Первый байт содержит количество нажатых клавиш (если какие-то клавиши были нажаты) непосредственно после ввода имени программы. За этим числом следуют введенные символы (если они есть), а за ними - "мусор", оставшийся в памяти после предыдущей операции. Следующие четыре примера призваны разъяснить содержимое и назначение DTA. Пример 1: Команда без операндов Предположим, что вы дали команду запустить программу CALCIT.EXE на выполнение - ввели в командной строке CALCIT и нажали <Enter>. Когда загрузчик программы создаетPSP, он поместит в DTA по умолчанию 00 0D .... Первый байт содержит число байтов, введенных вслед за именем CALCIT, не считая символа <Enter>. Поскольку никаких клавиш кроме <Enter> не нажималось, это число 0. Второй байт содержит 0DH - код <Enter>. Пример 2: Команда с текстовым операндом Предположим, что вы хотите выполнить программу COLOR и передать ей параметр BY (blue yellow) для установки синего цвета символов на желтом фоне. Введите в командной строке COLOR BY и нажмите <Enter>. Загрузчик программы запишет в DTA последовательности 03 20 42 59 0D ... Первый байт означает длину - !3. За ним идет пробел, затем - символы BY и <Enter>. Кроме байта длины, это поле содержит именно то, что б^шо введено после имени программы в командной строке. 554 Часть VI. Специальные темы
Пример 3: Команда с именем файла в операнде Многие программы позволяют вам вводить имя файла после имени программы. Если вы введете, например, DEL A:CALCIT.OBJ<Enter>, DTA будет содержать следующую последовательность: 0D 20 41 ЗА 43 41 4С 43 49 54 2Е 4F 42 4А 0D ... A:CALCIT.OBJ Эта строка содержит длину введенного параметра - 0DH (13) и, собственно, введенный параметр (имя файла). Заканчивается строка символом 0DH - <Enter>. Пример 4: Команда с двумя именами файлов в операнде Рассмотрим ввод команды с двумя именами файлов в операнде, например, .COPY A:FILEA.ASM C:FILEB.ASM Загрузчик поместит в DTA следующую последовательность: 18 20 41 ЗА 46 49 4С 45 41 2Е 41 53 4D 20 И Т.Д. ... A:FILEA.ASM И Т.Д. . . . Эта последовательность содержит число введенных символов (18Н - 24), пробел, имя первого файла, еще один пробел, имя второго файла и символ <Enter>. Обращение к PSP Определив адрес PSP, вы можете обращаться к нему, чтобы обработать указанные файлы или выполнить некие специальные действия. Чтобы найти DTA СОМ-файла, просто загрузите 80Н в ВХ, DI или SI и обратитесь к содержимому: MOV SI,80H ;Смещение DTA в сегменте программы , CMP BYTE PTR[SI],0 ;Проверить буфер (DS:SI) JE выход ;Если ноль, то данных нет Однако программа типа .ЕХЕ не может опираться на предположение, что PSP всегда расположен непосредственно перед сегментом кода. Чтобы поместить в ВХ корректный адрес сегмента текущего PSP, можно вызвать функцию 51Н прерывания INT 21H. Фрагмент программы, приведенный на рис. 23.1, присваивает заданному файлу атрибут Hidden (02H). Пользователь в командной строке должен ввести имя программы, а за ним - имя файла, например, A23ATTRB d:nnnnnnnn.eee. Сначала программа определяет адрес PSP. Затем она ищет в DTA символ <Enter> и заменяет его байтом ООН. При этом образуется ASCIIZ-строка. Функция 43Н прерывания INT 21H использует ASCIIZ-строку из DTA, чтобы изменить атрибуты указанного файла. Глава 23. Загрузка программы и оверлеи 555
PUSH PUSH MOV- INT MOV MOV MOV MOV MOV REPNZ JNZ DEC MOV MOV MOV MOV MOV INT POP POP JC exit: A10MAIN ENDP ES DS AH,5lH 21H ES,BX DS,BX AL,0DH CX,21 DI,82H SCASB exit DI Сохранить ES и DS Запросить адрес PSP, в ВХ PSP адрес в ES и DS Поиск символа <Enter> •Число байтов •Адрес начала в PSP •Поиск <Enter> •Не найден, ошибка •Найден, ESrBYTE PTR [DI],0 ; заменить на ООН AH,43H AL,01 CX,02 DX,82H 21H DS ES exit ' END A10MAIN •Запрос установки атрибута файла hidden •ASCIIZ строка в PSP, адресуется DS:DX ;Ошибка, вызванная INT? да, вывод сообщения Рис. 23.1. Установка атрибутов файла Программу можно сделать более удобной, организовав ввод имен файлов и атрибутов с клавиатуры. Область верхней памяти Для адресации памяти процессор использует шину адреса определенной разрядности. Разряд адреса А20 позволяет адресовать 64К-область памяти, известную как верхняя память (high memory area, НМА). Ее адреса - от FFFF:0010H до FFFF:FFFFH - непосредственно за областью в 1 Мбайт. Когда процессор работает в реальном режиме (8086), он обычно отключает разряд адреса А20, и адреса, превышающие 1 Мбайт, отображаются в начало адресов памяти. Включение шины А20 позволяет обращаться к НМА. В файле CONFIG.SYS можно указать перемещение системных файлов из нижней памяти в верхнюю, освободив основную память для выполнения программ. 556 Часть VI. Специальные темы
INT 2FH (мультиплексное прерывание) помимо множества других функций, позволяет проверять свободное пространство в НМА: MOV 'АХ,4А01Н ;Запросить свободный объем НМА INT 2FH /Вызвать обработчик прерывания Операция возвращает в регистры следующие значения: ВХ - число свободных байт в НМА (0, если COMMAND.COM не загружен в область верхней памяти). ES:DI - адрес первого свободного байта в НМА (FFFF:FFFF, если системные модули не загружались в верхнюю память). Стратегия выделения памяти Функция 58Н предусматривает ряд способов определения места размещения программы в памяти: MOV АХ,5800Н /Запросить стратегию INT 21H /Вызвать обработчик прерывания Операция устанавливает флаг CF в 0 и помещает в АХ код стратегии: • ООН - первый подходящий (по умолчанию): просмотреть память, начиная с нулевого адреса, в поисках первого свободного блока, достаточного по размеру для размещения программы; • 01Н - наиболее подходящий: найти в основной памяти наименьший блок, достаточный по размеру для размещения программы; • 02Н - последний подходящий: искать подходящий блок, начиная с наивысшего адреса в основной памяти; • 40Н - первый подходящий, только в верхней памяти: искать подходящий блок, начиная с начала верхней памяти; • 41Н - наиболее подходящий, только в верхней памяти: искать наименьший подходящий блок в верхней памяти; • 42Н - последний подходящий, только в верхней памяти: искать подходящий блок, начиная с вершины верхней памяти; • 80Н - первый подходящий в верхней памяти: искать подходящий блок в верхней памяти, а если таковой не найден, то искать его в основной памяти; • 81Н - наиболее подходящий в верхней памяти: искать наименьший подходящий блок в верхней памяти, а если таковой не найден, то искать его в основной памяти; Глава 23. Загрузка программы и оверлеи 557
• 82Н - последний подходящий в верхней памяти: искать подходящий блок, начиная с наивысшего адреса в верхней памяти, а если таковой не найден, то искать его в основной памяти. Стратегии поиска наиболее подходящего блока и последнего подходящего блока подходят для многозадачных систем, в которых свободное пространство в памяти может быть фрагментировано из-за одновременной работы нескольких программ. Когда программа завершает работу, занимаемая ею память освобождается системой. Функция 5801Н: установить стратегию выделения памяти Эта операция позволяет выбирать стратегию выделения памяти. Чтобы выбрать стратегию выделения, загрузите в регистр AL код 01, а в ВХ - код стратегии. При возникновении ошибок флаг CF устанавливается в 1, а в АХ помещается 01 (некорректный код функции). Функция 5802Н: получить связь с верхней памятью Эта операция показывает, может ли программа выделять память из области, лежащей за пределами первых 640 Кбайт (Upper Mamory). Операция сбрасывает флаг CF в 0 и помещает в регистр AL код ООН, если, выделять память нельзя, и 01Н, если память подключена и ее можно выделять. Функция 5803Н: установить связь с верхней памятью Эта операция позволяет подключать или отключать верхнюю память и, если она подключена, выполнять выделение памяти из нее; MOV АХ,5803Н ;Запросить MOV ВХ,флаг ; подключение/отключение INT 21H ; верхней (Upper) памяти Флаг в регистре ВХ может иметь значение: ООН - для отключения области памяти, или 01Н - для подключения ее. В случае успешного выполнения операция сбрасывает флаг CF в 0 и позволяет программе выделять память. При возникновении ошибок флаг CF устанавливается в 1, а в АХ помещается код 01 (если в CONFIG.SYS нет строки DOS=UMB) или 07 (повреждена связь с памятью). 558 Часть VI. Специальные темы
(Если не загружен драйвер верхней памяти, то наличие строки DOS=UMB в CONFIG.SYS не приведет к загрузке DOS в верхнюю память. - Прим. ред.) Загрузчик программ При загрузке .СОМ и .ЕХЕ-программ загрузчик создает префикс сегмента программы по адресу ООН в сегменте программы в памяти и размещает программу, начиная с адреса 100Н. После этого шага загрузка и выполнение программ типа .СОМ и .ЕХЕ различны. Одно из основных различий состоит в том, что компоновщик вставляет специальный заголовок в файл .ЕХЕ при его сохранении на диске, и загрузчик использует этот заголовок при загрузке. Загрузка и выполнение программы .СОМ При загрузке программы .СОМ загрузчик программы выполняет следующие действия: • размещает PSP непосредственно перед программой в памяти; • загружает в 4 сегментных регистра адрес первого байта PSP; • устанавливает SP (указатель стека) на конец 64 К-сегмента, FFFEH (или в конец памяти, если сегмент недостаточно велик), и помещает в стек нулевое слово; • устанавливает IP (указатель инструкций) в 100Н (первый байт за PSP) и позволяет начать выполнение с байта, адресуемого сочетанием CS:IP - первого байта за PSP; это первый байт программы, и он должен содержать исполняемую инструкцию. Загрузка и выполнение программы .ЕХЕ Сохраненная на диске компоновщиком программа .ЕХЕ состоит из двух частей: записи заголовка (header record), содержащей управляющую информацию и информацию о перемещениях, и, собственно, загружаемого модуля. Заголовок имеет длину минимум 512 байт и может быть еще длиннее, если нужно перемещать много элементов. В заголовке содержится информация о размере исполняемого модуля, место, где он должен размещаться в памяти, адрес стека, и адреса перемещения, которые должны вставляться в неполные машинные адреса. В последующем списке термин блок обозначает 512-байтовую область в памяти. 00—01Н Шестнадцатеричный код 4D5A ('MZ'), обозначающий модуль типа .ЕХЕ. Глава 23. Загрузка программы и оверлеи 559
02—03Н Число байт в последнем блоке ЕХЕ-файла. 04—05Н Размер файла, включая заголовок, в 512-байтовых блоках. Например, если размер файла- 1025 байт, то это поле содержит 2, а поле 02—03Н содержит 1. 06—07Н Число элементов таблицы перемещений (см. элемент 1СН). 08—09Н Размер заголовка в 16-байтовых (по параграфам) блоках. Позволяет загрузчику определить начало, собственно, исполняемого модуля, следующего за заголовком. Минимальное число - 20Н (32, так как 32 х 16 = 512 байт). 0А—0BH Минимальное число параграфов, которые должны размещаться после конца программы, когда она загружена. ОС—0DH Переключатель загрузки в область верхней (НМА)/нижней памяти. При компоновке вы определяете, должна ли программа загружаться в нижнюю (обычно) или в верхнюю область памяти. Значение 0000Н указывает на верхнюю область памяти. Если значение другое, то оно обозначает максимальное количество параграфов, которое может размещаться после загруженной программы. 0Е—0FH Смещение сегмента стека в исполняемом модуле. 10—11Н Определенный размер стека как смещение, которое загрузчик должен поместить в регистр SP при передаче управления исполняемому модулю. 12—13Н Значение контрольной суммы - сумма всех слов в файле (с игнорированием переполнения). Используется для контроля возможных потерь данных. 14—15Н Смещение (обычно, но не всегда, ООН), которое загрузчик должен поместить в регистр IP при передаче управления исполняемому модулю. 16—17Н Смещение сегмента кода в исполняемом модуле, которое загрузчик помещает в регистр CS. Смещение указывается с учетом длины других сегментов, поэтому, если сегмент кода идет первым, смещение будет равно 0. 18—19Н Смещение таблицы перемещений (см. элемент 1СН). 1АН—1ВН Номер оверлея, где 0 (обычно) означает, что файл .ЕХЕ содержит основную программу. 560 Часть VI. Специальные темы
1СН—конец Таблица перемещений, содержащая некоторое число перемещаемых элементов, указанное в элементе со смещением 06—07Н. Этот элемент данных заголовка указывает число элементов исполняемого модуля, подлежащих перемещению. Каждый перемещаемый элемент, начиная с 1СН, состоит из 2-байтового смещения и 2-байтового адреса сегмента. Система создает блоки памяти для среды и сегмента программы. Далее перечислены шаги, которые должен выполнить загрузчик при загрузке и инициализации программы .ЕХЕ. • Считать в память отформатированную часть заголовка. • Вычислить размер исполняемого модуля (общий размер файла в элементе данных 04Н—05Н минус размер заголовка в 08—09Н). • Считать этот модуль в память в начало сегмента. • Прочитать таблицу перемещений в рабочую область и прибавить значение каждого элемента к начальному значению сегмента. • Установить в регистрах DS и ES адрес сегмента PSP. • Установить в регистре SS адрес PSP плюс 100Н (размер PSP) плюс смещение SS (из элемента данных ОЕН—0FH). • Установить регистр SP в значение из элемента данных ЮН—11Н (размер стека). • Установить регистр CS в значение адреса PSP плюс 100Н (размер PSP) плюс значение смещения CS в заголовке (элемент данных 16Н—17Н). • Установить регистр IP в значение смещения из элемента данных 14Н—15Н. Пара регистров CS:IP указывает адрес первой исполняемой инструкции в сегменте кода. С этой точки начнется выполнение программы. После выполнения указанных действий загрузчик завершает обработку заголовка .ЕХЕ-файла и отбрасывает его. Регистры CS и SS содержат корректные значения, но программа должна проинициализировать регистры DS и ES адресом сегмента данных: MOV АХ,имя_сегмента_данных ;Установка в регистрах MOV DS,AX ; адреса сегмента MOV ES,AX ; данных Пример: загрузка программы .ЕХЕ Рассмотрим карту связей, созданную компоновщиком для программы .ЕХЕ: Глава 23. Загрузка программы, и оверлеи 561 36 —5-741
Начало -00000Н' 00040Н 00060Н Точка входа Конец 0003АН 0005АН 0007FH программы - Длина 003ВН 001ВН 0020Н - 0000:0000 Имя CODESEG DATASEG STACK Класс Code Data Stack В карте указаны относительные (а не действительные) положения трех сегментов (некоторые компоновщики размещают сегменты в карте в алфавитном порядке). Согласно карте сегмент кода, CODESEG, начинается с адреса 00000Н - с начала исполняемого модуля, и длина его равна 003ВН. Сегмент данных, DATASEG, начинается со смещения 00040Н, и его длина-001ВН. Адрес 00040Н- это адрес первого параграфа за сегментом кода (адрес, нацело делящийся на ЮН). Сегмент стека, STACK, начинается со смещения 00060Н - с первого адреса параграфа за сегментом данных. DEBUG не может вывести запись заголовка, поскольку после того, как она обработана, загрузчик помещает на ее место PSP. Однако можно воспользоваться командой загрузчика L, чтобы прочитать сектор диска, и командой D, чтобы вывести его на экран. Например, загрузите в адреса, начинающиеся с CS:100 сектор 3 с диска A: L 100 0 3 1. Заголовок программы, который мы рассматриваем, содержит следующую информацию (в порядке возрастания адреса; данные представлены в виде с переставленными байтами). ООН Шестнадцатеричное 4D5A ("MZ"). 02Н Число байтов в последнем блоке - 5В00Н (или 005ВН). 04Н Размер файла, включая заголовок, в 512-байтовых блоках: 0200Н (0002x512 =1024 байта). 06Н Количество элементов в таблице перемещаемых элементов данных, следующей за форматированной частью заголовка - 0100Н (т.е., 0001). 08Н Размер заголовка в 16-байтовых блоках: 2000Н (0020Н = 32, а 32 х 16 = 512 байт). 0CH Загрузка в нижнюю память: FFFFH. 0EH Смещение сегмента стека: 6000Н (или 0060Н). ЮН Смещение для подстановки в SP: 2000H (или 0020Н). 14Н Смещение для IP: 0000H 16Н Смещение для CS: 0000Н 18Н Смещение для таблицы перемещений: 1Е00Н (или 001ЕН). 562 Часть VI. Специальные темы
Когда DEBUG загружал эту программу, регистры содержали следующие значения: SP = 0020 SS = 13А5 DS = 138F CS = 139F ES-= 138F IP = 0000 Для модулей .ЕХЕ загрузчик помещает в DS и ES адрес PSP и устанавливает в CS, IP, SS и SP значения из заголовка. Давайте посмотрим, как загрузчик инициализирует эти регистры. Пара регистров CS:IP Согласно регистру DS, когда программа загружалась, адрес PSP был равен 138F[0]H. Поскольку длина PSP - 100H байт, а сегмент кода идет первым (смещение 0), то сегмент кода начинается непосредственно за PSP - с адреса 139F[0]H. Смещение можно найти в заголовке - в элементе данных 16Н. Загрузчик использует эти значения для инициализации регистра CS: Адрес начала PSP (из регистра DS): 138F0H Длина PSP: * + 10ОН Смещение сегмента кода; + ОН Адрес сегмента кода: 13 9F0H Теперь CS содержит адрес начала сегмента кода программы (CODESEG). С помощью команды DEBUG D CS:0000 можно просмотреть машинный код программы. Этот код будет аналогичен шестнадцатеричной части файла .LST этой программы, за исключением операндов, которые в листинге отмечены как R. Кроме того, загрузчик устанавливает в IP смещение 0000Н, указанное в заголовке в элементе данных 14Н. Пара регистров SS:SP Загрузчик использовал значение 60Н в заголовке (из элемента 0EH) для установки адреса стека в SS: Адрес начала PSP (из регистра DS): 138F0H Длина PSP: + 100Н Смещение сегмента стека (по адресу 0EH в заголовке): + 60Н Адрес сегмента кода: 13А50Н Загрузчик использовал 20Н из заголовка (элемент данных ЮН) для инициализации указателя стека длиной стека. В этом примере стек был определен как DW 16 DUP(?), т.е. 16 2-байтовых элементов (или 32 байта - 20Н). SP будет указывать на текущую вершину стека. Глава 23. Загрузка программы и оверлеи 563 36*
Регистр DS Загрузчик использует регистр DS для установки точки начала PSP в 138F[0]H. Поскольку в заголовке нет адреса сегмента программы для DS, программа должна инициализировать его сама: 0004 0007 В8 8Е D8 R MOV MOV DX,DATASEG DS,AX Регистр CS DS SS Адрес 139F[0]H 13АЗ[0]Н 13А5[0]Н> Транслятор оставляет незаполненным машинный адрес DATASEG, который станет записью в таблице перемещений заголовка (начиная с адреса 1ЕН). Загрузчик вычисляет адрес для DS следующим образом: Адрес из регистра CS: 139F0H Смещение сегмента данных: + 4QH (смотрите карту связей) Адрес сегмента кода: 13А30Н DEBUG показывает готовую к выполнению инструкцию как В8 A3 13. Загрузчик помещает А313Н в регистр DS как 13АЗН. Поэтому значения в начале выполнения программы таковы: Смещение на карте связей ООН 40Н 60Н В качестве упражнения попытайтесь отследить выполнение какой-нибудь оттранслированной и скомпонованной программы .ЕХЕ и обратите внимание на изменения значений в регистрах: Инструкция Изменяющиеся регистры MOV AX, DATASEG IP и АХ MOV DS,AX IP и DS MOV ES,AX IP и ES Теперь DS будет содержать корректный адрес сегмента данных. Попробуйте воспользоваться DEBUG: командой D DS:0000 - для просмотра сегмента данных, и командой D SS:0000 - для просмотра стека. Выделение и освобождение памяти Ряд функций прерывания INT 21H позволяют выделять, освобождать и модифицировать размеры области памяти. Вероятнее всего, вам придется применять эти функции в программах, остающихся резидентными после завершения, или в программах, запускающих другие программы. Поскольку 564 Часть VI. Специальные темы
DOS была изначально разработана как однозадачная система, программа, запускающая другую программу, должна освобождать некоторую часть отведенной для нее памяти. Функция 48Н прерывания INT 21Н: выделить память Чтобы выделить память под программу, вызовите функцию 48Н и укажите в ВХ количество требуемых параграфов памяти: MOV АН,48Н /Запросить выделение памяти MOV ВХ,пара графы ;Количество выделяемых параграфов INT 21H /Вызвать обработчик прерывания Операция просматривает все блоки памяти, начиная с первого, пока не найдет блок, подходящий по размеру, обычно ближе к концу памяти. Успешная операция сбрасывает флаг CF в 0 и помещает в регистр АХ сегментный адрес выделенного блока памяти. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки (07 - разрушен блок памяти, или 08 - недостаточно памяти). При этом в ВХ помещается размер Наибольшего доступного блока в параграфах. Ошибка "разрушен блок памяти" означает, что операция обнаружила блок памяти, в котором первая буква была не "М" или "Z". Функция 49Н прерывания INT 21Н: освободить выделенную память Функция 49Н освобождает выделенную память. Обычно она используется для освобождения памяти, которую занимала резидентная программа. Загрузите в регистр ES сегментный адрес блока, который нужно освободить: MOV АН,49Н ;Запросить освобождение выделенной памяти MOV ES, сегмент /Сегментный адрес ^освобождаемого блока INT 21H /Вызвать обработчик прерывания Успешная операция сбрасывает флаг CF в 0 и помещает ООН в первый и второй байты освобождаемого блока, указывая тем самым, что этот блок больше не используется. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки (07 - разрушен блок памяти, или 09 - неправильный адрес блока памяти). Глава 23. Загрузка программы и оверлеи 565
Функция 4АН прерывания INT 21Н: изменить выделенный блок памяти Функция 4АН может увеличивать или уменьшать размер блока памяти. Поместите в ВХ число параграфов, которые нужно передать программе, а в ES - адрес ее PSP: MOV АН, 4АН ; Запросить изменение размера выделенного блока MOV ВХ,параграфы ;Количество выделяемых параграфов MOV ES,адрес ;Адрес PSP INT 21H /Вызвать обработчик прерывания Программа может вычислить собственный размер, вычитая адрес PSP из адреса конца последнего сегмента. Необходимо убедиться, что вы используете последний сегмент, если компоновщик расположил сегменты программы в алфавитном порядке. Успешная операция сбрасывает флаг CF в 0. В случае неудачи операция устанавливает флаг CF в 1 и помещает в АХ код ошибки (07 - разрушен блок памяти, 08 - недостаточно памяти, или 09 - некорректный адрес блока памяти). При этом в ВХ помещается размер наибольшего доступного блока (если была сделана попытка увеличить размер блока). Некорректный адрес в ES может вызвать ошибку 07. Загрузка или выполнение подпрограммы (функции) Рассмотрим, как запустить программу и из нее загрузить и выполнить подпрограмму. Функция 4ВН позволяет программе загрузить в память на выполнение подпрограмму. Для этого загрузите в регистры следующие значения: • AL - код подфункции (один из следующих: ООН - загрузить и выполнить, 01Н - загрузить программу, 03Н - загрузить оверлей, 05Н - установить состояние выполнения (здесь не рассматривается)); • пара ES:BX - адрес блока параметров; • пара DS:DX - адрес ASCIIZ-строки из заглавных букв, содержащей путь и имя вызываемой программы. Вот инструкции для загрузки подпрограммы: MOV АН,4ВН ;Запросить загрузку подпрограммы MOV AL, код ;Код подфункции (в данном случае - 01Н) LEA ВХ,адрес ;Адрес блока параметров LEA ВХ,путь ;Адрес строки пути INT 21H /Вызвать обработчик прерывания 566 Часть VI. Специальные темы
При возникновении ошибок операция устанавливает флаг CF в 1 я помещает в регистр АХ код ошибки - один из перечисленных в таблице, приведенной в начале гл. 17 (рис. 17.1). Код подфункции ООН: загрузить и выполнить Эта операция загружает программу .ЕХЕ или .СОМ в память, создает для нее PSP и передает ей управление. Поскольку все регистры, включая SS, изменяют значения, операция довольно сложна в применении. Блок параметров, адрес которого указывается в паре ES:BX, состоит из следующих элементов: Смещение Значение ООН Адрес сегмента блока среды, который нужно поместить в PSP со смещением 2СН. Нулевой адрес означает, что загружаемая программа должна унаследовать среду загружавшей. 02Н Двойное слово - указатель на командную строку, помещаемую в PSP со смещением 80Н. 06Н Двойное слово - указатель на FCB1 для передачи в PSP со смещением 5СН ОАН Двойное слово - указатель на FCB2 для передачи в PSP со смещением 6СН Двойные слова содержат адреса в форме сегмент/смещение. Код подфункции 01Н: загрузить программу Эта операция загружает программу .ЕХЕ или .СОМ в память и создает для нее PSP, но не передает ей управление. Блок параметров, адрес которого указывается в паре ES:BX, состоит из следующих ниже элементов. Смещение Значение ООН Адрес сегмента блока среды, который нужно поместить b'PSP со смещением 2СН. Нулевой адрес означает, что загружаемая программа должна унаследовать среду загружавшей. 02Н Двойное слово - указатель на командную строку, помещаемую в PSP со смещением 80Н. 06Н Двойное слово - указатель на FCB1 для передачи в PSP со смещением 5СН ОАН Двойное слово - указатель на FCB2 для передачи в PSP со смещением 6СН Глава 23. Загрузка программы и оверлеи 567
ОЕН Начальный адрес стека 12Н Начальный адрес сегмента кода Двойные слова содержат адреса в форме сегмент/смещение. Код подфункции ОЗН: загрузить оверлей Эта операция загружает в память программу или блок кода, но не создает для них PSP и не передает им управление. Поэтому запрошенная программа может размещаться в памяти на месте ранее загруженной программы или ее части. Блок параметров, адресуемый парой регистров ES:BX, содержит только два элемента. Смещение Значение ООН Адрес (в формате слова) сегмента, в который должна загружаться программа 02Н Слово - фактор настройки загружаемого модуля При ошибках флаг CF устанавливается в 1, а в АХ помещается код ошибки, согласно таблице, рассмотренной в гл. 17 (рис. 17.1). Программа: загрузка и выполнение Программа, приведенная на рис. 23.2, указывает системе выполнить команду DIR из командной строки для указанного диска. Сначала программа использует функцию 4АН, чтобы уменьшить требуемый объем памяти до своего действительного размера в параграфах. В программе используются упрощенные сегментные директивы, а ассемблер последовательно размещает сегменты кода, данных и стека. Отметим также, что в начале -выполнения ES содержит адрес начала PSP. Чтобы вычислить размер программы, функция 4АН выполняет следующие действия. 1. Помещает сегментный адрес стека (последнего сегмента) в регистр ВХ. 2. Прибавляет размер стека (в параграфах) к значению в ВХ. 3. Вычитает сегментный адрес PSP из значения в ВХ. Другие трансляторы и компоновщики могут разместить сегменты в другой последовательности, поэтому перед тем, как попытаться выполнить эту программу, лучше проверить ее карту связей. В примере функция 4ВН с кодом подфункции ООН выполняет загрузку командного процессора (COMMAND.COM) и передачу ему управления. Программа выводит на экран результаты выполнения команды DIR. 568 Часть VI. Специальные темы
TITLE A23EXDIR (EXE) Выполнение команды DIR| PARAREA DIRCOM FCB1 FCB2 PROGNAME 9Pfi • .zoo , A10MAIN A20ERR: A30ERR: A90XIT: A10MAIN .MODEL SMALL .STACK 64 .DATA LABEL BYTE DW DW DW DW DW DW DW DB DB DB DB о OFFSET DIRCOM ©data OFFSET FCB1 @data OFFSET FCB2 ©data 17,'/C DIR C:•,13 16 DUP(O) 16 DUP(O) 'C:\COMMAND.COM' , .CODE PROC FAR MOV MOV ADD MOV SUB INT JC MOV MOV MOV MOV MOV LEA LEA INT JC MOV JMP MOV JMP MOV MOV INT AH,4AH BX,STACK BX, 04 CX,ES BX,CX 21H A20ERR AX,©data DS,AX ES,AX AH,4BH AL, 00 BX,PARAREA DX, PROGNAME 21H A30ERR AL, 00 A90XIT AL, 01 A90XIT AL, 02 AH,4CH 21H ENDP END A10MAIN Блок параметров: адрес строки среды указатель на командную строку указатель на FCB1 по умолчанию указатель на FCB2 по умолчанию ,0 ; Командная строка 0 ; Путь к COMMAND.COM Уменьшить размер памяти Адрес сегмента стека + размер в параграфах Адрес сегмента PSP Вычесть из суммы •Не хватает памяти? •ок, установить DS и ES ■Запрос загрузки и выполнения COMMAND.COM •Ошибка выполнения? •ОК, нет ошибки ?Код ошибки 1 ;Код ошибки 2 ;Запрос ; конца работы Рис. 23.2. Выполнение команды DIR из программы пользователя Глава 23. Загрузка программы и оверлеи 569
Функция 4DH прерывания INT 21Н: получить код возврата подпрограммы Эта операция извлекает код возврата, который последняя программа передает при завершении функцией 4СН или 31Н. Она помещает в регистры следующие значения: • АН - метод завершения подпрограммы, где: • ООН - нормальное завершение; • 01Н - прервано комбинацией Ctrl+C; • 02Н - критическая ошибка устройства; • ОЗН - завершено функцией 31Н прерывания INT 21H (завершиться и остаться резидентной); • AL - значение, возвращенное подпрограммой. Оверлеи программ В программе, приведенной на рис. 23.3, используется функция 4ВН, но на этот раз -только для загрузки программы в память (без передачи ей управления). TITLE STACK STACK DATASEG PARABLOK FILENAME ERRMSG1 ERRMSG2 ERRMSG3 DATASEG 9 Q£ .ZOO , CODESEG A10MAIN A23CALLV (EXE) Вызов подпрограммы и оверлея EXTRN A23SUB1:FAR SEGMENT PARA STACK 'Stackl' | DW ENDS 64 DUP(?) SEGMENT PARA 'Datal LABEL DW DW DB DB DB DB ENDS v WORD ;Блок параметров 0 0 'C:\A23SUB2.EXE',0 •Modify mem error' 'Allocate error ' 'Seg call error ' SEGMENT PARA 'Codel' PROC ASSUME MOV .FAR CS:CODESEG,DS:DATASEG,SS:STACK AX,DATASEG Рис. 23.3. Вызов подпрограммы и оверлея 570 Часть VI. Специальные темы
А20ERROR: A30ERROR: MOV MOV INT CALL MOV MOV MOV SHR INC ADD MOV SUB INT JC MOV MOV MOV MOV INT JC MOV MOV MOV LEA LEA INT JC MOV MOV MOV LEA CALL JMP LEA CALL JMP LEA CALL JMP DS,AX AX,0003H ЮН A23SUB1 AH,4AH BX,CS CX,OFFSET B90END CX,04 CX BX,CX CX,ES BX,CX 21H A?0ERROR AX,DS ES,AX AH,48H BX,40 21H A3 0ERROR PARABLOK,AX AH,4BH AL, 03 BX,PARABLOK DX,FILENAME 21H A50ERROR AX,PARABLOK PARABLOK+2,AX PARABLOK,20H BX,PARABLOK DWORD PTR [BX] A90 BP,ERRMSG1 B10DISPLY A90 BP;ERRMSG2 B10DISPLY A90 Установка режима и очистка экрана Вызов подпрограммы 1 Уменьшение памяти: Сегментный адрес CS Смещение концаiпрограммы Пересчет в параграфы + 1 Добавление к числу параграфов Вычитание сегментного адреса PSP Размер данной программы Если ошибка, выход Инициализация ES для работы с оверлеем Выделение памяти 40 параграфов Если ошибка/ выход Сохранение сегментного адреса Загрузка подпрограммы 2 без выполнения Если ошибка, выход •Обмен двух слов из PARABLOK •Установка смещения CS = 2ОН • Вызов подпрограммы 2 •Вывод сообщения • Вывод сообщения Рис. 23.3 (Продолжение) Глава 23. Загрузка программы и оверлеи 571
А50ERROR: A90: A10MAIN B10DISPLY B90END: B10DISPLY CODESEG TITLE DATASEG SUBMSG DATASEG CODESEG A23SUB1 LEA CALL JMP MOV INT ENDP PROC MOV MOV MOV MOV MOV MOV INT RET NOP ENDP ENDS END BP,ERRMSG3 B10DISPLY A90 AX,4C00H 21H NEAR AX,DS ES,AX AX,1301H BX,001EH CX,16 DX,1510H 10H A10MAIN A2 3 SUB1 Вызыв а ема я SEGMENT PARA 'Data2' DB ENDS 'Subprogram 1 SEGMENT PARA 'Code2' PROC ASSUME PUBLIC PUSH PUSH MOV MOV MOV MOV MOV LEA MOV MOV INT POP POP FAR ;Вывод сообщения ;Конец работы ;BP установлен при вызове ;Установка ES для ; этой процедуры ;Запрос вывода ;Страница:атрибуты ;Длина ;Строка:столбец ;Конечное смещение программы подпрограмма reporting' CS:CODESEG,DS:DATASEG | A23SUB1 DS ES AX,DATASEG DS,AX ES,AX АХД301Н BX,001EH BP,SUBMSG CX,22 DX,0810H 10H ES DS ;Сохранение DS вызвавшей ; программы и ES инициализация DS и ES ;Запрос вывода ;Страница и атрибуты ;Сообщение ;Длина ;Строка:столбец ;Восстановление ES вызывавшей ; программы и DS Рис. 23.3 (Продолжение) 572 Часть VI. Специальные темы
A23SUB1 CODESEG TITLE DATASEG SUBMSG DATASEG CODESEG A23SUB2 A23SUB2 CODESEG RET ENDP ENDS END A23SUB2 Вызываемая оверлейная программа SEGMENT PARA 'Data' DB ENDS 'Subprogram 2 reporting* SEGMENT PARA ■Code" PROC ASSUME PUSH MOV MOV MOV MOV MOV LEA MOV MOV INT POP RET ENDP ENDS END FAR CS:CODESEG,DS:DATASEG DS ;Coxp.DS вызывавшей программы AX,CS /Установка адреса первого DS,AX ; сегмента DS ES,AX АХД301Н /Запрос вывода ВХ,001ЕН /Страница:атрибуты ВР,SUBMSG /Сообщение СХ,22 /Длина DX,1010H /Строка:столбец ЮН DS /Bocct.DS вызывавшей программы Рис. 23.3 (Окончание) Процесс состоит из основной программы, A23CALLV, и двух подпрограмм, A23SUB1 и A23SUB2. Программа A23CALLV содержит следующие сегменты: STACK SEGMENT PARA STACK 'Stackl' DATASEG SEGMENT PARA •Datal' CODESEG SEGMENT PARA 'Codel' A23SUB1 связана с A23CALLV и вызывается из нее. Она состоит из двух сегментов: DATASEG CODESEG SEGMENT PARA SEGMENT PARA ■Data2' 'Code2' Сначала компонуются сегменты A23CALLV - поэтому и отличаются имена их классов: 'Datal', 'Data2', 'Codel', 'Code2' и т.д. Вот карта связей для A23CALLV+A23SUB1: Глава 23. Загрузка программы и оверлеи 573
Начало ОООООН 00080Н 000D0H 00160Н 00180Н Конец 0007FH 000С2Н 0015FH 00175Н 0019DH Длина 00080Н 00043Н 00090Н 00016Н 0001ЕН Имя STACK DATASEG CODESEG DATASEG CODESEG Класс Stackl Datal Codel Data2 Code2 A23SUB2 также вызывается из A23CALLV, но транслируется и компонуется отдельно. Она содержит два сегмента: DATASEG SEGMENT PARA 'Data' CODESEG SEGMENT PARA 'Code' Карта связей для нее содержит предупреждение об отсутствии сегмента стека и содержит две строки: Начало Конец Длина Имя Класс ОООООН 00015Н 00016Н DATASEG Data 00020Н 0003АН 0001ВН CODESEG Code В этих программах, в отличие от программы, приведенной на рис. 23.2, используются полные сегментные директивы, и компоновщик расставил сегменты в том порядке, в каком они объявлены в тексте программы. Поэтому сегмент кода имеет наибольший адрес. Когда загрузчик помещает A23CALLV + A23SUB1 в память на выполнение, A23CALLV вызывает и выполняет A23SUB1 в обычном режиме. CALL типа near корректно инициализирует IP, но, поскольку у A23SUB1 собственный сегмент данных, она должна сохранить в стеке предыдущее значение DS и поместить в DS адрес собственного сегмента данных. A23SUB1 устанавливает курсор, выводит на экран сообщение и возвращает управление A23CALLV. Чтобы поместить A23SUB2 поверх A23SUB1, A23CALLV должна уменьшить свой блок памяти, поскольку система выделила ей всю доступную память. Чтобы вычислить свой размер перед выполнением функции 4АН прерывания INT 21H, программа выполняет следующие действия. 1. Помещает сегментный адрес сегмента кода в регистр ВХ. 2. Прибавляет размер сегмента кода (в параграфах) к значению в ВХ. 3. Вычитает сегментный адрес PSP из значения в ВХ. После этого функция 48Н прерывания INT 21Н выделяет блок памяти для размещения A23SUB2 поверх A23SUB1 размером 40Н параграфов. Операция помещает адрес загрузки в АХ, и A23CALLV сохраняет его в PARABLOK. !^то первое слово блока параметров, который будет использоваться функцией 4ВН. 574 Часть VI. Специальные темы
Функция 4ВН с кодом подфункции ОЗН в AL загружает A23SUB2 в память. Обратите внимание на определение в сегменте данных: 'C:\A23SUB2.EXE,0\ Функция 4ВН обращается к CS и PARABLOK - первое слово содержит сегментный адрес, по которому должен загружаться оверлей, а второе слово - смещение, в данном случае - 0. Все вышесказанное может стать более ясным из таблицы: После начальной загрузки 000 PSP 100 A23CALLV 260 A23SUB1 После уменьшения размера блока функцией 4АН 000 PSP 100 A23CALLV После выделения памяти функцией 48Н 000 PSP 100 A23CALLV 260 A23SUB2 CALL типа far к программе A23SUB2 требует адреса в форме IP:CS, но в PARABLOK этот адрес хранится в виде CS:IP. Поэтому значение для CS перемещается во второе слово, а в первом записывается 20Н - поскольку на карте связей именно это значение указывается в качестве смещения сегмента кода A23SUB2. Последующие инструкции загружают адрес PARABLOK в ВХ и вызывают A23SUB2: LEA ВХ,PARABLOK ;Адрес PARABLOK CALL DWORD PTR [ВХ] ;Вызвать A23SUB2 Заметьте, что A23CALLV не вызывает A23SUB2 по имени в ее сегменте кода, поэтому не требуется и выражение EXTERN, определяющее имя A23SUB2. Поскольку A23SUB2 имеет собственный сегмент данных, она сохраняет предыдущее значение DS в стеке и инициализирует DS адресом собственного сегмента данных. Но A23SUB2 не была скомпонована с A23CALLV. В результате инструкция MOV AX,DATASEG поместила бы в АХ просто адрес смещения DATASEG, 0[0]Н, а не адрес нужного сегмента. Мы знаем, что CALL помещает в CS адрес первого сегмента, которым (согласно карте связей) является сегмент данных. Копирование значения CS в DS дает в DS корректный адрес. Заметьте, что программу пришлось бы изменить, если бы сегменты A23SUB2 размещались в другом порядке. A23SUB2 выводит на экран сообщение, извлекает из стека значение DS и возвращает управление A23CALLV. Резидентные программы Некоторые программы построены так, чтобы постоянно находиться в памяти и выполнять какие-то действия в ответ на определенные события, например, нажатие указанных сочетаний клавиш. Эти программы, называемые резидентными ('terminate but stay resident' или TSR), можно загружать перед другими, работающими в обычном режиме, программами. Они почти всегда являются программами типа .СОМ. Глава 23. Загрузка программы и оверлеи 575
Оставить программу в памяти в резидентном режиме после ее завершения достаточно просто. Вместо нормального завершения завершите ее вызовом функции 31Н прерывания INT 21H (сохранить программу). При этом необходимо указать размер программы в регистре DX: MOV АН,31Н ;Запросить создание TSR MOV DX, размер ; Размер программы INT 21H ;Вызвать обработчик прерывания Когда выполняется процедура инициализации, система резервирует блок памяти, в котором находится программа, и загружает другие программы в последующие блоки памяти. Более сложная часть создания резидентной программы - создание механизма ее активации в резидентном режиме, поскольку это не программа, являющаяся частью операционной системы. Чаще всего эта задача решается изменением таблицы векторов прерываний так, чтобы резидентная программа просматривала все нажимаемые сочетания клавиш, реагировала на нужные сочетания и передавала системе все остальные сочетания. Практически, резидентные программы почти всегда состоят из частей, перечисленных ниже. 1. Раздел, переопределяющий адреса в таблице векторов прерываний. 2. Процедура инициализации, выполняемая единственный раз при запуске программы и выполняющая следующие действия: • замена адреса в таблице векторов прерываний адресом резидентной программы; • установка размера части программы, которая должна остаться резидентной; • использование прерывания, указывающего системе завершить выполнение текущей программы и разместить указанную часть программы в памяти. 3. Процедура, остающаяся резидентной и получающая управление при некоторых событиях, например, нажатии определенного сочетания клавиш или обращения к диску. Практически, процедура инициализации устанавливает все элементы, обеспечивающие работу резидентной части программы, после чего позволяет удалить себя из оперативной памяти. Резидентная программа может также использовать две функции прерывания INT 21Н для обращения к таблице векторов прерываний, поскольку нет уверенности, что в более продвинутых компьютерах эта таблица все так же будет размещаться в памяти, начиная с адреса 0000Н. 576 Часть VI. Специальные темы
Функция 35Н прерывания INT 21Н: получить вектор прерывания Чтобы получить адрес определенного обработчика прерывания из таблицы векторов прерываний, загрузите в AL требуемый номер обработчика и вызовите эту функцию: MOV АН,35Н /Запросить адрес обработчика MOV AL,номер /Номер прерывания INT 21H /Вызвать обработчик прерывания Операция помещает запрошенный адрес в пару регистров ES:BX в форме сегмент:смещение. Для основной памяти запрос, например, адреса обработчика INT 09H, вернет ООН в ES и 24Н (36) - в ВХ. Функция 25Н прерывания INT 21Н: установить вектор прерывания Для установки нового адреса обработчика прерывания загрузите номер требуемого обработчика в AL, а новый адрес - в DX: MOV АН,25Н /Установить адрес обработчика MOV AL,номер /Номер прерывания MOV DX,адрес /Адрес нового обработчика INT 21H /Вызвать обработчик прерывания Операция заменяет текущий адрес в таблице новым адресом. После этого, когда вызывается соответствующее прерывание, обработка передается резидентной программе, а не обычно используемому обработчику. Пример резидентной программы Резидентная программа A23RESID, приведенная на рис. 23.4, подает звуковой сигнал каждый раз, когда нажимается клавиша ESC. Единственное практическое назначение данной программы - продемонстрировать технические особенности резидентных программ настолько просто, насколько только можно. Изучив эту программу, вы сможете писать более сложные резидентные программы, решающие реальные задачи. В программе интересны следующие моменты. CODESEG начинает сегмент кода A23RESID. Первая исполняемая инструкция, JMP BIOINIT, передает управление на процедуру инициализации, минуя резидентную часть. Эта процедура сначала исполняет инструкцию CLI, чтобы заблокировать вызовы прерываний, которые могут произойти во время исполнения критически важной части процедуры. После этого она вызывает функцию 35Н прерывания INT 21H, чтобы получить адрес прерывания INT 09H из таблицы Глава 23. Загрузка программы и оверлеи 577 37 — 5-741
векторов прерываний. Этот адрес оказывается в паре регистров ES:BX, а затем процедура сохраняет его в SAVEINT9. Затем функция 25Н устанавливает адрес самой программы в качестве адреса прерывания INT 09H в таблице. Это A10TEST, точка входа резидентной части программы. Практически программа сохраняет исходный адрес INT 09H и заменяет его собственным адресом. На последнем шаге необходимо установить размер резидентной части программы (всего кода вплоть до B10INIT), поместив его в DX, и вызвать функцию 31Н (остановить и оставить резидентной) прерывания INT 21H для завершения. В дальнейшем код процедуры B10INIT в памяти будет замещен программами, запускаемыми на выполнение. TITLE A23RESID (СОМ) Резидентная программа: / CODESEG BEGIN: SAVEINT9 DURATION A10TEST: А20: N АЗО: A50EXIT: B10INIT: Звонок по нажатию Esc SEGMENT PARA ASSUME ORG JMP DD DW PUSH PUSH IN CMP JNE IN PUSH OR OUT MOV LOOP OR MOV LOOP POP AND OUT POP POP JMP CLI MOV MOV CS:CODESEG 100H B10INIT 100H AX CX AL,60H AL,01 A50EXIT AL,61H AX AL,00000011B 61H,AL CX,512H A20 AL,00000010B CX,512H A3 0 AX AL,11111100B 61H,AL CX AX CS:SAVEINT9 AH,35H AL,09H Переход к инициализации INT 09H адрес Сохранение регистров Прием кода от порта Скэнкод 01 (Esc)? нет, выход Получение статуса порта и сохранение Включение спикера Длина Установка бита 1 •Статус порта ■Выключение спикера •Восстановление регистров •К старому INT 09H •Инициализация: 1 •Запрет прерываний •Получение адреса INT 0'9Н в ES:BX Рис. 23.4. Резидентная программа 578 Часть VI. Специальные темы
CODESEG - INT MOV MOV MOV MOV MOV INT MOV MOV STI INT ENDS END 21H WORD PTR SAVEINT9,BX WORD PTR SAVEINT9+2,ES AH,25H AL,09H DX,OFFSET A10TEST 21H AH,3lH DX,OFFSET B10INIT 21H BEGIN ; и сохранение его ;Установка нового адреса для INT 09H = A10TEST ;Оставить резидентной ;Установка размера ;Разрешение прерываний Рис. 23.4 (Окончание) A10TEST - имя резидентной процедуры, активируемой, если пользователь нажмет клавишу. Система передаст управление по адресу INT 09H из таблицы векторов прерываний, который заменен на адрес A10TEST. Поскольку прерывание может возникнуть, например, в момент, когда производится операция с регистрами, A23RESID должна сохранять значения всех регистров, которые она использует. Программа обращается к флагу клавиатуры, чтобы выяснить, была ли нажата клавиша Esc (скэн-код 01). Если да, то программа подает звуковой сигнал (использование спикера описано в гл. 24 в параграфе "Генерация звука"). Завершающие инструкции включают в себя восстановление сохраненных в стеке значений регистров - в обратном сохранению порядке - и переход по адресу из SAVEINT9, т.е. к исходному обработчику, воспринимающему, как и раньше, нажатия остальных клавиш. Приведенный далее пример должен разъяснить вышесказанное. Сначала рассмотрим последовательность действий при стандартной обработке прерывания. 1. Пользователь нажимает клавишу, и клавиатура посылает запрос INT 09H в BIOS. 2. BIOS использует адрес INT 09H в таблице векторов прерываний, чтобы определить адрес обработчика прерывания в BIOS. 3. После этого управление передается подпрограмме в BIOS. 4. Процедура считывает символ и, если это стандартный символ, помещает его в буфер клавиатуры. Теперь рассмотрим процедуру с участием резидентной программы. 1. Пользователь нажимает клавишу, и клавиатура посылает запрос INT 09H в BIOS. Глава 23. Загрузка программы и оверлеи 579 37*
2. BIOS использует адрес INT 09H в таблице векторов прерываний, чтобы определить адрес обработчика прерывания в BIOS. 3. Теперь таблица содержит измененный адрес обработчика прерывания - адрес процедуры A10TEST в резидентной программе - и именно ей передается управление. 4. Если нажата клавиша Esc, то подается звуковой сигнал. 5. A10TEST завершается переходом к подпрограмме исходного обработчика INT 09H, передающего управление подпрограмме в BIOS. 6. Процедура считывает символ и, если это стандартный символ, помещает его в буфер клавиатуры. Попробуйте воспользоваться DEBUG и понаблюдать за выполнением программы..С помощью команды D 00:20 просмотрите содержимое части таблицы векторов прерываний в строке 0000:0020 по адресу 24Н (36), где хранится адрер обработчика INT 09H. Первое слово по этому адресу - смещение, а второе - адрес сегмента, оба - в виде с переставленными байтами. Например, если адрес - 07 01 EF 05, то, чтобы просмотреть сам обработчик, введите команду D 0107:05EF. Просматриваемая область памяти должна начинаться с 50 51 IE B8 - это начальный фрагмент машинного кода процедуры A10TEST. Некоторые программы, также переназначающие адрес обработчика INT 09H, могут не позволить перехват прерывания этой программой. Ключевые моменты • Модуль .ЕХЕ, создаваемый компоновщиком, состоит из заголовка, содержащего информацию о перемещениях и управляющие данные, и, собственно, загружаемого модуля. • При загрузке программы .СОМ загрузчик помещает в сегментные регистры адрес PSP, устанавливает указатель стека на конец сегмента, помещает в стек нулевое слово и устанавливает указатель инструкций (IP) в 100Н (на первый байт за PSP). После этого выполнение начинается с адреса в паре CS:IP, т.е. непосредственно за PSP. • При загрузке программы .ЕХЕ загрузчик считывает в память заголовок файла, вычисляет размер исполняемого модуля и помещает модуль с диска в начало сегмента. Он прибавляет значение каждого элемента таблицы перемещений к значению начального адреса сегмента. Затем он устанавливает DS и ES на адрес начала PSP; устанавливает SS равным адресу PSP, плюс 100Н, плюс значение смещения сегмента стека из заголовка; устанавливает в SP размер стека; устанавливает в CS адрес PSP, плюс 100Н, плюс величину 580 Часть VI. Специальные темы
смещения сегмента кода из заголовка. Загрузчик также устанавливает в IP смещение из элемента данных с адресом 14Н в заголовке. Пара CS:IP указывает на первую подлежащую выполнению инструкцию в сегменте кода программы. Поля в PSP включают в себя область параметра 1, начинающуюся со смещения 5СН, область параметра 2, начинающуюся со смещения 6СН, и область обмена с диском по умолчанию, начинающуюся со смещения 80Н. Резидентные программы необходимо запускать перед запуском обычных программ. Они переводятся в резидентный режим с помощью функции 31Н прерывания INT 21Н, причем в регистре DX необходимо указывать размер резидентной части. Вопросы и упражнения 1. (а) Где система хранит префикс сегмента программы (PSP)? (б) Каков его размер? 2. Каково назначение следующих полей в PSP? (а) Первых двух байтов, содержащих CD 20H, (б) Байтов 18—2ВН таблицы дескрипторов файлов по умолчанию, (в) Байтов 2С—2DH сегментного адреса среды программы, (г) 80—FFH установленного по умолчанию DTA. 3. Программа .СОМ загружена так, что ее PSP начинается с адреса ЗАВ6[0]Н. Какой адрес загрузчик программы поместит в регистр (a) CS, (б) DS, (в) ES, (г) SS. 4. Карта связей для .ЕХЕ-модуля выглядит так: Начало ооооон 00050Н 00080Н Конец 0004FH 0007ВН 000ACH Длина 00050Н 0002СН 0002DH Имя STACK CODESEG DATASEG Класс STACK CODE DATA Загрузчик размещает программу так, что PSP начинается с адреса 2АС6[0]Н. Приведя необходимые вычисления, определите значения во время загрузки в следующих регистрах: (a) SS, (б) SP, (в) CS, (г) DS, (д) ES. Резидентные программы обычно перехватывают ввод с клавиатуры. Какой адрес при этом изменяется? Чем существенно различаются завершение выполнения обычной программы и программы, остающейся резидентной? Глава 23. Загрузка программы и оверлеи 581
Часть VII Обзорная информация Глава 24. Области данных BIOS, прерывания и порты Глава 25. Операторы и директивы Глава 26. Набор инструкций PC
Глава 24 Области данных BIOS, прерывания и порты Цель: описать области данных BIOS, обработчики прерываний и операции с портами. Введение BIOS содержит обширный набор процедур ввода/вывода и таблиц, отражающих состояние различных системных устройств. И сама операционная система, и прикладные программы обращаются к процедурам BIOS для организации взаимодействия с устройствами, подсоединенными к системе. К BIOS можно обращаться, вызывая программные прерывания. В этой главе рассмотрены области данных BIOS, процедуры прерываний, прерывания BIOS от ООН до 1ВН, прерывание DOS INT 21Н и системные порты. Процесс загрузки В PC ПЗУ начинается с адреса в памяти FFFF0H. При включении компьютера процессор входит в состояние сброса, все ячейки памяти устанавливаются в 0, выполняется проверка четности для всего ОЗУ, регистр CS устанавливается в FFFF[0]H, a IP - в 0. Поэтому первая инструкция, подлежащая выполнению, начинается с адреса FFFF:0 - с точки начала BIOS. В BIOS по адресу 40[0]:72Н хранится значение 1234Н, указывающее, что при последующих перезагрузках сочетанием клавиш Ctrl+Alt+Del нет необходимости вновь полностью выполнять тестирование системы. BIOS проверяет различные порты, чтобы опознать и проинициализировать подсоединенные устройства, используя INT 11Н (определение оборудования) и INT 12H (определение объема памяти). После этого, начиная с адреса 0 в оперативной памяти, BIOS размещает таблицу векторов прерываний, содержащую адреса обработчиков прерываний. Затем BIOS определяет, есть ли диск, содержащий системные файлы, и, если он обнаружен, вызывает прерывание INT 19H, чтобы загрузить в память первый сектор диска, содержащий стартовый загрузчик. Эта программа - временная операционная система, которой программа BIOS передает управление после ее загрузки с диска в память. Начальный загрузчик предназначен только для загрузки в память первой части операционной системы. Глава 24. Области данных BIOS, прерывания и порты 583
Область данных BIOS BIOS содержит собственную 256-байтовую (100Н) область данных в младших адресах памяти, начиная с адреса сегмента 40[0]Н. Ее поля содержат данные в виде с переставленными байтами. Эти поля перечислены ниже в порядке возрастания смещения. Область данных последовательных портов ООН—07Н Четыре слова, содержащие адреса четырех последовательных портов - СОМ1—COM4. Область данных параллельных портов 08Н—0FH Четыре слова, содержащие адреса четырех параллельных портов - LPT1—LPT4. Область данных системного оборудования ЮН—ПН Состояние оборудования - примитивное отражение состояния подключенных устройств. Можно вызвать прерывание INT 11Н, которое поместит в АХ следующую информацию: Биты Устройство 15, 14 Число присоединенных параллельных портов 11—9 Число присоединенных последовательных (RS232) портов 7, 6 Число Дисководов (биты 00 -1 дисковод, 01-2 дисковода, 10-3 дисковода, и 11 - 4 дисковода) 5,4 Начальный видеоре&им: биты 00 - не используется, 01 - режим 40x25, цветной, 10 - режим 80x25, цветной, 11 - режим 80x25, монохромный 2 Устройство указания (мышь); если 1 - установлена 1 1 - присутствует математический сопроцессор 0 1 - присутствует дисковод Область дополнительных данных 12Н - тестовые флаги производителя. Область данных о размере памяти 13Н—14Н - объем памяти на системной плате, в килобайтах. 15Н—16Н - объем расширенной памяти, в килобайтах. 584 Часть VII. Обзорная информация
Область данных клавиатуры 1 17Н - первый байт текущего состояния клавиатуры: Бит Значение Бит Значение 7 Режим Insert активен 3 Нажата правая клавиша Alt 6 Режим Caps Lock активен 2 Нажата правая клавиша Ctrl 5 Режим Num Lock активен 1 Нажата левая клавиша Alt 4 Режим Scroll Lock активен 0 Нажата левая клавиша Ctrl "Активен" означает, что соответствующая клавиша была нажата и режим установлен во включенное состояние. "Нажата" означает, что в момент просмотра BIOS соответствующая клавиша удерживалась нажатой. 18Н - второй байт текущего состояния клавиатуры: Бит Значение Бит Значение 7 Клавиша Insert нажата 3 Нажата клавиша Ctrl/Num Lock 6 Клавиша Caps Lock нажата 2 Нажата клавиша SysReq 5 Клавиша Num Lock нажата 1 Нажата левая клавиша Alt 4 Клавиша Scroll Lock нажата 0 Нажата левая клавиша Ctrl 19Н - альтернативный буфер клавиатуры для ASCII-символов. 1 АН—1ВН - указатель на начало буфера клавиатуры. 1СН—1DH - указатель на конец буфера клавиатуры. 1ЕН—3DH - буфер клавиатуры (32 байта). Область данных дисководов ЗЕН - состояние поиска диска. Бит 0 относится к дисководу А, 1 - к дисководу В, 2 - к С, и 3 - к D. Бит, установленный в 0, означает, что следующая операция поиска должна перейти к цилиндру 0, чтобы перекалибровать дисковод. 3FH - состояние привода дисковода. Если бит 7 установлен в 1, то ведется запись. Бит 0 относится к дисководу А, 1 - к дисководу В, 2 - к С, и 3v— к D. 40Н - счетчик времени до выключения привода. 41Н - состояние диска, указывающее на ошибку при последней операции с дискетой: Глава 24. Области данных BIOS, прерывания и порты 585
Ошибок нет Некорректный параметр дисковода Не найдена метка адреса Ошибка защиты от записи Сектор не найден Активен флаг замены дискеты Перегрузка DMA 8Н 09Н Попытка выхода DMA за пределы 64 Кбайт ОСН Тип носителя не найден ООН 01Н 02Н ОЗН 04Н 06Н 08Н 42Н—4Ш - состояние контроллера дисковода. Область данных видео 1 49Н-текущий видеорежим, указываемый единичным битом: 1 ОН CRC-ошибка при чтении 20Н Ошибка контроллера 40Н Неудача поиска 80Н Устройство не готово Бит 7 6 5 4 Режим Монохромный 640x200 монохромный 320x200 монохромный 320x200 цветной Бит 3 2 1 0 Режим 80x25 цветной 80x25 монохромный 40x25 цветной 40x25 монохромный ВН - количество столбцов на экране. DH - объем буфера видеостраницы. FH - смещение начала видеобуфера. 4АН- 4СН- 4ЕН- 50Н—5FH - 8 слов, указывающих текущие начальные адреса каждой из восьми видеостраниц, нумеруемых 0—7. 60Н—61Н - начальная и конечная линии курсора. 62Н - номер страницы, отображаемой в данный момент. 63Н—64Н - адрес порта активного дисплея (монохромный - 03В4Н, цветной - 03D4H). 65Н - текущее значение в регистре управления видеорежимом. 66Н - текущая цветовая палитра. Область системных данных 67Н—68Н - счетчик даты. 69Н—6АН - регистр проверки циклической избыточности (CRC). 586 Часть VII. Обзорная информация
6ВН - последнее значение ввода. 6СН—6FH - таймер, хранит время в последовательности с переставленными данными. Значение обновляется на 1 каждый "тик" - это 1/18,2 секунды (или около 55 миллисекунд). Умножение этого числа на 18,2 даст число секунд, прошедших с последней полуночи. 70Н - переполнение таймера (1, если таймер прошел через полночь). 71Н - нажатие клавиш Ctrl+Break устанавливает бит 7 этого байта в 1. 72Н—73Н - флаг сброса памяти. Если значение равно 1234Н, то нажатие сочетания клавиш Ctrl+Alt+Del вызывает перезагрузку системы. Область данных жесткого диска 74Н - состояние последней операции с жестким диском (см. гл. 19). 75Н - число присоединенных жестких дисков. Область данных интервалов ожидания (time-out intervals) 78Н—7ВН - интервал ожидания для параллельных портов (LPT1—LPT4). 7СН—7FH - интервал ожидания для последовательных портов (СОМ1— COM4). Область данных клавиатуры 2 80—81Н - адрес смещения начала буфера клавиатуры. 82Н—83Н - адрес смещения конца буфера клавиатуры. Область видеоданных 2 84Н - число строк на экране минус 1. 85Н—86Н - высота символа, в строках растра. 87Н - информация о видео, по битам: Бит Значение 0 Если 1, то включена эмуляция текстового режима для курсора 3 Если 1, видеоподсистема неактивна 6—5 Объем видеопамяти (если 11 - 256 Кбайт или более) 7 То же, что и бит 7 в номере видеорежима, передаваемого функцией ООН прерывания INT 1 ОН 88Н - различная информация о видеосистеме. 89Н - различные флаги, по битам: Глава 24. Области данных BIOS, прерывания и порты 587
0 - если 1, то режим VGA активен; 1 - если 1, то суммирование по шкале серого разрешено; 2 - если 0, то монитор цветной, если 1 - монохромный; 3 - если 1, то отключена загрузка палитры по умолчанию; биты 4 и 7 определяют число строк растра в текстовом режиме: Бит 4 0 0 1 Бит 7 0 1 0 Режим 350 строк 400 строк 200 строк Область данных дисководов/жестких дисков 8ВН—95Н - состояние контроллера и ошибки. Область данных клавиатуры 3 96Н - флаги состояния и типа клавиатуры: Бит Значение Бит Значение 7 Идет процесс чтения ID 3 Нажата правая клавиша Alt 6 Последний код - АСК 2 Нажата правая клавиша Ctrl 5 Принудительно включить 1 Последний скан-код - E0H NumLock, если чтение ID и КВХ 4 Установлена расширенная 0 Последний скан-код - Е1Н клавиатура 97Н - состояние индикаторов на клавиатуре (бит 0 - индикатор ScrollLock, 1 - NumLock, 2 - CapsLock). Область данных часов реального времени 98Н—А7Н - состояние флагов ожидания. Область данных указателей сохранения А8Н—АВН - указатели на область сохранения BIOS. Первый адрес (в формате сегмент:смещение) указывает на таблицу сохранения указателей видео. 588 Часть VII. Обзорная информация
Область вспомогательных данных 2 АСН—FFH - зарезервированы системой для внутреннего использования. Следующие области данных BIOS размещены в верхней памяти:. А0000—AFFFF - область буфера видеоадаптера. В0000—B0FFF - монохромный видеобуфер. В8000—B0FA0 - область цветного текстового видеобуфера. F0000—FFFFF - область информации ПЗУ BIOS. Обработчики прерываний Операция прерывания приостанавливает выполнение программы, чтобы система могла выполнить какие-то действия. Программа обработки прерывания выполняется и обычно возвращает управление вызвавшей программе, которая продолжает выполняться. BIOS содержит обработчики прерываний ООН—1FH, aDOS-INT20H—3FH. Таблица векторов прерываний При включении компьютера система размещает таблицу векторов прерываний в ячейках 0000Н—03FFH основной памяти. Таблица содержит 256 (100Н) адресов прерываний, каждый из которых - 4 байта, содержащие адрес в форме смещение:сегмент (IP:CS). Операнд инструкции прерывания, например, ЕМТ 05Н, указывает на тип запроса. Поскольку в таблице 256 записей, каждая длиной по 4 байта, таблица занимает первые 1024 байта памяти, от 0000Н до 03FFH. Каждый адрес в таблице указывает на BIOS- или DOS-программу, решающую конкретную задачу. Поэтому байты 0—3 содержат адрес прерывания ООН, байты 4—7 - адрес прерывания 01Н и т.д. Вот функции некоторых прерываний: Прерывание Назначение ООН Деление на ноль 01Н Пошаговое выполнение Прерывание Назначение 14Н Прерывание последовательных портов 16Н Прерывание клавиатуры 17Н Прерывание принтера 02Н Немаскируемое прерывание (Non-Maskable Interrupt, NMI) Глава 24. Области данных BIOS, прерывания и порты 589
озн 04Н 05Н 08Н 09Н ОЕН ЮН ПН 12Н 13Н Адрес точки останова (breakpoint) Переполнение Печать экрана (print screen) Интервальный таймер BIOS-прерывание клавиатуры Прерывание диска 19Н 1АН 1ВН 1СН 1DH 1ЕН Работа с дисплеем 1FH Проверка аппаратуры 21Н Проверка памяти ЗЗН Операции ввода/ вывода с диском Начальный загрузчик Время дня Управление при клавиатурном останове (Ctrl+Break) Управление при прерывании с таймера Адрес видеотаблицы Адрес дисковой таблицы Адрес символа ASCII Прерывание DOS Прерывание мыши Обработка прерывания Вызов прерывания помещает в стек содержимое регистра флагов, CS и ЕР. Например, нажатие Ctrl+PrintScrn заставляет .BIOS вызвать программу, адрес которой указан для INT 05H в таблице векторов прерываний. Этот адрес расположен, начиная с ячейки 14Н (05Н х 4). Операция извлекает 4-байтовый адрес из ячеек, начиная с 0014Н, и помещает 2 байта в IP, и 2 - в CS. После этого адрес в CS:IP будет указывать на начало программы в BIOS, выводящей на печать содержимое области видеоадаптера. Прерывание возвращает управление инструкцией IRET (Interrupt Return - возврат из прерывания), извлекающей IP, CS и содержимое регистра флагов из стека и передающей управление в точку за инструкцией INT. Внешние и внутренние прерывания Внешнее (external) прерывание вызывается устройством, внешним с точки зрения процессора. Внешние прерывания могут передаваться одним из двух способов: через немаскируемое прерывание (NMI) или через канал запроса прерываний (Interrupt request - INTR). Канал NMI докладывает об ошибках четности в памяти или об ошибках при вводе/выводе. Процессор всегда реагирует на такое прерывание, даже если реагирование на обычные прерывания 590 Часть VII. Обзорная информация
заблокировано инструкцией CLI, сбрасывающей флаг прерываний (Interrupt Flag - IF). Канал INTR передает запросы от внешних устройств (по прерываниям от 05Н до 0FH) для таймера, клавиатуры, последовательных портов, жестких дисков, дисководов и параллельных портов. Внутренние (internal) прерывания возникают в результате выполнения инструкций INT, операций деления, вызывающих переполнение, при работе в пошаговом режиме или при запросе внешнего прерывания, например, вводе-выводе с диска. Программы обычно используют немаскируемые внутренние прерывания для обращения к процедурам BIOS и DOS. Прерывания BIOS В этом параграфе описаны прерывания BIOS от ООН до 1ВН. Операции, не описанные здесь, могут выполняться только BIOS. INT ООН: деление на ноль Вызывается при попытке деления на ноль. Выводит сообщение и обычно останавливает систему. INT01H: пошаговый режим Используется DEBUG и другими программами отладки для пошагового выполнения программ. INT02H: немаскируемое прерывание Используется в различных аппаратных ситуациях, например, при ошибках проверки четности, и его вызов возможен всегда - он не блокируется инструкцией СП. INT 03H: точка останова Используется отладчиками для прерывания выполнения программы. Команды DEBUG G (Go) и Р (Proceed) вызывают это прерывание в соответствующих точках останова программы. DEBUG отключает пошаговый режим и позволяет программе выполняться вплоть до инструкции ESfT 03H, где DEBUG вновь устанавливает пошаговый режим. INT04H: переполнение Может вызываться при арифметических операциях, хотя обычно не предпринимается никаких действий. INT 05H: печать экрана Вызывает вывод на печать содержимого области видеоадаптера. Вызов INT 05Н активирует прерывание изнутри, а нажатие Ctrl+PrintScrn активирует его Глава 24. Области данных BIOS, прерывания и порты 591
извне. Операция разрешает прерывания и сохраняет позицию курсора. Регистры сохраняют свое содержимое. Адрес 50:00 в BIOS содержит состояние операции. INT 08H: системный таймер Аппаратное прерывание, обновляющее системное время и (при необходимости) дату. Программируемый чип таймера генерирует это прерывание каждые 54,9254 миллисекунды, приблизительно 18,2 раза в секунду. INT09H: прерывание клавиатуры Вызывается при нажатии или отпускании клавиши клавиатуры; подробно описано в гл. 10. INT 0BH, INT 0CH: управление последовательными устройствами Управляют последовательными портами, соответственно, СОМ1 и COM2. INT ODH, INT 0FH: управление параллельными устройствами Управляют параллельными портами, соответственно, LPT1 и LPT2. INT 0EH: управление дискетами Указывает на операции с дискетами, например, завершение операции ввода/ вывода. INT 10H: вывод на экран Выполняет ряд функций, связанных с дисплеем и выбираемых по коду в регистре АН, таких как установка курсора, вывод на экран и прокрутка. Подробно описано в гл. 9. INT 11H: определение оборудования Определяет необязательные устройства в системе и возвращает результат из адреса 40: ЮН в BIOS в регистр АХ (при включении система выполняет эту операцию и сохраняет значение из АХ в 40: ЮН; см. выше, в параграфе "Области данных BIOS"). INT 12H: определение размера памяти Возвращает в АХ объем основной памяти в килобайтах. INT 13H: операции с диском Выполняет ряд функций, выбираемых по коду в регистре АН. Эти функции проверяют состояние диска, читают и записывают сектора, проверяют запись, форматируют диски и считывают диагностическую информацию; подробно они рассмотрены в гл.19. 592 Часть VII. Обзорная информация
INT 14H: последовательный ввод/вывод Обрабатывает потоковый побитовый ввод/вывод (т. е. по одному биту за раз) через последовательный порт типа RS232. Регистр DX должен содержать код порта RS232 (0—3 для СОМ1, COM2, COM3 и COM4, соответственно). Конкретная функция выбирается из набора по коду в регистре АН: Функция ООН: проинициализировать коммуникационный порт. Установите в регистре AL следующие параметры (по битам): Скорость Четность Бит останова Длина слова (биты 7—5) (биты 4—3) (бит 2) (биты 1—0) 100 - 1200 бит/с 00 - нет проверки 0-1 10 - 7 бит 101 - 2400 бит/с 01- на нечетность 1-2 11-8 бит 110 - 4800 бит/с 10 - нет проверки 111 - 9600 бит/с 11 - на четность Вот пример установки порта СОМ1 в режим обмена со скоростью 1200 бит/с, без проверки четности, с одним битом останова и 8-битовым словом: MOV АН,ООН ;Запросить инициализацию порта MOV AL,11100011B ;Параметры MOV DX,00 ;Для порта COM1 INT 14H /Вызвать обработчик прерывания Операция возвращает состояние коммуникационного порта в регистр АХ (см. описание функции 03Н далее). Функция 01Н: передать символ. Загрузите в AL символ, подлежащий передаче, а в DX - номер порта. По завершении операция вернет в АН состояние порта (см. функцию 03Н). Если операция не смогла передать байт, она также устанавливает бит 7 регистра АН в 1, хотя обычно этот бит указывает на ошибку типа "закончился интервал ожидания" - time-out error. Перед использованием этой функции вызовите функцию ООН. Функция 02Н: принять символ. Загрузите номер порта в регистр DX. Операция считывает символ из порта в регистр AL. Она также помещает в АН состояние порта (см. функцию 03 для битов ошибок 7, 4, 3, 2 и 1). Поэтому ненулевое значение в АН указывает на ошибку обмена. Перед использованием этой функции вызовите функцию ООН. Функция ОЗН: вернуть состояние коммуникационного порта. Загрузите номер порта в регистр ЦХ. Операция возвращает состояние линии (из порта 03FDH) в регистр АН и состояние модема (из порта 03FEH) в AL: Глава 24. Области данных BIOS, прерывания и порты 593 38 — 5-741
Регистр АН (состояние линии) Регистр AL (состояние модема) Бит Значение Бит Значение 7 Нет ответа (time out) 7 Обнаружен сигнал линии приема 6 Сдвиговый регистр передачи пуст 6 Индикатор звонка 5 Регистр захвата передачи пуст 5 Готовы данные 4 Обнаружена ошибка 4 Разрешение на передачу прерывания 3 Ошибка кадрирования 3 Обнаружено изменение сигнал линии приема 2 Ошибка четности 2 Изменение в распознавателе звонка 1 Ошибка из-за превышения 1 Изменение готовности данных скорости (overrun error) О Данные готовы 0 Изменение разрешения на передачу Другие функции INT 14Н - это 04Н (расширенная инициализация) и 05Н (расширенное управление коммуникационным портом). Поскольку функции INT 14H ограничены скоростью 9600 бит/с, они подходят в основном для экспериментов с передачей данных. Значительно более быстрый, но более сложный метод - адресация портов напрямую с помощью инструкций INS и OUTS (здесь не рассматривается). INT 15H: системные функций Эта сложная операция выполняет ряд функций, выбираемых по коду в регистре АН, включая следующие: 21Н - тестирование при включении; 43Н - чтение состояния системы; 84Н - поддержка джойстика; 88Н - определение размера расщиренной памяти; 89Н - переключение процессора в защищенный режим; С2Н - интерфейс мыши. INT 16H: ввод с клавиатуры Выполняет ряд функций, выбираемых кодом в регистре АН. Подробно это прерывание описано в гл. 10. 594 Часть VII. Обзорная информация
INT 17H: вывод на принтер Предоставляет ряд функций для печати через BIOS; рассматривалось в гл. 20. INT 18H: вызов ROM BASIC Вызывается из BIOS, если система запускается без диска, на котором есть системные файлы. INT 19H: стартовый загрузчик Если диск (или дискета) с операционной системой обнаружена, эта операция считывает в память первый сектор диска с дорожки 0, в загрузочную область памяти по адресу 7С00Н и передает управление по этому адресу. Эту операцию можно использовать как программное прерывание; она не очищает экран и не инициализирует данные в ПЗУ BIOS. INT 1 АН: прочитать/установить время Позволяет читать/устанавливать время дня согласно коду функции в регистре АН. • ООН - прочитать показания системного таймера. Возвращает старшие разряды результата в регистр СХ, а младшие - в DX. Если таймер отработал более чем 24 часа после последнего прочтения, операция установит AL в ненулевое значение. • 01Н - установить значение системного таймера. Загрузите старшие разряды значения в СХ, а младшие - в DX. • 02Н—07Н - эти функции устанавливают время и дату для служб реального времени. Чтобы определить, как долго выполняется какая-то программа, установите таймер в 0 в начале и прочитайте его значение в конце выполнения программы. INT 1BH: получить управление при прерывании с клавиатуры При нажатии Ctrl+Break BIOS передает управление этому обработчику, устанавливающему флаг. Функции прерывания INT 21Н Далее перечислены функции прерывания DOS INT 21H, существенные для этой книги и выбираемые по коду функции в регистре АН: 01Н - ввод с клавиатуры с отображением на экране (См. гл. 10.) Глава 24. Области данных BIOS, прерывания и порты 595 38*
02Н - вывод символа на экран (См. гл. 8.) ОЗН - коммуникационный ввод Считывает символ из последовательного порта в регистр AL; это примитивная функция, и чаще используют функции прерывания BIOS INT 14H. 04Н - коммуникационный вывод Выводимый символ помещается в регистр DL; лучше использовать BIOS INT 14H. 05Н - вывод на принтер Для печати одиночных символов: MOV АН,05Н /Запросить печать MOV Dh,символ ; одиночного символа INT 21H /Вызвать обработчик прерывания 06Н - прямое обращение к клавиатуре и дисплею Эта довольно странная функция может передавать любой символ или управляющий код. Есть две ее версии - для ввода с клавиатуры и для вывода на экран. Для ввода поместите в DL код 0FFH. Если символов в буфере клавиатуры нет, операция устанавливает флаг ZF в 1 и не ожидает ввода. Если в буфере клавиатуры есть символ, операция помещает символ в AL и сбрасывает в 0 флаг ZF. Операция не отображает символ на экране и не воспринимает сочетания клавиш Ctrl+Break и CtrH-PrintScrn. Ненулевое значение в регистре AL представляет стандартный ASCII-символ, например, букву или число. Ноль в AL означает, что пользователь нажал расширенную функциональную клавишу, например, Ноте, F1 или PageUp. Чтобы получить их скэн-код в AL, незамедлительно выполните операцию INT 21H. Для вывода на экран загрузите ASCII-символ (не 0FFH) в регистр DL. 07Н - прямой ввод с клавиатуры без отображения на экране (См. гл. 11.) 08Н - ввод с клавиатуры без отображения на экране (См. гл. 10.) 09Н - вывод строки (См. гл. 8.) ОАН - буферизованный ввод с клавиатуры (См. гл. 10.) 596 Часть VII. Обзорная информация
ОВН - проверка состояния клавиатуры (См. гл. 10.) ОСН - очистить буфер клавиатуры и вызвать ввод (См. гл. 10.) 0DH - сбросить состояние дискового накопителя (См. гл. 18.) ОЕН - выбрать дисковый накопитель по умолчанию (См. гл. 18.) 19Н - определить дисковый накопитель по умолчанию (См. гл. 18.) 1 АН - установить область обмена с диском (DTA) (См. гл. 18.) 1FH - получить блок параметров накопителя по умолчанию (См. гл. 18.) 25Н - установить вектор прерывания Когда пользователь нажимает Ctrl+Break или Ctrl+C, программа обычно завершается, и управление передается операционной системе. Возможно, вы захотите включить в свою программу собственный обработчик нажатия этих сочетаний клавиш. В приведенном ниже фрагменте программы функция 25Н используется для изменения адреса обработчика нажатий Ctrl+Break в таблице векторов прерываний. Это сочетание клавиш соответствует прерыванию INT 23H, и в программе вместо него вызывается собственный обработчик - C10BREAK, в который вы можете включить любые необходимые действия. MOV АН,25Н /Запросить установку адреса MOV AL,23H ; для прерывания INT 23H LEA DX,C10BREAK ;Адрес нового обработчика INT 21H /Вызвать обработчик прерывания C10BREAK: ,-Обработчик нажатия Ctrl+Break IRET /Возврат из обработчика прерывания 29Н-разобрать имя файла (См. гл. 18.) Глава 24. Области данных BIOS, прерывания и порты 597
2АН - получить системную дату Возвращает следующие двоичные значения: в регистр AL - день недели (воскресенье - 0, понедельник - 1 и т.д.), СХ - год (1980—2099), DH - месяц (01—12), DL-день (01—31). 2ВН - установить системную дату Загрузите в регистры следующие двоичные значения: в СХ - год (1980— 2099), в DH - месяц (01—12), в DL - день (01—31). После выполнения в случае успеха в регистр AL помещается код ООН, а при неудаче - FFH. 2СН - получить системное время Возвращает следующие двоичные значения: в регистр СН - часы (00—23, где 00 - полночь), в CL - минуты (00—59), в DH - секунды (00—59), в DL - сотые доли секунды (00—99). 2DH - установить системное время Поместите в регистры следующие двоичные значения: в регистр СН - часы (00—23, где 00 - полночь), в CL - минуты (00—59), в DH - секунды (00—59), в DL- сотые доли секунды (00—99). После выполнения в случае успеха в регистр AL помещается код ООН, а при неудаче - FFH. 2ЕН - установить/отменить проверку записи на диск (См. гл. 18.) 2FH - получить адрес текущей области обмена с диском (DTA) (См. гл. 18.) 31Н - завершить и оставить резидентной (См. гл. 23.) 32Н - получить блок параметров накопителя (DPB) (См. гл. 18.) 3300Н - получить состояние Ctrl+C Если флаг Ctrl+C установлен в 0, это заставляет систему проверять, нажаты ли клавиши Ctrl+C только при выполнении 01Н—0CH операций ввода/вывода. Если флаг установлен в 1, система проверяет нажатие этих клавиш и при выполнении других функций. Для получения состояния этого флага поместите код подфункции ООН в регистр AL. В регистр DL будет помещено значение ООН, если проверка отключена, и 01Н - если включена. 598 Часть VII. Обзорная информация
3301Н - установить состояние Ctrl+C Если флаг Ctrl+C установлен в 0, это заставляет систему проверять, нажаты ли Ctrl+C только при выполнении операций ввода/вывода 01Н—ОСН. Если флаг установлен в 1, система проверяет нажатие этих клавиш и при выполнении других функций. Для изменения состояния этого флага поместите код подфункции 01Н в регистр AL. Поместите в регистр DL значение ООН, чтобы отключить проверку, и 01Н - чтобы ее включить. 3305Н - получить стартовый накопитель Помещает в DL номер устройства (1 - дисковод А, и т.д.), с которого загружались системные файлы. 3306Н - получить версию DOS Возвращает в регистры следующие значения: BL- номер основной версии, например, 3 для версии 3.11. ВН - номер подверсии, например, шестнадцатеричное В (11) для 3.11. DL - номер ревизии в битах 2—0. DH - флаг версии DOS (указывает, работает ли система в основной памяти, в верхней памяти или в ПЗУ). Хотя командой SETVER номер версии можно заменить, функция 3306Н всегда возвращает истинный номер версии. 35Н - получить адрес таблицы векторов прерываний (См. гл. 23.) 36Н - получить объем свободного пространства на диске (См. гл. 18.) 38Н - получить/установить информацию для локализации Поддерживает несколько функций, связанных с информацией, специфичной для различных стран, например, символы и форматы национальных валют, разделители для тысяч и десятичных дробей, и форматы представления даты и времени. Загрузите регистр DX для этой операции: значение 0FFFFH, чтобы установить код страны, которую система будет использовать до его переназначения, или любое другое значение, чтобы узнать, какой код страны используется в данный момент. 39Н - создать поддиректорию (команда MKDIR) (См. гл. 18.) Глава 24. Области данных BIOS, прерывания и порты 599
ЗАН - удалить поддиректорию (команда RMDIR) (См. гл. 1^8.) ЗВН - изменить текущую директорию (команда CHDIR) (См. гл. 18.) ЗСН - создать файл (См. гл. 17.) 3DH - открыть файл (См. гл. 17.) ЗЕН - закрыть файл (См. гл. 17.) 3FH - прочитать файл/устройство (См. гл.гл. 9 и 17.) 40Н - записать в файл/устройство с помощью дескриптора (См. гл.гл. 8, 17 и 20.) 41Н - удалить файл из структуры директорий (См. гл. 18.) 42Н - переместить файловый указатель (См. гл. 17.) 43Н - прочитать/изменить атрибуты файла (См. гл. 18.) 44Н - управление вводом/выводом для устройств Поддерживает обширный набор операций для проверки устройств и обмена данными с ними (см. гл. 18). 45Н - продублировать дескриптор файла (См. гл. 18.) 46Н - принудительно продублировать дескриптор файла (См. гл. 18.) 47Н - получить текущую директорию (См. гл. 18.) 600 Часть VII. Обзорная информация
48Н - выделить блок памяти (См. гл. 23.) 49Н - освободить выделенный блок памяти (См. гл. 23.) 4АН - установить размер выделенного блока памяти (См. гл. 23.) 4ВН - загрузить/выполнить программу (См. гл. 23.) 4СН-завершить выполнение программы Стандартная операция для завершения выполнения программы (см. гл. 4). 4DH - получить код возврата подпроцесса (См. гл. 23.) 4ЕН - найти первую соответствующую условиям запись в директории (См. гл. 18.) 4FH - найти следующую соответствующую условиям запись в директории (См. гл. 18.) 50Н - установить адрес префикса сегмента программы (PSP) Загрузите в регистр ВХ адрес смещения PSP текущей программы. Функция не возвращает значений. 51Н - получить адрес префикса сегмента программы (PSP) Возвращает адрес смещения PSP текущей программы (см. гл. 23). 54Н - получить состояние проверки (См. гл. 18.) 56Н - переименовать файл (См. гл. 18.) 57Н - получить/установить имя и дату для файла (См. гл. 18.) Глава 24. Области данных BIOS, прерывания и порты 601
5800Н - получить стратегию выделения памяти (См. гл. 23.) 5801Н - установить стратегию выделения памяти (См. гл. 23.) 5802Н - получить связь с верхней памятью (См. гл. 23.) 5803Н - установить связь с верхней памятью (См. гл. 23.) 59Н - получить расширенный код ошибки (См. гл. 18.) 5АН - создать временный файл (См. гл. 18.) 5ВН - создать новый файл (См. гл. 18.) 5DH - установить расширенную ошибку Загрузите в регистр DX адрес (смещение) таблицы с информацией об ошибках. Выполненная позже функция 59Н (получить расширенный код ошибки) извлечет таблицу (см. описание функции 59Н в гл. 18). 62Н - получить адрес префикса сегмента программы (PSP) Идентична функции 51Н. 65Н - получить расширенную информацию о стране Поддерживает ряд операций, связанных с представлением информации в форматах, специфичных для разных стран. 67Н - установить максимальное число дескрипторов (См. гл. 23.) 68Н - передать файл (См. гл. 18.) 6СН - расширенное открытие файла Сочетает функции ЗСН (создать файл), 3DH (открыть файл) и 5ВН (создать новый файл) (см. гл. 18). 602 Часть VII. Обзорная информация
Порты Порт - это устройство, соединяющее процессор с внешним миром. Через порт процессор принимает сигналы от устройств ввода и передает сигналы устройствам вывода. К портам обращаются по их адресам, в пределах 000Н— 3FFH, т.е. доступно 1024 порта. Заметьте, что адреса портов - это не адреса в оперативной памяти. Далее перечислены адреса основных портов. • 020Н—021Н - порты программируемого контроллера прерываний (programmable interrupt controller). Контроллер отвечает на внешние прерывания от таких устройств, как клавиатура, дисковые накопители и системные часы. Запросы от этих устройств прерывают работу процессора, и он обрабатывает запрос. Порты имеют следующие назначения: • 20Н - адрес порта 8259, передающего сигнал конца прерывания (EOI), когда операция пересылает ему значение 20Н; 21Н - регистр маски прерывания 8259, указывающий, что прерывание разрешено (0) или запрещено (1). Номера битов и IRQ совпадают: • IRQ 0 1 2 3 Назначение Системный таймер Клавиатура Вспомогательный канал ввода/вывода Последовательный порт (COM2) IRQ 4 5 6 7 Назначение Последовательный порт (СОМ1) Параллельный порт (LPT2) Контроллер дисководов Параллельный порт (LPT1) 040Н—042Н - порты 8253 - программируемого таймера (programmable interval timer). Эти три порта имеют следующие назначения: • 40Н - канал 0 регистра счетчика, прерывает систему 18,2 раза в секунду (счетчик увеличивает значение от на 1 от 0 до 65535 приблизительно каждые 55 миллисекунд) и обновляет показание системных часов; • 41Н - канал 1 прерывает контроллер прямого доступа к памяти (DMA) для регенерации памяти (микросхемы динамического ОЗУ хранят данные без регенерации всего несколько миллисекунд); • 42Н - канал 2, управляет встроенным динамиком (спикером), подсоединенным через порт 61Н. 060Н - клавиатура. Передает скэн-коды клавиатуры. Глава 24. Области данных BIOS, прерывания и порты 603
061Н - порт микросхемы 8255 - интерфейса, управляющего следующими функциями (по значениям битов): • Бит 0 - блокировка таймера 8253 (порт 42Н); • Бит 1 - блокировка спикера; • Бит 4 - разрешение ошибок четности в ОЗУ; • Бит 6 - отключение щелчка клавиатуры; • Бит 7 - разрешение клавиатуры. 200Н—20FH - игровой контроллер. 278Н—27FH - параллельный порт принтера (LPT3). 2F8H—2FFH - последовательный порт (COM2). 378Н—37FH - параллельный порт принтера (LPT2). ЗВОН—ЗВВН - порт монохромного дисплея. ЗВСН—3BFH - параллельный порт принтера (LPT1). ЗСОН—3CFH - VGA-порт. 3DAH—3DFH - VGA-порт для чтения регистра состояния ЭЛТ. 3F0H—3F7H - контроллер диска. 3F8H—3FFH - последовательный порт (СОМ1). Хотя обычно для ввода/вывода используются прерывания (INT), можно без посредства BIOS обращаться к портам 21Н, 40—42Н, 60Н, 61Н и 201Н. Например, при загрузке программа из ПЗУ BIOS просматривает систему, чтобы определить адреса параллельных и последовательных портов. Если адреса найдены, BIOS помещает адреса последовательных портов в своей области данных, по адресам, начиная с 40:00Н, а параллельных - начиная с 40:08Н. Каждому порту соответствует одно слово в памяти. Таблица в BIOS системы с двумя параллельными и двумя последовательными портами может выглядеть, например, так: 40 40 40 40 00 02 04 06 F803 F802 0000 0000 СОМ1 COM2 Не используется Не используется 40 40 40 40 08 0А ОС .0Е 7803 7802 0000 0000 LPT1 LPT2 Не используется Не используется Например, чтобы использовать прерывание BIOS INT 17H для печати символа, поместите номер принтерного порта в регистр DX: MOV АН,ООН ;Запросить печать MOV AL,символ ;Печатаемый символ MOV DX,0 ;Порт 0 (LPT1) INT 17H ;Вызвать обработчик прерывания 604 Часть VII. Обзорная информация
Некоторые программы позволяют печатать только через порт LPT1. Для системы с двумя принтерными портами, установленными как LPT1 и LPT2, приведенный ниже фрагмент программы может использоваться, чтобы поменять местами их адреса в области данных BIOS. Сегмент данных BIOSDATA определяет область данных BIOS, a PARLPORT определяет первое из четырех слов адресов: ;Область данных BIOS ;Адреса портов принтеров ;4 слова Адрес начала BIOS - в сегментный регистр Адрес LPT1 - в АХ Адрес LPT2 - в ВХ Меняем местами адреса портов BIOSDATA PARLPORT BIOSDATA SEGMENT ORG DW ENDS ASSUME MOV MOV MOV MOV MOV MOV AT 4OH 8H 4 DUP(?) DS:BIOSDATA AX,BIOSDATA DS,AX AX, PARLPORT (0) BX,PARLPORT(2) PARLPORT(0),BX PARLPORT(2) , AX Инструкции IN и OUT Инструкции IN и OUT могут выполнять ввод/вывод непосредственно на уровне портов. Инструкция IN передает данные из приемного порта в регистр AL, если принимается байт, и в АХ - если слово, а инструкция OUT передает в порт-передатчик слова из регистра АХ или байты - из AL. Их форматы: [метка: ] [метка: ] IN OUT регистр-аккумулятор, порт пор т, регистр-а ккумулятор Адрес порта можно указывать как статически, так и динамически. Статически: используйте непосредственный операнд - число от 0 до 255, например в виде IN AL,номер ;Ввод байтов из порта с указанным номером OUT номер,AL ;Вывод байтов через порт с указанным номером Динамически: используйте регистр DX в качестве источника адреса - в нем размещаются значения от 0 до 65535. Можно использовать такой способ указания адреса порта для последовательного перебора портов, повторно увеличивая содержимое DX на 1. Следующий фрагмент обращается к порту 60Н: MOV DX,60H ;Порт 60Н (клавиатура) IN AL,DX ;Получить из порта байт Глава 24. Области данных BIOS, прерывания и порты 605
Генерация случайных чисел Приведенный ниже фрагмент программы можно использовать в качестве генератора случайных чисел. Они считываются с выхода микросхемы-таймера 8253: MOV АХ, 0 * OUT 43H,AL IN AL,40H IN AL,40H ;Обращение к таймеру через порт 43Н Выполняем два обращения к порту 4ОН В регистре AL будет случайное число. Ввод/вывод строк Инструкции INSn и OUTSn могут передавать данные и работают в значительной степени так же, как и строковые инструкции, рассмотренные в гл. 11. Инструкции INSn Эта группа включает 4 инструкции: Инструкция INS INSB INSW INSD Процессоры 80286+ 80286+ 80286+ 80386+ Пример INS ES:назначение,DX REP INSB REP INSW REP INSD Назначение (или строка-приемник) - это последовательность байтов, адресуемая парой регистров ES:DI. В регистре DX содержится адрес входного порта. Обычно инструкции этой группы используются с префиксом REP, при этом в регистре СХ задается количество элементов (байтов, слов или двойных слов), подлежащих приему. Если флаг направления DF установлен в 0, значение в DI увеличивается с каждым принятым элементом, если DF установлен в 1, то значение в DI уменьшается по мере приема. В следующем фрагменте иллюстрируется применение инструкций INSn: MOV СХ,число ;Количество байтов LEA DI,назначение ;Смещение строки-приемника (в паре ES:DI) MOV DX, номер ; Номер порта REP INSB /Выполнить прием Инструкции OUTSn Эта группа включает 4 инструкции: 606 Часть VII. Обзорная информация
Инструкция OUTS OUTSB OUTSW OUTSD Процессоры 80286+ 80286+ 80286+ 80386+ Пример t OUTS DX,DS:источник REP OUTSB REP OUTSW REP OUTSD Источник - это последовательность байтов, адресуемая парой регистров DS:SI. В регистре DX содержится адрес входного порта. Обычно инструкции этой группы используются с префиксом REP, при этом в регистре СХ задается количество элементов (байтов, слов или двойных слов), подлежащих приему. Если флаг направления DF установлен в 0, значение в SI увеличивается с каждым принятым элементом, если DF установлен в 1, то значение в SI уменьшается по мере приема. Вот пример применения инструкции OUTSB: MOV СХ, число ;Количество байтов LEA SI, источник /Смещение строки-источника (в паре DS:SI) MOV DX, номер ; Номер порта REP OUTSB ;Выполнить передачу Генерация звука Любой PC может генерировать звук, используя встроенный громкоговоритель (спикер), подсоединенный к портам 42Н, 43Н и 61Н. Для обращения к спикеру выполните следующие шаги. 1. Получите состояние порта 61Н и сохраните его. 2. Для включения громкоговорителя пошлите в порт 61Н последовательность битов, содержащую в битах 0 и 1 сочетание 11. Порт активирует микросхему Intel 8255 - программируемый периферийный интерфейс. 3. Для выключения спикера пошлите в порт 61Н строку, в которой биты 0 и 1 установлены в сочетание 00. Фрагмент программы, приведенный на рис. 24.1, генерирует последовательность звуков возрастающей частоты. Элемент данных DURATION указывает длительность одного звука, a TONE определяет базовую частоту. Сначала программа обращается к порту 61Н и сохраняет значение, возвращаемое операцией. Таймер генерирует сигнал каждые 55 миллисекунд, и этот сигнал вызывает изменение частоты. Инструкция CLI в программе позволяет установить постоянную частоту, сбрасывая флаг прерываний (IF) в 0. Содержимое TONE определяет частоту: большие значения задают низкие частоты, а малые - высокие. После того как программа воспроизводит очередной Глава 24. Области данных BIOS, прерывания и порты 607
звук, она увеличивает частоту TONE, сдвигая его вправо на 1 бит (фактически, деля значение пополам). Поскольку уменьшение значения TONE будет уменьшать время воспроизведения одного звука, программа также удваивает значение DURATION (опять же, с помощью сдвига, но влево). DURATION TONE А20: АЗО: А40: А50: .DATA DW DW .CODE IN PUSH -MOV OR OUT MOV LOOP OR OUT MOV LOOP DEC JNZ SHL SHR JNZ POP AND OUT 10000 512H AL,61H AX DX,DURATION AL,00000011B 61H,AL CX,TONE A40 AL,00000010B 61H,AL CX,TONE A50 DX A30 DURATION,1 TONE,l A20 AX AL,11111100B 61H,AL Длительность звука Частота Получить статус порта и сохранить Установить длительность Установить биты 0 и 1 (в 1) Передать спикеру Установить длительность Задержка Установить бит 1 (в 1) Передать 'спикеру 1 Установить длительность Задержка Уменьшить длительность Продолжить? нет, увеличить длительность ■Уменьшить частоту ■Не ноль? • Сброс порт значение Рис. 24.1. Генерация звука Программа заканчивается, когда TONE уменьшается до 0. Изначальные значения TONE и DURATION не имеют принципиального значения. Можно поэкспериментировать, задавая случайные их значения. Попробуйте также выполнить программу без инструкции CLI. Можно использовать различные сочетания звуков, например, чтобы привлечь внимание пользователя к каким-либо событиям. 608 Часть VII. Обзорная информация
Ключевые моменты • ПЗУ располагается в памяти, начиная с адреса FFFF0H. При включении компьютера процессор входит в состояние сброса, устанавливает в 0 все ячейки памяти, выполняет проверку четности для памяти и заносит в регистр CS значение FFFF[0]H, а в IP - 0. Поэтому первая выполняемая инструкция находится по адресу FFFF:0 (или FFFF0) - точке начала BIOS. • При загрузке BIOS проверяет различные порты, чтобы обнаружить и про- инициализировать обнаруженные устройства. После этого он помещает в начальные адреса памяти таблицу векторов прерываний, содержащую адреса обработчиков прерываний, возникающих в ходе работы. Еще две операции, выполняемые BIOS - определение присоединенных устройств и объема памяти. После этого BIOS обращается к первому сектору диска, содержащему начальный загрузчик. • BIOS хранит в нижней памяти собственную область данных, начиная с адреса сегмента 40[0]Н. В этой области данных содержится информация о параллельных и последовательных портах, системных устройствах, клавиатуре, дисковых накопителях, видеоконтроллере, жестком диске и часах реального времени. • Операнд инструкции INT, например, INT 12H, указывает тип запрашиваемой операции. Для каждого из возможных 256 значений операнда система хранит соответствующий адрес обработчика - в области памяти с адресами от 0000Н до 03FFH. • Прерывания BIOS имеют номера от ООН до 1FH и отвечают за печать экрана, работу с таймером, видеоконтроллером, дисководами, вывод на экран, определение устройств и объема памяти, ввод/вывод на диск, ввод с клавиатуры, коммуникации, вывод на принтер и начальную загрузку системы. • Прерывание DOS INT 21Н выполняет такие операции, как ввод с клавиатуры, вывод на дисплей, вывод на принтер, сброс состояния накопителей, открытие/закрытие файлов, их удаление, чтение/запись отдельных записей, создание резидентных программ, завершение программ и создание поддиректорий. • Через порты процессор получает сигналы от устройств ввода и передает сигналы устройствам вывода. Порты идентифицируются по их адресам, лежащим в диапазоне от 0000Н до 03FFH, то есть от 0 до 1023. • Любой PC может генерировать звук с помощью встроенного громкоговорителя (спикера), присоединенного к портам 42Н, 43Н и 61Н. Глава 24. Области данных BIOS, прерывания и порты 609 39 — 5-741
Вопросы и упражнения 1. Объясните разницу между внешними и внутренними прерываниями. 2. Объясните разницу между каналом NMI и каналом INTR. 3. (а) Какой адрес начала BIOS в памяти? (б) Как система передает управление по этому адресу при включении? 4. При загрузке BIOS выполняет INT ПН, 12Н и 19Н. Объясните назначение каждого прерывания. 5. Какой адрес начала области данных BIOS? 6. В области данных BIOS были обнаружены следующие двоичные значения. Для каждого из них объясните смысл единичных битов: (а) смещение 10— ПН, значение 01000100 01100111, (б) 17Н, значение 11101010, (в) 18Н, значение 00000001, (г) 96Н, значение 00011010. 7. Область данных BIOS содержит следующие шестнадцатеричные значения. В каждом случае определите назначение поля и смысл значений: (а) смещение 00—ОЗН, значение F8 03 F8 02, (б) 08—0BH, значение 78 03 00 00, (в) 13—14Н, значение 80 02, (г) 15—16Н, значение 00 10, (д) 4A-^tBH, значение 50 00, (е) 60—61Н, значение 0Е 0D, (ж) 84Н, значение 18. 8. Укажите, какие прерывания BIOS отвечают за (а) пошаговый режим выполнения, (б) обмен данными и коммуникации, (в) определение состояния оборудования, (г) печать экрана, (д) ввод/вывод с диска, (е) ввод с клавиатуры, (ж) клавиатуру, (з) вывод на экран, (и) вывод на принтер, (к) чтение и установку времени. 9. Укажите, какие функции прерывания ПМТ 21Н выполняют следующие действия: (а) перевод программы в резидентный режим, (б) получение адреса таблицы векторов прерываний, (в) создание поддиректории, (г) получение объема свободного пространства на диске, (д) получение адреса PSP, (е) создание нового файла, (ж) получение системного времени, (з) переименование файла. 10. Укажите назначение следующих функций прерывания INT 21H: (а) 05Н, (б) 09Н, (в) 0DH, (г) 19Н, (д) 2ВН, (е) 31Н, (ж) 36H, (з) ЗАН, (и) 42Н. 11. Модифицируйте программу, приведенную в конце параграфа "Порты" этой главы так, чтобы она меняла местами адреса портов СОМ1 и COM2. 12. Модифицируйте программу, приведенную на рис. 24.1 так, чтобы генерируемые звуки уменьшались по высоте - инициализируйте TONE значением 1, a DURATION - большим значением. При каждом обращении увеличивайте значение в TONE и уменьшайте значение в DURATION, завершая программу при достижении DURATION значения 0. 610 Часть VII. Обзорная информация
Глава 25 Операторы и директивы Цель: привести детальное описание директив и операторов языка ассемблера. Введение Некоторые особенности и элементы языка ассемблера вначале могут показаться несколько неожиданными. Однако, после ознакомления с наиболее простыми и общими возможностями, рассмотренными в первых главах, описания новых директив, операторов и одределений типов, приведенные в этой главе, станут вполне доступными для понимания, и их краткое описание может оказаться полезным. В руководствах по языку ассемблера можно найти полезную дополнительную информацию. Замечание, относящееся к Turbo Assembler (TASM): этот транслятор может выполняться либо в режиме MASM, либо в "идеальном" (Ideal) режиме. В последнем случае во многих ситуациях могут использоваться несколько другие правила и термины, а спецификации MASM будут выполняться не всегда. Спецификаторы типов Спецификаторы типов указывают размер переменной (элемента данных) или относительное расстояние до метки инструкции. Спецификаторы типов, указывающие размер переменных - это BYTE, WORD, DWORD, FWORD, QWORD и TBYTE. Расстояние до метки инструкции определяют спецификаторы NEAR, FAR и PROC. Адрес типа NEAR, указываемый просто в виде смещения, расположен в том же сегменте, что и исходная программная позиция. Адрес типа far, указываемый в виде пары сегмент:смещение, может использоваться для обращения к другому сегменту. Операторы PTR и THIS, так же, как директивы COM, EXTRN, LABEL и PROC, используют спецификаторы типов. Операторы Оператор предоставляет возможность анализа или изменения операндов во время трансляции. Операторы делятся на несколько групп: Глава 25. Операторы и директивы 611 39*
• Вычислительные операторы (calculation operators): арифметические, индексные, логические, операторы сдвига и операторы обращения к полям структур. • Макрооператоры (macro operators): группа операторов, описанию которых посвящена гл. 21. • Операторы записей (record operators): операторы MASK и WIDTH, рассмотренные далее в этой главе вместе с директивой RECORD. • Операторы соотношений (relational operators): EQ, GE, GT, LE, LT и NE. • Операторы сегментов (segment operators): OFFSET, SEG и переопределения сегментов. • Операторы типов (или атрибутов) (type or attribute operators): HIGH, HIGHWORD, LENGTH, LOW, LOWWORD, PTR, SHORT, SIZE, THIS и TYPE. Поскольку необязательно знать, к какой категории принадлежит конкретный оператор, в этой главе операторы рассматриваются в алфавитном порядке. Арифметические операторы Эти операторы включают в себя обычные знаки арифметических действий и выполняются в период трансляции. В большинстве случаев выполняемые ими вычисления вы можете выполнить сами, и основное преимущество применения этих операторов, что при трансляции (после очередного изменения программы) ассемблер автоматически пересчитает значения, задаваемые с помощью арифметических операторов. Далее приводится таблица этих операторов с примерами их применения и результатами: Знак + + - - * / MOD Значение Сложение Положительное Вычитание Обращение знака Умножение Деление Вычисление остатка при делении Пример FLDA+25 +FLDA FLDB-FLDA -FLDA value*3 value/3 valuel MOD value2 Результат Прибавляет 25 к значению в FLDA Воспринимает FLDA как положительное Вычисляет разницу между двумя адресами смещений. Изменяет знак FLDA Умножает value на 3 Делит value на 3 Возвращает остаток от деления valuel на value2 612 Часть VII. Обзорная информация
За исключением операторов - (вычитание) и + (сложение) все операторы работают только с целочисленными константами. Ниже приведены примеры использования операторов: valuel = 12 * 4 ;48 valuel = valuel /6 ;48 / 6 = 8 valuel = -valuel - 3 ;(-8) - (3) = -11 Арифметические операторы часто используются в сочетании с директивами присваивания, рассматриваемыми в параграфе "Директивы ". Операторы HIGH и HIGHWORD Оператор HIGH возвращает старший (первый слева) байт выражения, а HIGHWORD (появившийся в MASM 6.0) возвращает старшее слово выражения (для сравнения посмотрите описания операторов LOW и LOWWORD). Пример: EQUVAL EQU 1234H MOV CL,HIGH EQUVAL ; в CL будет помещено 12Н Операторы индексов Для косвенной адресации памяти операнд ссылается на базовый или индексный регистр, константу, переменную по смещению или просто на переменную. Оператор индекса, использующий квадратные скобки, действует как знак + (плюс). Это действие часто используется для адресации элементов таблиц. Для обращения к индексированным элементам памяти можно применять следующие операции: • Непосредственное число или имя в квадратных скобках в виде [константа]. Например, чтобы загрузить значение из пятого элемента PART_TBL в регистр CL, используется такая инструкция: PART_TBL DB 25 DUP(?) /Определение таблицы MOV CL,PART_TBL[4] ;Получить 5-ю запись (байт 5) Обратите внимание на то, что пятая запись в таблице адресуется индексом 4, поскольку нумерация начинается с нуля. • Базовый регистр ВХ в виде [ВХ], в сочетании с сегментным регистром DS или базовый регистр ВР в виде [ВР] в сочетании с сегментным регистром , SS. Например, воспользуемся адресом смещения в регистре ВХ (в паре DS:BX), и поместим считанное значение в регистр DX: MOV DX,[ВХ] ;Адресация с помощью базового регистра (парой DS:BX) Глава 25. Операторы и директивы 613
• Индексный регистр DI в виде [DI] или индексный регистр SI в виде [SI]. Оба эти регистра сочетаются с сегментным регистром DS. Например, воспользуемся значением из регистра SI в качестве смещения (в паре DS:SI) и поместим считанный элемент данных в регистр АХ: MOV AX,[SI] /Адресация с помощью индексного регистра (парой DS:SI) • Сочетание индексных регистров. Например, чтобы поместить значение из регистра АХ в память по адресу, равному сумме значений сегмента DS, смещения ВХ, смещения SI и 4, используется такая инструкция: %MOV [BX+SI+4],AX /Адресация сочетанием индексных регистров и константы. Первый операнд в этом примере может также указываться как [BX+SI]+4. Три элемента этого операнда можно указывать в любой последовательности, но нельзя сочетать два базовых регистра [ВХ+ВР] и два индексных регистра [DI+SI], В квадратных скобках должны быть только индексные регистры, чтобы ассемблер мог расценить выражение в квадратных скобках как индексный оператор. Оператор LENGTH Этот оператор возвращает число записей, определенных оператором DUP, как показано в следующем фрагменте: PART_TBL DW 10 DUP(?) MOV DX, LENGTH PART_TBL ;Возвращает длину 10 в регистр DX Если указанный операнд объявлялся без применения DUP, оператор LENGTH вернет значение 1 (что ограничивает его использование). См. также описания операторов SIZE и TYPE. Логические операторы Эти операторы работают с отдельными битами выражений: Оператор Формат использования Действие AND выражение! AND выражение2 Логическое И над битами OR выражение! OR выражение2 Логическое Или над битами XOR выражение! XOR выражение2 Логическое Исключающее Или над битами NOT NOT выражение! Инвертирование битов Вот два примера: MOV CL,00111100В AND 01010101В ; CL = 00010100В MOV DL,NOT 01010101B ; DL = 10101010B 614 Часть VII. Обзорная информация
Операторы LOW и LOWWORD Оператор LOW возвращает младший (первый справа) байт выражения. Оператор LOWWORD (появившийся в MASM 6.0) возвращает младшее слово выражения (для сравнения см. описания операторов HIGH и HIGH WORD). Вот пример: EQU_VAL EQU 1234H MOV CL,LOW EQU_VAL ;B CL будет помещено 34Н Оператор OFFSET Этот оператор возвращает смещение элемента данных или метки, указанных за ним. Оператор используется в форме offset переменная/метка. Следующая инструкция MOV помещает в регистр DX смещение PART_TBL относительно начала сегмента данных: MOV DX,OFFSET PART_TBL Заметьте, что то же самое значение можно поместить в DX без использования оператора OFFSET (инструкцией LEA DX,PART_TBL). Оператор MASK Описан вместе с директивой RECORD в параграфе "Директивы " этой главы. Оператор PTR Оператор PTR может использоваться с элементами данных и метками инструкций. Он использует спецификаторы типов BYTE, WORD, FWORD, DWORD, QWORD и TBYTE для указания размера операнда или для явного указания определенного типа (DB, DW, DF, DD, DQ или DT) для переменных. Он также использует спецификаторы типов NEAR, FAR и PROC для явного указания подразумевающегося расстояния до метки перехода. Оператор используется в форме тип ptr выражение, где тип - спецификатор типа, например, BYTE, а выражение - переменная или константа. Далее приводятся несколько независимых примеров использования PTR: BYTEA DB 22H DB 3 5H WORDA DW 2 672H /Сохраняется в памяти в виде 7226Н ADD BL,BYTE PTR WORDA+1 ;Прибавить к значению в BL второй байт (26Н) WORDA MOV BYTE PTR WORDA, 05 ;Поместить 05 в первый ; байт WORDA MOV AX,WORD PTR BYTEA ;Поместить в АХ два байта (2235Н) из BYTEA CALL FAR PTR[BX] ;Вызвать процедуру типа FAR Глава 25. Операторы и директивы 615
Аналогичные функции выполняет описанная далее директива LABEL. Оператор SEG Этот оператор возвращает адрес сегмента, в котором размещена указанная метка или переменная. Этот оператор чаще всего применяется в программах, использующих раздельно транслируемые сегменты. Оператор используется в форме SEG метка/переменная. Следующие независимые инструкции MOV возвращают адрес сегмента, в котором содержатся указанные имена: MOV AX,SEG WORDA ;Получить адрес сегмента данных MOV AX,SEG A10BEGIN ;Получить адрес сегмента кода Оператор переопределения сегмента (segment override operator) Этот оператор, обозначаемый двоеточием (:), вычисляет адрес метки или переменной относительно определенного сегмента. Оператор задается в виде сегмент-.выражение. Сегмент - это любой сегментный регистр или имя сегмента или группы. Выражение может быть константой, выражением или оператором SEG с выражением. Вот примеры, переопределяющие сегментный регистр DS: MOV BH,ES:10H ;Считать данные по адресу ES+1ОН MOV CX,SS:[BX] ;Считать данные по адресу SS+смещение из ВХ Оператор переопределения сегмента может применяться только в одном из операндов. Операторы SHL и SHR Эти операторы выполняют побитовый сдвиг выражения в период трансляции. Они ИСПОЛЬЗУЮТСЯ В формате выражение SHL/SHR величина. В последующем примере оператор SHR сдвигает содержимое битовой константы на 3 бита вправо: MOV BL,01011101В SHR "3 ;В BL помещено 00001011В Обычно выражение - это не непосредственное значение, а символьное имя. Оператор SHORT Этот оператор изменяет атрибут NEAR инструкции JMP, выполняющей переход по относительному адресу в пределах от -128 до 127 байтов. Оператор используется в виде JMP SHORT метка. При этом ассемблер уменьшает размер машинного кода операнда с двух байтов до одного. Эта возможность полезна при использовании ближних переходов вперед, поскольку здесь ассемблер изначально не знает, на какое расстояние выполняется переход, и будет по умолчанию использовать двухбайтовый операнд. 616 Часть VII. Обзорная информация
Операнд SIZE Оператор SIZE возвращает произведение LENGTH на TYPE и полезен только в случае, если указанная переменная объявлялась с использованием DUP. Оператор SIZE используется в виде size переменная. Пример его использования приведен в описании оператора TYPE. Оператор THIS Этот оператор создает операнд с адресами сегмента и смещения, равными текущим значениям счетчика адреса. Оператор используется в виде this тип. Тип - это BYTE, WORD, DWORD, FWORD, QWORD или TBYTE для переменных и NEAR, FAR или PROC - для меток. THIS обычно применяется совместно с директивами EQU или = (знак равенства). Например: PART_RECEQU THIS BYTE Эквивалентом такого определения является определение PART_REC LABEL BYTE Оператор TYPE Этот оператор возвращает размер (в байтах) определенной переменной, в зависимости от ее типа. Однако он всегда возвращает 1 для строковой переменной и 0 - для константы. Оператор задается в виде type переменная/метка. Тип переменной DB/BYTE DW/WORD DD/DWORD DF/FWORD DQ/QWORD DT/TWORD STRUC/STRUCT NEAR метка FAR метка Возвращаемое значение 1 2 .4 ' 6 8 10 Количество байтов, определенных структурой FFFFH FFFEH Следующие примеры иллюстрируют применение операторов TYPE, LENGTH и SIZE: BYTEA DB ? ; Определить один байт PART_TBL DW 10 DUP(?) /Определить десять слов Глава 25. Операторы и директивы 617
MOV AX,TYPE BYTEA ;AX = 0001H MOV AX,TYPE PART_TBL ;AX = 0002H MOV CX,LENGTH PART_TBL ;CX = 000AH (10) MOV DX,SIZE PART_TBL ;DX = 0014H (20) Поскольку PART_TBL определяется как DW, TYPE возвращает 0002Н, LENGTH возвращает 000AH (10 элементов, определенных с помощью DUP), a SIZE - произведение 2 х К) = 20 (0014Н). Оператор WIDTH Описан вместе с директивой RECORD в параграфе "Директивы " этой главы. Директивы В этом параграфе рассмотрено большинство директив ассемблера. В гл. 4 подробно рассмотрены директивы определения элементов данных (DB, DW и другие), а в гл. 21 -директивы, связанные с макросами, поэтому и те, и другие, здесь не описываются. Директивы делятся на ряд категорий. • Метки кода: ALIGN, EVEN, LABEL и PROC. • Условная трансляция: IF, ELSE и другие, рассмотренные в гл. 21. • Условия ошибок: .ERR, .ERR1 и другие. • Выделение памяти: ALIGN, EQU, EVEN, LABEL и ORG (директивы DB, DW, DD, DF, DQ и DT рассмотрены в гл. 4). • Управление листингом: .CREF, .LIST, PAGE, SUBTTL, TITLE, .XCREF и .XLIST, рассмотренные в этой главе, и .LALL, .LFCOND, .SALL, .SFCOND, .TFCOND и .XALL рассмотренные в гл. 21. • Макросы: ENDM, EXITM, LOCAL, MACRO и PURGE, рассмотренные в гл.21. • Вспомогательные: COMMENT, INCLUDE, INCLUDELIB, NAME, &OUT и .RADIX. • Директивы процессора: .8086, .286, .286Р, .386, .386Р и т.д. • Директивы повторения блоков: IRP, IRPC и REPT, рассмотренные в гл. 21. • Директивы области видимости: СОММ, EXTRN и PUBLIC. • Сегментные директивы: .ALPHA, ASSUME, .DOSSEG, END, ENDS, GROUP, SEGMENT и .SEQ. • Упрощенные сегментные директивы: .CODE, .CONST, .DATA, .DATA?, .EXIT, .FARDATA, .FARDATA?, .MODEL и .STACK. 618 Часть VII. Обзорная информация
• Директиву структур/записей: ENDS, RECORD, STRUCT, TYPEDEF и UNION. Ниже директивы рассмотрены в алфавитном порядке. Директива ALIGN Эта директива заставляет ассемблер выравнивать в памяти следующий элемент данных или инструкцию по адресу, соответствующему указанному значению. Это может ускорить выполнение программы, поскольку процессор быстрее воспринимает данные, выровненные в памяти определенным образом (по четным границам или по границам, делящимся на 4). Формат использования этой директивы - ALIGN число, где число - всегда степень двойки, например, 2, 4, 8 или 16. В следующем примере адрес в счетчике адреса равен 0005, а выражение ALIGN 4 заставляет транслятор увеличить его значение до следующего адреса, нацело делящегося на 4: 0005 ALIGN 4 0008 DBWORD DD 00 ;Выравнивание по границе двойного слова Если счетчик адреса уже содержит подходящее значение, оно не изменяется. Ассемблер заполняет неиспользуемые байты нулями для данных и инструкциями NOP - для инструкций. Заметьте, что директива EVEN эквивалентна директиве ALIGN 2. Директива .ALPHA Эта директива, помещенная в начале программы, указывает ассемблеру размещать сегменты в памяти в алфавитном порядке имен, для совместимости с более ранними версиями ассемблеров. Ту же функцию выполняет ключ /А в командной строке при запуске ассемблера. Директива ASSUMEf Эта директива устанавливает соответствие между именами сегментов и сегментными регистрами CS, DS, SS и ES. Ее формат: ASSUME сегментный-регистр: имя- сегмента [, ...] Можно использовать регистры CS, SS, DS, SS, FS и GS. Корректные имена сегментов - это имена объявленных сегментов, NOTHING, GROUP и выражение SEG. Одно выражение ASSUME может устанавливать до четырех соответствий в произвольном порядке. Упрощенные сегментные директивы автоматически генерируют директиву ASSUME. В следующем выражении CODESEG, DATASEG и STACK - имена, используемые программой для определения сегментов: Глава 25. Операторы и директивы 619
ASSUME CS:CODESEG, DS:DATASEG, SS:STACK, ES:DATASEG Отсутствие указания сегмента эквивалентно использованию NOTHING. Использование NOTHING также отменяет любое ранее установленное соответствие: например, ASSUME ES:NOTHING. Предположим, что вы не используете ни NOTHING, ни явного определения для регистра ES. Тогда для обращения к элементу данных в операнде инструкции можно применить оператор переопределения сегмента (:) для обращения к ES, который должен содержать корректный адрес сегмента для правильного выполнения: MOV AX,ES:[BX] /Использовать индексированный адрес MOV AX,ES:WORDA /Переместить содержимое WORDA Директива .CODE Эта упрощенная сегментная директива определяет сегмент кода. Ее используют в формате .code [имя], где имя указывать не обязательно. Весь исполняемый код должен размещаться в этом сегменте. Для моделей Tiny, Small и Compact по умолчанию этому сегменту присваивается имя TEXT, а в моделях Medium и Large допускается существование нескольких сегментов кода, различаемых по именам. (См. также директиву .MODEL). Директива СОММ Определение переменной с помощью СОММ (сокращение от Common - общий) присваивает ей одновременно атрибуты PUBLIC и EXTRN. При этом вам не понадобится объявлять ее как PUBLIC в одном модуле и как EXTRN в другом. Формат этой директивы следующий: СОММ [NEAR/FAR] метка-.размер [ : счетчик] Учитывайте следующие особенности: СОММ используется в сегменте данных; атрибуты NEAR и FAR можно задавать/пропускать, чтобы принудительно указать тип переменной, или оставить его установленным по умолчанию, в зависимости от используемой модели памяти; метка - это имя переменной; заметьте, что переменной нельзя присвоить начальное значение; размер указывается стандартными спецификаторами типов BYTE, WORD, DWORD, QWORD и TBYTE или целым числом, указывающим размер переменной в байтах; счетчик указывает количество элементов переменной; по умолчанию в переменной содержится единственный элемент. 620 Часть VII. Обзорная информация
Вот примеры объявления переменных с помощью директивы СОММ: СОММ NEAR COM_FLDl:WORD ; Слово С атрибутом СОММ СОММ FAR COM_FLD2:BYTE:25 ;25 байтов с атрибутом СОММ Директива COMMENT Эту директиву можно использовать для выделения нескольких строк комментариев. Ее формат: COMMENT ограничитель [комментарий] [ комментарии] ограничитель [ комментарий] Ограничитель - это первый символ, не являющийся пробелом, например, % или +, следующий за COMMENT. Комментарии заканчиваются в строке, где тот же знак-ограничитель встречается второй раз. Вот пример, использующий символ + в качестве ограничителя: COMMENT + Эта программа ищет 'некорректные символы + среди вводимых с клавиатуры. Директива .CONST Эта упрощенная сегментная директива определяет сегмент данных (или константных данных) с классом "const". (См. также описание директивы .MODEL.) Директива .CREF Эта директива (используемая по умолчанию) указывает ассемблеру сгенерировать таблицу перекрестных ссылок. Ее можно применить вслед за директивой .XCREF, подавлявшей создание таблицы. Директивы .DATA и .DATA? Эти упрощенные сегментные директивы определяют сегменты данных. .DATA определяет сегмент проинициализированных данных типа near, a .DATA? - сегмент неинициализированных данных типа near. Обычно вторая разновидность используется для связи с программой на языке высокого уровня. Для изолированной программы на ассемблере неинициализированные данные можно тоже определять в сегменте, создаваемом с помощью .DATA. (См. также описания директив .FARDATA и .MODEL.) Директивы DOSSEG/.DOSSEG Есть несколько способов управления порядком, в котором расположатся в памяти сегменты программы. Можно использовать директивы .ALPHA или Глава 25. Операторы и директивы 621
.SEQ в начале программы, или задавать ключи /А или /S в командной строке при запуске ассемблера. Директива DOSSEG (.DOSSEG начиная с MASM 6.0) указывает ассемблеру игнорировать любые другие требования и использовать стандартную для DOS последовательность сегментов - т.е. код, данные и стек. Используйте эту директиву в начале программы либо недалеко от него, главным образом, для того, чтобы сделать возможным применение отладчика CODEVIEW для отдельных программ. Директива END Эта директива помещается в конце исходного текста программы. Ее формат - end [начальный адрес]. Необязательный начальный адрес указывает на размещение сегмента кода (обычно, его первой инструкции), с которого должно начинаться выполнение программы. Системный загрузчик использует этот адрес ддя инициализации регистра CS. Если ваша программа состоит только из одного модуля, определите для него стартовый адрес. Если в программе несколько модулей, начальный адрес нужно определить только для одного (обычно первого) модуля. Директива ENDP Эта директива обозначает конец процедуры, начинающейся с директивы PROC. Ее формат: имя процедуры ENDP Имя процедуры - то же, что и в соответствующей директиве PROC. Директива ENDS Эта директива обозначает конец сегмента (определенного директивой SEGMENT) или структуры (определенной с помощью STRUC или STRUCT). Ее формат: имя сегмента ENDS Имя сегмента указывается то же, что и в соответствующей директиве SEGMENT. Директивы приравнивания (Equate directives) Есть две директивы приравнивания - знак равенства (=) и директива EQU. Обе воспринимаются только в период трансляции и не генерируют какого-либо кода или данных сами по себе. Они предназначены, например, для задания имен числовым константам или псевдонимов (aliases) определенным элементам данных. Эти директивы должны использоваться в программе перед тем, как на них будут сделаны ссылки. 622 Часть VII. Обзорная информация
Знак равенства используется в форме имя = выражение, где выражение - это любое целочисленное значение или арифметическое выражение. Директива может присваивать значение элементу данных любое количество раз. Вот два примера: Пример 1: ROW = 12 COL = 16 MOV BX,ROW+COL Пример 2: SCREEN_LOCS = 25 * О MOV CX,SCREEN_LOCS Директива EQU используется для переопределения имен данных и переменных другими именами данных, переменными или непосредственными значениями. Ассемблер заменяет требуемым значением каждое встреченное имя. Директива EQU может присваивать величину какому-либо элементу только раз в программе. Форматы для числовых и строковых данных различаются: Числовой: Строковой: Вот примеры использования EQU для числовых данных: ;Еще одно имя для COUNTER ;Числовое значение ;Увеличить значение в COUNTER на 1 ;Прибавить 10 к значению COUNTER Ниже показано применение EQU для строковых данных: PROD_MSG EQU <'Enter product number:'> BY_PTR EQU <BYTE_PTR> ИМЯ EQU выражение имя EQU <строка> COUNTER SUM TEN DW EQU EQU INC ADD 0 COUNTER 10 SUM SUM, TEN MESSGEl DB PROD MSG ;Заменяется строкой MOV SAVE,BY_PTR [BX] /Заменяется строкой Угловые скобки позволяют лучше отличать строковые операнды. Глава 25. Операторы и директивы 623
Директивы .ERR Эти директивы помогают обнаружить ошибки, возникающие при трансляции: Директива .ERR .ERR1 .ERR2 .ERRE .ERRNZ .ERRDEF .ERRNDEF .ERRB .ERRNB .ERRIDN[I] .ERRDIF[I] Когда возникает ошибка При обнаружении При первом проходе транслятора При втором проходе транслятора При истинном (0) выражении При ложном (не 0) выражении По определенному символу По не определенному символу По пустой строке По не пустой строке По идентичным строкам По разным строкам Начиная с MASM 6.0, больше нет нужды указывать .ERR1 (для первого прохода) или .ERR2 (для второго прохода транслятора). Все директивы из таблицы можно использовать в макросах и выражениях условной трансляции. В следующем примере конструкций условной трансляции ассемблер выводит сообщение, если выражение не истинно: IF условие ELSE .ERR %OUT [сообщение] ENDIF Директива EVEN Эта директива заставляет транслятор при необходимости увеличивать значение счетчика адреса, чтобы размещать следующую инструкцию или элемент данных по четному адресу. Это позволяет коду выполняться быстрее на процессорах, обрабатывающих одновременно 16 или 32 бита. Директива ALIGN 2 дает тот же эффект, что и EVEN. В следующем примере BYTE_LOCN - однобайтовый элемент данных, выровненный по четной границе - 0016. Текущее значение счетчика адреса - 0017. EVEN заставляет ассемблер увеличить значение счетчика до 0018 - следующего четного значения. По этому адресу будет размещаться следующий элемент данных - слово WORD LOCN: 624 Часть VII. Обзорная информация
0016 BYTE_LOCN DB ? 0017 EVEN /Увеличиваем значение счетчика адреса 0018 WORD_LOCN DW ? Директива .EXIT Эту директиву можно использовать в сегменте кода для генерации кода завершения программы. Ее формат- .exit [код-возврата], где код возврата 0 означает нормальное завершение, а 1 - ошибочное. Этой директивой генерируется следующий код: MOV АН,4СН MOV AL,код-возврата ;Генерируется, если задан код возврата INT 21H Директивы EXTRN/EXTERN Директива EXTRN (или EXTERN, начиная с MASM 6.0) указывает ассемблеру, что на соответствующую метку или элемент данных есть ссылки в этом модуле, но определяются они в другом (связанном с данным модулем). Формат этих директив следующий: EXTRN/EXTERN имя:тип [,...] Здесь имя - элемент, определяемый в другом файле, и объявленный там как PUBLIC. Тип - это: • для элементов данных: ABS (для констант), BYTE, WORD, DWORD, FWORD, QWORD, TBYTE; задавайте EXTRN в сегменте, в котором упоминается элемент; • для расстояний: NEAR или FAR; задайте NEAR в сегменте, в котором встречается элемент, или задавайте FAR, если элемент встречается где угодно. В следующем примере вызывающая программа определяет CONJVALUE как PUBLIC и DW. В вызываемой подпрограмме CONJVALUE (в другом сегменте) определяется как EXTRN и FAR. Вызывающая программа: DSEGl SEGMENT PUBLIC CON_VALUE CON_VALUE DW ? DSEGl ENDS Вызываемая подпрограмма: Глава 25. Операторы и директивы 625 40 — 5-741
EXTRN CON_VALUE:FAR DSEG2 SEGMENT MOV AX,WORD PTR CON_VALUE DSEG2 ENDS В гл. 22 приведены другие примеры использования EXTRN. Директивы .FARDATA и .FARDATA? Эти упрощенные сегментные директивы определяют сегменты данных. .FARDATA определяет сегменты проинициализированных данных типа far, a .FARDATA? - неинициализированных данных типа far. Для изолированной ассемблерной программы неинициализированные данные можно тоже определять в сегменте .FARDATA. (См. также описания директив .DATA и .MODEL.) Директива GROUP Программа может содержать несколько сегментов одного и того же типа (стек, данные или код). Директива GROUP предназначена для объединения сегментов одного типа под одним именем так, что они размещаются в одном сегменте, обычно - в сегменте данных. Формат этой директивы следующий: имя GROUP имя сегмента [, имя сегмента ] , ... В следующем примере GROUP объединяет сегменты DATASEG1 и DATASEG2 в одном и том же ассемблерном модуле: GROUPX GROUP DATASEGl, DATASEG2 DATASEG1 'SEGMENT PARA 'Data' ASSUME DS:GROUPX DATASEG1 ENDS DATASEG2 SEGMENT PARA 'Data' ASSUME DS:GROUPX DATASEG2 ENDS Того же эффекта, что и от директивы GROUP, можно добиться, присвоив сегментам одинаковые имена и атрибуты PUBLIC. Директива INCLUDE Если у вас есть фрагменты ассемблерного кода или макроинструкции, используемые различными программами, вы можете сохранить их на диске в отдельных файлах, доступных для использования любой программой. 626 Часть VII. Обзорная информация
Предположим, что программа, преобразующая ASCII-число в двоичный формат, хранится в файле CONVERT.LIB. Чтобы воспользоваться этим файлом, включите в программу выражение INCLUDE путь:CONVERT.LIB в том месте, где вы вставили бы программу преобразования ASCII-чисел. Ассемблер находит файл на диске и включает его текст в вашу программу (если он не сможет найти файл, то будет выведено сообщение об ошибке). Для каждой включенной строки ассемблер выводит символ С (символ зависит от версии) в столбце 30 LST-файла и начинает исходный код со столбца 33. В главе 21 содержится пример использования директивы INCLUDE и показано, как использовать эту директиву только для первого прохода трансляции. Директива LABEL Эта директива позволяет программе переопределять атрибуты метки инструкции или имени элемента данных. Ее формат следующий: имя LABEL специфика тор типа Для меток LABEL может переопределять исполняемый код как NEAR, FAR или PROC, например, для вспомогательных точек входа в процедуру. Для переменных можно указывать спецификатор типа BYTE, WORD, DWORD, FWORD, QWORD, TBYTE или имя структуры, чтобы переопределять элементы данных и имена структур. Например, LABEL позволяет определить в программе поле как DB и DW одновременно. Ассемблер не увеличивает значение счетчика адреса. В следующем примере используются типы BYTE и WORD, и предположим, что отсчет начинается с адреса 01 ОН: Обозначаем первый байт BYTE1, первые два байта WORD1, третий и четвертый байты WORD2, третий байт BYTE2, четвертый байт ;Перемещаем первый байт ;Перемещаем третий и четвертый байты Первая инструкция MOV перемещает только первый байт WORD1. Вторая инструкция MOV перемещает два байта, начиная с BYTE2. Оператор PTR выполнят аналогичные функции. 010 010 012 012 013 BYTE1 WORD1 WORD2 BYTE2 LABEL BYTE DW , 2532Н LABEL WORD DB 25H DB 32H MOV AL,BYTEl MOV BX,WORD2 Глава 25. Операторы и директивы 627 40*
Директива .LIST Директива .LIST (используемая по умолчанию) задает создание листинга исходной программы. Возможно, вы желаете исключить из листинга некоторый фрагмент кода, поскольку он обычен для программ. В этом случае можно применить директиву .XLIST (или .NOLIST) в начале этого фрагмента кода для прекращения генерации листинга, а в конце фрагмента вставить директиву .LIST для возобновления генерации. Эти директивы не имеют операндов. Директива .MODEL Эта упрощенная сегментная директива создает сегменты по умолчанию и соответствующие им выражения ASSUME и GROUP. Ее формат - .model модель_памяти. В качестве модели_памяти можно задать следующие ниже модели. Tiny. И код, и данные в единственном общем сегменте, используется для .СОМ-программ. Small. Единственный сегмент кода (размером до 64 Кбайт), единственный сегмент данных (размером до 64 Кбайт). Medium. Любое количество кодовых сегментов, данные в одном сегменте (размером до 64 Кбайт). Compact. Код в одном сегменте (размером до 64 Кбайт), любое количество сегментов данных. Large. Любое количество сегментов кода и данных, размер массивов до 64 Кбайт. Huge. Любое количество сегментов кода и данных, размер массивов может быть более 64 Кбайт. Flat. Несегментированная модель с 32-битовыми адресами, работает в защищенном режиме. Директива.NOLIST См. описание директивы .XLIST. Директива ORG Рассмотрим сегмент данных, содержащий следующие элементы: Смещение 00 02 03 05 Имя WORD1 BYTE1 WORD2 BYTE2 Операция DW DB DW DD Операнд 2542Н ЗбН 212ЕН 00000705Н Значение счетчика адреса 02 03 05 09 628 Часть VII. Обзорная информация
Имя BYTE3 W0RD3 BYTE4 Операция ORG DB DW DB ORG Операнд 0 7 7 4 ? $+5 Значение счетчика адреса 00 ' 01 03 04 09 Изначально счетчик адреса ассемблера установлен в 0. Поскольку размер WORD1 - 2 байта, значение счетчика адреса увеличивается до 02, чтобы указать адрес следующего элемента. Поскольку BYTE1 имеет длину 1 байт, значение увеличивается до 03, и т.д. Директиву ORG можно использовать, чтобы изменять значение счетчика адреса и, соответственно, положение следующего определяемого элемента. Формат этой директивы - org выражение, где выражение должно быть эквивалентным двухбайтовому абсолютному числу и не должно быть символьным именем. Предположим, что непосредственно за BYTE2 из предыдущей таблицы определены следующие элементы: Смещение 00 01 03 Первая директива ORG сбрасывает счетчик адреса в 0. Следующие переменные - BYTE3, WORD3 и BYTE4 - переопределяют область памяти, изначально выделенную для WORD1, BYTE1 и WORD2. Операнд, содержащий знак доллара ($) в последней директиве ORG указывает на текущее значение счетчика адреса. Поэтому операнд $+5 устанавливает счетчик адреса в значение 04+5, или 09 - в то же значение, что и после определения BYTE2. Ссылка на WORD2 - это обращение к двухбайтовому полю со смещением 03, а обращение к BYTE4 - это обращение к однобайтовому полю тоже со смещением 03: MOV AX,WORD2 ; Одно слово MOV AL,BYTE4 ;Один байт Когда вы используете ORG для переназначения ячеек памяти, проверяйте корректность изменения значений счетчика адреса. Кроме того, в переменных, размещаемых поверх других переменных, не должно содержаться констант <- они могут заменить константы, ранее размещенные по тем же адресам. Директива ORG не может использоваться в определении структуры директивой STRUC. Директива %OUT/ECHO Эта директива заставляет ассемблер передать сообщение на стандартное устройство вывода (обычно это экран). Начиная с MASM 6.0, эта директива обозначается ECHO. Формат этой директивы - %out/echo сообщение. Ранее в Глава 25. Операторы и директивы 629
этой главе, в описании директив .ERR, приводился пример использования этой директивы. Директива PAGE Эта директива в начале исходного текста программы указывает максимальное количество строк, выводимых на странице, и максимальное количество символов в строке. Формат ее вызова - page [ [длина], [ширина] ]. Например, PAGE 60,132 устанавливает режим с 60 строками на странице и 132 символами в строке. Количество строк на странице может быть от 10 до 255, а количество символов в строке - от 60 до 132. Отсутствие директивы PAGE приводит к использованию значений по умолчанию - PAGE 50,80. Чтобы завершить вывод страницы на конкретной строке, например, на конце сегмента, укажите директиву PAGE без операнда. Директива PROC Процедура - это участок кода, начинающийся с директивы PROC и заканчивающийся директивой ENDP. Хотя теоретически можно непосредственно переходить к процедуре инструкцией JMP при последовательном выполнении команд программы, обычно процедуры вызываются инструкциями CALL, a возврат из них выполняется инструкциями RETN и RETF. Операнд CALL может содержать спецификатор дистанции NEAR или FAR. Процедура, расположенная в том же сегменте, что и вызвавшая процедура - это процедура типа NEAR, и она вызывается простым изменением смещения в регистре IP. Формат директивы PROC для такой процедуры - имя_процедуры proc [near] . Отсутствующий спецификатор NEAR добавляется по умолчанию. Если вызываемая процедура находится за пределами текущего сегмента, она должна быть объявлена как PUBLIC и вызываться с помощью CALL. Для программы типа .ЕХЕ основная процедура, содержащая первую выполняемую инструкцию, должна быть типа FAR. Директивы процессора (Processor directives) Эти директивы определяют процессоры, для которых составляется программа ^ и инструкции которых транслятор должен распознавать. Директивы процессора должны размещаться в начале программы или в той точке, начиная с которой вы хотите использовать или игнорировать свойства соответствующего процессора. По умолчанию используется директива .8086. • Директивы .286, .386, .486 и .586 разрешают использование всех инструкций, появившихся в процессорах вплоть до указанного. 630 Часть VII. Обзорная информация
• Директивы .286Р, .386Р, .486Р и .586Р разрешают использование всех инструкций, упомянутых в предыдущем пункте, а также привилегированных инструкций процессора. Директива PUBLIC Эта директива указывает транслятору и компоновщику, что к соответствующему символу будут обращаться из других модулей, компонуемых вместе с данным модулем. Формат этой директивы - public символ [, . .. ], где символ - это метка, число (вплоть до двух байтов) или переменная. Смотрите описание директивы EXTRN в этой главе, а примеры и дополнительную информацию - в гл. 22. Директива RECORD Эта директива позволяет определять последовательности битов, например, цветовые палитры или флажки, как одиночные биты или группы битов. Ее формат: имя записи RECORD имя-поля:ширина [ =выражение] [, . . . ] Имя записи и имена полей могут быть любыми корректными уникальными идентификаторами. За каждым именем поля следует двоеточие (:) и значение ширины, указывающее количество битов. Диапазон допустимых значений ширины - от 1 до 32 битов. Длины вплоть до 8 бит считаются равными 8 битам, от9 до 16-16 битам, а от 17 до 32 -32 битам, выравниваясь, при необходимости, по правому краю. Вот пример использования директивы RECORD для определения BITREC: BIT_REC RECORD BITSl:3,BITS2:7,BITS3:6 BITS1 определяет первые 3 бита BIT_REC, BITS2 - следующие 7, a BITS3 - определяет последние 6 бит. Всего в записи - 16 бит (или одно слово). Эти биты можно проинициализировать следующим образом: BIT_REC2 RECORD BITSl:3=101В,BITS2:7=0110110В,BITS3:6=011010В Заметьте, что само по себе использование RECORD не создает в памяти каких-либо элементов данных. Поэтому следом за выражением RECORD в сегменте данных должно идти выражение, выделяющее для него область памяти. Определите корректное уникальное имя, имя записи и угловые скобки (символ больше и символ меньше): DEF_BITS BIT_REC <> Выделение памяти под DEF_BITS генерирует объектный код AD9AH (сохраняемый как 9AADH) в сегменте данных. В угловых скобках может Глава 25. Операторы и директивы 631
содержаться запись, переопределяющая начальные значения, заданные в BIT_REC. В фрагменте программы, приведенном на рис. 25.1, BIT_REC определена как RECORD, но начальные значения в полях записи не заданы. В этом случае директива выделения памяти в сегменте данных инициализирует поля в угловых скобках. С директивой RECORD связаны операторы WIDTH, счетчика сдвига и MASK. Применение этих операторов позволяет вам изменять определение RECORD без изменения инструкций, ссылающихся на нее. BIT_REC DBF_BITS А20: АЗО: А40: А50: А60: .DATA RECORD BITSA:3,BITSB:7, BITSC:6 ; Define 1 BIT_REC <101B,0110110B,011010B> /Initialize | .CODE MOV MOV MOV MOV MOV MOV MOV MOV MOV AND MOV SHR MOV MOV SHR BH,WIDTH BIT_REC AL,WIDTH BITSB CL,BITSA CL,BITSB CL,BITSC AX,MASK BITSA BX,MASK BITSB CX,MASK BITSC AX,DEF_BITS AX,MASK BITSB CL,BITSB AX,CL AX,DBF_BITS CL,BITSA AX,CL Размер: записи (16) поля (07) Счетчик сдвига: 0DH 06Н ООН Маска: E000H 1FC0H 003FH Выборка BITSB: получение записи очистка BITSA (и BITSC) число сдвигов = 06 сдвиг вправо Выборка BITSA: получение записи число сдвигов = 13 сдвиг вправо Рис. 25.1. Использование директивы RECORD Оператор WIDTH. Этот оператор возвращает ширину записи/поля, как число битов в записи RECORD или в поле RECORD. Например, на рис. 25.1 за меткой А20 есть два примера использования WIDTH. Первая инструкция MOV возвращает ширину всей записи BITREC (16 битов); вторая инструкция MOV возвращает ширину поля записи BITSB (7 битов). В обоих случаях ассемблер сгенерировал на месте оператора WIDTH непосредственное значение. 632 Часть VII. Обзорная информация
Оператор счетчика сдвига. Непосредственное обращение к полю RECORD, например, MOV CL,BITSB, не обращается к содержимому BITSB. Вместо этого ассемблер генерирует в объектном коде непосредственный операнд, помогающий выделить это поле. Непосредственное значение содержит число битов, на которое необходимо сдвинуть BITSB, чтобы выровнять его по правому краю. На рис. 25.1 три инструкции, следующих за меткой АЗО, возвращают значение счетчика сдвига для BITSA (OD), BITSB (06) и BITSC (00). Оператор MASK. Этот оператор возвращает битовую маску, в которой единичные биты указывают позицию битов заданного поля. Например, маска для каждого из полей BIT_REC выглядит так: Поле Маска в двоичном виде Шестнадцатеричное представление маски BITSA 1110000000000000 Е000 BITSB 0001111111000000 1FC0 BITSC 0000000000111111 003F На рис. 25.1 три инструкции за меткой А40 возвращают значения масок для BITSA, BITSB и BITSC. Инструкции за А50 и А60 выделяют BITSB и BITSA, соответственно, из BITREC. Инструкция за А50 помещает запись в регистр АХ и использует маску BITS2 для проведения с записью операции Й: Запись: 101 0110110 011010 Маска BITSB: 000 1111111 000000 Результат побитовой операции И: 000 0110110 000000 Практически результат равноценен обнулению всех битов, за исключением битов BITSB. Следующие две инструкции сдвигают содержимое АХ на 6 бит вправо, так что BITSB выравнивается по правому краю: 0000000000110110 (0036Н) Инструкции за меткой А60 помещают запись в АХ, и, поскольку BITSA расположена у левого края записи, лишние биты обнуляются просто при сдвиге вправо на 13 битов: 0000000000000101 (0005Н) Директива SEGMENT Ассемблерный модуль состоит из одного или более сегментов, части сегмента или частей нескольких сегментов. Формат сегмента: имя сегмента имя сегмента SEGMENT ENDS [выравнивание] [совмещение] ['класс'] Глава 25. Операторы и директивы 633
Операнды выравнивания, совмещения и класса необязательны. Выравнивание (align). Этот операнд указывает, каким должен быть адрес начала сегмента: • BYTE-любым; • WORD - четным (нацело делящимся на 2); • DWORD - нацело делящимся на 4; • PARA - адресом начала параграфа (нацело делящимся на 16 или ЮН); • PAGE - адресом начала страницы (делящимся нацело на 256 или 100Н). Обычно для сегментов всех типов используется выравнивание PARA. Выравнивание BYTE или WORD может использоваться для сегментов, которые могут совмещаться с другими сегментами (обычно для сегментов данных). Совмещение (combine). Операнды совмещения - это NONE, PUBLIC, STACK и COMMON. Они указывают на то, как данный сегмент должен восприниматься компоновщиком. • NONE (используется по умолчанию). Этот сегмент должен быть логически изолирован от других сегментов, хотя физически может оказаться рядом с ними. Этот сегмент должен иметь собственный базовый адрес. • PUBLIC. Компоновщик размещает сегменты типа PUBLIC с одинаковыми именами и классами в памяти друг за другом. Для всех таких сегментов указывается общий базовый адрес. • STACK. Компоновщик воспринимает STACK так же, как и PUBLIC. В программе типа .ЕХЕ должен быть хотя бы один такой сегмент. Если сегментов типа STACK больше одного, то SP связывается с первым из них. • COMMON. Если сегменты COMMON имеют одинаковые имена и классы, компоновщик назначает им единый базовый адрес. Во время выполнения второй сегмент размещается поверх первого. Самый большой по размеру сегмент, даже если он впоследствии и перекрывается другим, определяет длину общей области. • AT адрес_параграфа. Параграф должен быть определен заранее. Такая запись позволяет определять метки инструкций и элементы данных на фиксированных адресах в памяти, например, таблицу векторов прерываний - в нижней памяти, или область данных BIOS - по адресу 40[0]Н. Например, можно определить область видеопамяти как VIDEO_RAM SEGMENT AT 0B800H Ассемблер создает ложный сегмент, который практически отображает содержимое ячеек памяти. 634 Часть VII. Обзорная информация
Класс ('class*). Этот операнд помогает компоновщику связывать сегменты с разными именами, идентифицировать сегменты и управлять их порядком. Класс - это любое корректное имя, заключенное в одинарные кавычки. Компоновщик использует эти имена для связывания сегментов, имеющих одинаковые имена и классы. Типичные примеры - 'Data' и 'Code'. Если вы определите класс 'Code', компоновщик будет ожидать, что этот сегмент содержит исполняемый код. Кроме того, отладчик CODEVIEW ожидает, что класс 'Code' будет соответствовать сегменту кода. * Компоновщик совмещает следующие два сегмента с одинаковым именем CSEG и классом 'Code' в один физический сегмент, связывая его с одним сегментным регистром: модуль 1 CSEG SEGMENT PARA PUBLIC 'Code' ASSUME CS:CSEG CSEG ENDS модуль 2 CSEG SEGMENT ASSUME PARA PUBLIC CS:CSEG 'Code' CSEG ENDS Для управления порядком следования сегментов в программе полезно понимать, как компоновщик выполняет их размещение. Начальный порядок сегментов в исходном коде задает начальную последовательность, которую можно изменить с помощью атрибутов PUBLIC и имен классов. В следующем примере показаны два объектных модуля (каждый модуль содержит сегмент DATASEG1 с атрибутом PUBLIC и общим именем класса) перед компоновкой: модуль модуль модуль модуль модуль модуль модуль 1 1 1 1 2 2 2 STACK DATASEG1 DATASEG2 CODESEG DATASEG1 DATASEG2 CODESEG SEGMENT SEGMENT SEGMENT SEGMENT SEGMENT SEGMENT % SEGMENT PARA PARA PARA PARA PARA PARA PARA STACK PUBLIC 'Code' PUBLIC 'Code' 'Data 'Data После совместной компоновки двух модулей файл .ЕХЕ будет иметь такую структуру: модуль модуль модули модуль модуль модуль 1 2 1 и 2 1 2 1 CODESEG CODESEG DATASEG1 DATASEG2 DATASEG2 STACK SEGMENT SEGMENT SEGMENT SEGMENT SEGMENT SEGMENT PARA PARA PARA PARA PARA PARA 'Code' 'Code' PUBLIC STACK 'Data' Глава 25. Операторы и директивы 635
Можно использовать вложенные сегменты, но только при условии, что один вложенный сегмент полностью содержится в другом. В следующем примере DATASEG2 полностью помещается в DATASEG1: DATASEGl SEGMENT начало DATASEGl DATASEG2 SEGMENT область DATASEG2 DATASEG2 ENDS * продолжение DATASEGl DATASEGl ENDS Директивы .ALPHA, .SEQ и DOSSEG, а также ключи командной строки /А и /S также могут управлять порядком сегментов. Для объединения сегментов в группы используется директива GROUP. Директива .SEQ Эта директива, используемая по умолчанию, помещенная в начале программы или неподалеку от него, указывает ассемблеру оставить сегменты в их исходном порядке. Для этого можно также'использовать ключ командной строки /А. (См. также описания директив .ALPHA и DOSSEG.) Директива .STACK | Эта упрощенная сегментная директива определяет сегмент стека. Ее формат - . stack [размер]. По умолчанию размер стека равен 1024 байтам, но вы его можете изменить. (См. также описание директивы .MODEL.) Директива .STARTUP При Использовании в начале сегмента кода эта директива инициализирует регистры DS, SS и SP. (См. также описание директивы .EXIT.) Директива STRUC/STRUCT Директива STRUC (STRUCT начиная с MASM 6.0) позволяет объединять связанные поля в структуры. Формат этой директивы следующий: имя имя структуры STRUC/STRUCT [определенные структуры ENDS поля] Структура начинается с имени и директивы STRUC и заканчивается именем и директивой ENDS. Ассемблер последовательно воспринимает определения полей, начиная с первого поля структуры. Поля могут иметь тип DB, DW, DD, DQ или DT с необязательными именами. 636 Часть VII. Обзорная информация
Во фрагменте программы, приведенном на рис. 25.2, директива STRUC определяет список параметров с именем PARAMLIST для использования в функции О АН прерывания INT 21H, организующей ввод имени с клавиатуры. Заметьте: как и RECORD, сама по себе директива STRUCT не создает каких-либо элементов данных в памяти. Необходимо отдельное выражение, выделяющее память под экземпляр структуры, и только после такого выделения к элементам структуры можно обращаться из программы: PARAMS PARAMLIST о Угловые скобки в операнде в этом примере пусты, но их можно использовать для переопределения (или перекрытия) данных в структуре. PARAMLIST MAXLENDB ACTLENDB NAMEINDB PARAMLIST PARAMS PROMPT DB [_; .DATA STRUC 25 ? 25 DUP(' ') ENDS PARAMLIST < > 'What .CODE MOV MOV MOV LEA INT MOV LEA INT MOV is the part AH,40H BX,01 CX,21 DX,PROMPT 21H AH,0AH DX, PARAMS 21H no ;Список параметров ;Выделение памяти ? ' ;Запрос вывода ;Длина подсказки ;Адрес подсказки ;Ввод данных ; с клавиатуры AL,PARAMS.ACTLEN ;Длина ввода 4 \ Рис. 25.2. Использование директивы STRUC Инструкции могут напрямую обращаться к структуре по ее имени. Для обращения к полям структуры инструкции должны их явно указывать, используя имя экземпляра структуры (в примере - PARAMS), за которым идет точка и имя поля, например, mov al , params . actlen. Директиву, выделяющую память под экземпляр структуры (PARAMS на рис. 25.2), можно использовать и для переопределения содержимого полей структуры. Глава 25. Операторы и директивы 637
Директива SUBTTUSUBTITLE Директива SUBTTL (или SUBTITLE начиная с MASM 6.0) вызывает вывод подзаголовка (длиной до 60 символов) на третьей строке каждой страницы листинга исходного кода. Эту директиву можно использовать в программе произвольное число раз. Ее формат- subttl/subtitle текст. Директива TEXTEQU Формат этой директивы, появившейся в MASM 6.0, - textequ [текстовый элемент]. Операнд {текстовый элемент) может быть символьной строкой константой, предваряемой знаком %, или строкой, которую возвращает макрофункция. Директива TITLE Эта директива вызывает вывод заголовка (длиной до 60 символов) на второй строке каждой страницы листинга исходного кода. Эту директиву можно использовать в программе только однажды. Ее формат - title текст. Директива .XCREF/.NOCREF Эта директива (.NOCREF появилась в MASM 6.0) подавляет вывод таблицы перекрестных ссылок. Формат этой директивы - .xcref [имя[, имя] , ... ]. Отсутствие операнда полностью подавляет вывод таблицы. Указание имен в качестве операндов вызывает подавление вывода в таблице записей об указанных именах. Вот пример использования директив .XCREF и .CREF: .XCREF ;Подавить вывод всех перекрестных ссылок .CREF ;Отобразить все перекрестные ссылки .XCREF FIELDA, FIELDB ;Подавить вывод двух перекрестных ссылок Директива .XLIST/.NOLIST Эта директива (названная .NOLIST начиная с MASM 6.0) может применяться в программе где угодно с целью прекращения вывода листинга. Обычно она применяется для подавления вывода обычных для всех программ действий, которые желательно исключить из листинга. Директива .LIST (используемая по умолчанию) возобновляет вывод листинга. Эти директивы не используют операнды. 638 Часть VII. Обзорная информация
Глава 26 Набор инструкций PC Цель: описать машинный код и набор инструкций PC Введение В этой главе объясняется структура машинного кода, а также приводятся краткие описания инструкций с разъяснением их назначения. Многие инструкции выполняют одно единственное действие, и для их кодирования хватает одного байта. Вот примеры таких инструкций: Смысл Увеличить на 1 значение в АХ Поместить в стек значение из АХ Возврат из процедуры типа short Возврат из процедуры типа far Установка в 1 флага направления (Direction Flag, DF) Ни одна из вышеприведенных инструкций не обращается напрямую к оперативной памяти. Инструкции, использующие непосредственный операнд, обращающиеся к двум регистрам или к оперативной памяти, более сложны и занимают два или более байт машинного кода. Машинный код содержит специальные индикаторы обращения к определенному регистру, а также индикаторы необходимости обращения к памяти - байт режима адресации. Машинный код 40 50 СЗ СВ FD Инструкция ассемблера INC AX PUSH AX RET (типа short RET (типа far) STD Обращение к регистрам Инструкции, обращающиеся к регистрам, могут содержать три бита, указывающие на определенный регистр, и w-бит, указывающий, ведется ли обращение к одному байту регистра (бит содержит 0) или сразу ко всем 16-и его разрядам, т.е. слову (бит содержит 1). Кроме того, только некоторые инструкции могут обращаться непосредственно к сегментным регистрам. Ниже в таблице приведены сочетания битов, соответствующие различным регистрам. Например, сочетание битов 000 соответствует регистру АН, если w-бит содержит 0, и регистру АХ - если 1. Глава 26. Набор инструкций PC 639
Регистры общего назначения, базовые и индексные регистры Биты Бит w - 0 Бит w - 1 000 AL АХ/ЕАХ 001 CL СХ/ЕСХ 010 DL DX/EDX Oil BL BX/EBX 100 AH SP 101 CH BP 110 DH SI 111 BH DI Сочетания битов при обращениях к сегментным регистрам 000 ES 001 CS 010 SS 011 DS 100 FS 101 GS Вот символьный и машинный коды инструкции MOV с однобайтовой непосредственной величиной в качестве операнда: MOV АН, 00 10110 100 00000000 w reg = AH Пятый бит слева, равный 0 - это w-бит, указывающий, что инструкция обращается к восьми разрядам регистра. Следующие 3 бита, содержащие 100, указывают на регистр АН (см. таблицу выше). Вот еще один пример инструкции MOV с непосредственным значением в операнде: MOV АХ,00 10111 000 00000000 00000000 I SH w reg = АХ На этот раз w-бит (пятый слева) содержит 1, указывая, что инструкция обращается ко всем 16-и разрядам регистра, а код регистра 000 указывает, что это - регистр АХ. В других инструкциях w-бит и биты, указывающие регистр, могут занимать другие позиции. Кроме того, в первом байте машинного кода инструкции может содержаться d-бит, указывающий направление (влево/вправо) потока. Упражнение Попробуйте с помощью отладчика выполнить следующее исследование машинного кода (в DEBUG начните с команды А 100): вводите инструкции MOV, помещающие значение 0 в следующие регистры: AL, CL, DL, BL, АН, СН, DH, BH, AX, CX, DX, BX, SP, BP, SI и DI. При этом машинный код соответствующих инструкций будет последовательно изменяться от ВО до BF. 640 Часть VII. Обзорная информация
Байт режима адресации Байт режима, если он есть - это второй байт машинного кода инструкции, и он состоит из следующих частей: • биты режима - это первые 2 бита; если они содержат 00, 01 или 10, то указывают на адреса в памяти, а если 11 - то на регистр; • биты регистра - это следующие 3 бита; они указывают на регистр; • последние 3 бита могут указывать на регистр или память. Вот пример инструкции с такой структурой: add вх,ах; 00000011 11 011 000 dw mod reg r/m Седьмой слева бит - d-бит - содержит единицу, означая, что восьмой и девятый mod-бты (11) задают режим (в данном примере, регистровый), десятый, одиннадцатый и двенадцатый reg-биты (011) описывают первый операнд, а последние три r/m-6uma (000) - второй операнд. Поскольку w-бит (восьмой) содержит 1, размерность операнда - слово. Поэтому инструкция должна прибавить содержимое АХ (r/m - 000) к содержимому ВХ (гeg - 011). Второй байт объектного кода может указывать большинство способов адресации памяти. Чтобы проверить пример, можете в DEBUG ввести объектный код инструкции командой Е 100 03 D8 и дизассемблировать его командой U 100,101. Биты режима Два бита режима определяют, обращается ли инструкция к регистру или к памяти. Вот их значения: 00 - точный способ адресации указывается последними тремя битами байта режима адресации (r/m-битами); байта смещения нет (кроме случая, когда последние три бита содержат 110); 01 - точный способ адресации указывается последними тремя битами байта режима адресации (r/m-битами); один байт смещения; 10 -точный способ адресации указывается последними тремя битами байта режима адресации (r/m-битами); два байта смещения; 11 - последние три бита (r/m-биты) указывают регистр; w-бит (в байте кода операции) определяет, обращается ли инструкция к 8-и, 16-и или 32-м разрядам регистра. Глава 26. Набор инструкций PC 641 41 —5-741
Биты регистра Три бита регистра (reg-биты) в сочетании с w-битом определяют точный размер операнда. Последние три бита (r/m-биты) в сочетании с битами режима (mod-битами) определяют режим адресации согласно таблице: Значение r/m-битов 000 001 010 011 100 101 110 111 Режим mod=00 BX+SI BX+DI BP+SI BP+DI SI DI Прямая ВХ Режим mod=01 или 10 DS:[BX+SI+смещ] DS:[BX+DI+смещ] SS:[BP+SI+смещ] SS:[BP+DI+смещ] DS:[SI+смещ] DS:[DI+смещ] SS:[ВР+смещ] DS:[ВХ+смещ] Режим mod=ll, w-бит = 0 AL CL DL BL АН СН DH ВН Режим w-бит АХ СХ DX ВХ SP ВР SI DI mod=ll, = 1 Двухбайтовые инструкции Вот пример двухбайтовой инструкции, прибавляющей значение из АХ к значению в ВХ: ADD AX,BX 00000011 11 000 011 dw mod reg r/m Бит d содержит 1 Биты регистра reg и w-бит указывают первый операнд (регистр АХ), а биты режима mod, w-бит и r/m-биты байта режима адресации указывают второй операнд (ВХ). Бит w содержит 1 Размерность операндов - слово. Биты режима mod содержат 11 Второй операнд - регистр. Биты регистра reg содержат 000 Первый операнд - регистр АХ. r/m-биты содержат 011 Второй операнд - регистр ВХ. Вот еще один пример, инструкция, умножающая значение в AL на значение изВЬ: MUL BL 11110110 11 100 011 dw mod reg r/m 642 Часть VII. Обзорная информация
Ширина (w-бит = 0) - байт, биты режима mod содержат 11, значит, операция обращается к регистрам, а r/m-биты содержат 011, то есть используется регистр BL. Значение 100 в битах регистра reg для данной инструкции несущественно. Процессор всегда предполагает, что множимое находится в аккумуляторе, т.е. регистре AL, если размер множителя - байт, или в регистре АХ, если множитель - слово, или в регистре ЕАХ, если множитель - двойное слово. Трехбайтовые инструкции Для следующей инструкции MOV генерируется три байта машинного кода: MOV слово_в_памяти,АХ 10100011 nmmunmmmm пишгагашшпш II dw Последние два бита первого байта - это d-бит и w-бит. Перемещение из аккумулятора (AL или АХ) в память требует только указания размера данных. В этом примере w-бит содержит 1, значит, перемещается слово. Поэтому используется весь регистр АХ. Если бы w-бит содержал 0, то в качестве данных воспринимался бы только второй байт, и использовался бы регистр AL. Байты 2 и 3 содержат смещение слова в памяти. Использование регистра-аккумулятора часто приводит к генерации меньшего по размеру и быстрее выполняющегося кода. Четырехбайтовые инструкции Следующая четырехбайтовая инструкция умножает содержимое регистра AL на содержимое байта в памяти: MUL байт_в_памяти 11110110 00 100 110 mmmmimnmm mmmmnimmin ll dw mod reg r/m • В этой инструкции (хотя байты регистра reg и содержат 100) множимое полагается размещенным в AL (множимое - байт, поскольку w-бит содержит 0). Биты режима mod содержат 00, что указывает на обращение к памяти, r/m-биты байта режима адресации содержат 110, указывая на прямое обращение к памяти. Последующие два байта содержат смещение требуемого байта в памяти. В следующем примере рассматривается инструкция LEA, указывающая адрес слова: LEA DX, память 10001101 00 010 110 mmimnmmmm mmimnmmmm dw mod reg r/m Регистр (reg 010) - это регистр DX; биты режима mod содержат 00, а последние три бита r/m - 110, значит, есть обращение к адресу в памяти. Последние два байта содержат смещение требуемого слова. Глава 26. Набор инструкций PC 643 41*
Набор инструкций В этом параграфе в алфавитном порядке перечислены инструкции ассемблера. Родственные и близкие по смыслу инструкции (например, инструкции условных переходов) расположены вместе. Процессор 80286 и более поздние поддерживают ряд специализированных инструкций, не описанных здесь: ARPL, BOUND, CLTS, LAR, LGDT, LIDT, LLDT, LMSW, LSL, LTR, SGDT, SIDT/ SLDT, SMSW, STR, VERR и VERW. В процессоре 80486 и более поздних есть специфические инструкции В SWAP, INVD, WBINWD и INVPLG, также не рассмотренные здесь. Везде, где в качестве операндов упоминаются 32-разрядные регистры или двойные слова, подразумевается процессор 80386 или более поздние. В описаниях инструкций применяются следующие обозначения: addr Адрес ячейки памяти addr-high Старший (правый) байт адреса addr-low Младший (левый) байт адреса d d-бит (бит направления) data Непосредственное значение (8 бит, если w-бит содержит 0, и 16, если 1) data-high Старший (правый) байт непосредственного значения data-low Младший (левый) байт непосредственного значения disp Смещение mod Биты режима reg Обращение к регистру r/m Биты регистра или памяти (последние три бита байта режима адресации) w w-бит (бит размера) Используются также следующие обозначения флагов: AF = Auxiliary (вспомогательный), CF = Сапу (перенос), DF = Direction (флаг направления), IF = Interrupt (флаг прерываний), OF = Overflow (флаг переполнения), PF = Parity (флаг четности), SF = Sign (флаг знака), TF = Trap (флаг пошагового выполнения), и ZF = Zero (флаг нуля). 644 Часть VII. Обзорная информация
AAA: ASCII-коррекция после сложения (ASCII Adjust After Addition) Преобразует полученную инструкцией ADD в AL сумму в два ASCII-байта. Если значение младших четырех битов AL больше 9 или AF установлен в 1, AAA прибавляет 1 к АН, 6 - к AL и устанавливает флаги AF и CF в 1. В противном случае AF и CF сбрасываются в 0. AAA всегда очищает в 0 четыре старших разряда AL. Флаги: влияет на AF и CF (OF, PF, SF и ZF не определены) Символьный код: AAA (операндов нет) Машинный код: оонош AAD: ASCII-коррекция перед делением (ASCII Adjust Before Division) Корректирует неупакованное BCD-число (делимое) в регистре АХ перед делением. AAD умножает значение в АН на 10, прибавляет произведение к AL и заносит 0 в АН. Результирующее двоичное значение в АХ эквивалентно исходному неупакованному BCD-значению и готово к двоичной операции деления. Флаги: влияет на PF, SF и ZF (AF, ZF и OF не определены) Символьный код: AAD (операндов нет) Машинный код: | noioioi |ооооюю | ААМ: ASCII-коррекция после умножения (ASCII Adjust After Multiplication) Корректирует произведение в AL после перемножения двух неупакованных BCD-цифр. ААМ делит содержимое AL на 10 и помещает частное в АН, а остаток - в AL. Флаги: влияет на PF, SF и ZF (AF, ZF и OF не определены) Символьный код: ААМ (операндов нет) Машинный код: | поююо | ооооюю | AAS: ASCII-коррекция после вычитания (ASCII Adjust After Subtraction) После применения SUB эта инструкция корректирует в AL разность двух ASCII-цифр. Если значение четырех младших битов больше 9 или флаг CF Глава 26. Набор инструкций PC 645
установлен в 1, AAS вычтет 6 из AL, вычтет 1 из АН и установит AF и CF в 1. В противном случае AF и CF сбрасываются в 0. AAS всегда сбрасывает в 0 четыре старших разряда AL. Флаги: влияет на AF и CF (OF, PF, SF и ZF не определены) Символьный код: AAS (операндов нет) Машинный код: оошш ADC: Суммирование с переносом (Add with Carry) Обычно используется при суммировании многоразрядных двоичных чисел для передачи битов переноса на следующий шаг сложения. ADC прибавляет содержимое флага переноса CF (0 или 1) к операнду 1, а затем прибавляет к операнду 1 операнд 2, точно так же, как ADD (см. также описание инструкции SBB). Флаги: влияет на AF, CF, OF, PP, SF и ZF Символьный код: ADC регистр/память, регистр/память/непосредственное значение Машинный код: три формата: Регистр/память К регистру: |000100dw|mod reg r/m| Непосредственное значение к аккумулятору: |0001010w|—data—| data, если w=l | Непосредственное значение к регистру/памяти: |100000sw|mod 010 г/m|--data--|data, .если sw=01| ADD: Суммирование двоичных чисел (Add Binary Numbers) Суммирует двоичные числа в памяти, регистре или заданные непосредственно в операнде с числами в регистре или прибавляет непосредственные значения или значения в регистрах к значениям в памяти. Операнды могут быть байтами, словами или двойными словами. Флаги: влияет на AF, CF, OF, PF, SF и ZF Символьный код: ADD регистр/память, регистр/память/непосредственное значение Машинный код: три формата: РегИСТр/памяТЬ С регистром: | 000000dw|mod reg r/m| 646 Часть VII. Обзорная информация
Непосредственное значение к аккумулятору: |OOOOOlOw|—data--|data, если w=l| Непосредственное значение к регистру/памяти: |100000sw|mod 000 r/m|--data—|data, если sw=01| AND: Логическое И (Logical AND) Выполняет логическую операцию И над битами двух операндов. Оба операнда должны быть байтами, словами или двойными словами, и AND обрабатывает их побитово. Если оба соответствующих бита содержат 1, бит первого операнда остается установленным в 1; иначе он сбрасывается в 0. См. также описания инструкций OR, XOR и TEST. Флаги: сбрасывает в 0 CF и OF, влияет на PF, SF и ZF (AF не определен) Символьный код: AND регистр/память, регистр/память/непосредственное значение Машинный код: три формата: Регистр/память с регистром: |ooiooodw|mod reg r/m| Непосредственное значение к аккумулятору: |0010010w|--data--|data, если w=l| Непосредственное значение к регистру/памяти: |100000sw|mod 100 r/m|—data—|data, если w=lj BSF/BSR: Побитовое сканирование в прямом/обратном направлении (Bit Scan Forward/Bit Scan Reverse) (80386+) Просматривают битовую строку в операнде 2 (16 или 32 бита) в поисках первого бита, содержащего 1. BSF просматривает строку справа налево (от младших разрядов к старшим), BSR - слева направо. Если обнаружен бит, содержащий 1, операция возвращает номер его позиции (относительно крайнего правого бита номер 0) в регистр-операнд 1 и устанавливает в 1 флаг ZF; в противном случае ZF сбрасывается в 0. Флаги: влияет на ZF Символьный код: BSF/BSR регистр, регистр/память Машинный код: BSF: |0000111l|l0111100|mod reg r/m| BSR: j00001111 J10111101|mod reg r/mj Глава 26. Набор инструкций PC 647
BT/BTC/BTR/BTS: Проверка битов (Bit Test) (80386+) Копируют указанный бит в флаг переноса CF. Операнд 1 содержит битовую строку, которую нужно проверить, операнд 2 содержит значение, указывающее позицию требуемого бита. ВТ просто копирует бит в CF. Другие инструкции также копируют бит, но воздействуют на него и в операнде 1: ВТС инвертирует его значение, BTR сбрасывает в О, BTS устанавливает в 1. Инструкции обращаются к 16-и и 32-разрядным регистрам. Флаги: влияют на CF Символьный код: BT/BTC/BTR/BTS регистр/память, регистр/непосредственное значение Машинный код: два формата: Непосредственное значение в регистр: |00001111|l0111010|mod *** r/m| Регистр/память в регистр: | ooooilll | ю *** oio|mod reg r/m| *** = 100 для ВТ, 111 для ВТС, 110 для BTR и 101 для BTS CALL: Вызов процедуры (Call a Procedure) Вызывает процедуру типа near или far. Ассемблер генерирует CALL типа near, если вызываемая процедура объявлена как NEAR, и CALL типа FAR, если вызываемая процедура - типа FAR. Инструкция CALL типа near помещает в стек значение регистра IP (смещение следующей инструкции); после этого она помещает в IP смещение первой инструкции вызываемой процедуры. CALL типа FAR помещает в стек значение регистра CS и помещает в CS адрес сегмента, в котором расположена вызываемая процедура; после этого в стек помещается значение IP, и в IP - смещение первой инструкции этой процедуры в ее сегменте. Для возврата из процедуры типа near используется инструкция RETN, а типа far-RETF. Флаги: не влияет Символьный код: CALL регистр/память Машинный код: четыре формата: Прямой в пределах сегмента: | шоюоо | disp-low | disp-high | Косвенный в пределах сегмента: |iiiiini|mod ою r/m| Косвенный в другой сегмент: | шиш |mod on r/m| Прямой в другой сегмент: |10011010|offset-low|offset-high|seg-low|seg-high| 648 Часть VII. Обзорная информация
CBW: Преобразовать байт в слово (Convert Byte to Word) Преобразует байтовое знаковое число в знаковое слово, копируя знак (бит 7) регистра AL во все биты регистра АН. См. также инструкцию MOVSX. Флаги: не влияет Символьный код: CBW (операндов нет) Машинный код: юонооо CDQ: Преобразовать двойное слово в счетверенное слово (Convert Doubleword to Quadword) (80386+) Преобразует 32-разрядное знаковое число в 64-разрядное, заполняя биты регистра EDX значением старшего разряда ЕАХ. Флаги: не влияет Символьный код: CDQ (операндов нет) Машинный код: 10011001 CLC: Сбросить в 0 флаг переноса CF (Clear Carry Flag) Сбрасывает в 0 флаг переноса CF, так что в результате, например, последующая инструкция ADC не прибавляет 1 бит. См. также описание инструкции STC. Флаги: сбрасывает CF в 0 Символьный код: CLC (операндов нет) Машинный код: 11111000 CLD: Сбросить в 0 флаг направления DF (Clear Direction Flag) Сбрасывает в 0 флаг DF, так что в результате строковые операции, например, MOVS, выполняются слева направо. См. также описание инструкции STD. Флаги: сбрасывает DF в 0 Символьный код: CLD (операндов нет) Машинный код: 11111100 Глава 26. Набор инструкций PC 649
CLI: Сбросить в 0 флаг прерываний IF (Clear Interrupt Flag) Сбрасывает в 0 флаг IF, используется, чтобы заблокировать маскируемые внешние прерывания. См. также описание инструкции STI. Флаги: сбрасывает IF в О Символьный код: CLI (операндов нет) Машинный код: 11111010 CMC: Дополнить флаг переноса CF (Complement Carry Flag) Изменяет значение флага CF на противоположное текущему: с 0 на 1 или с 1 наО. Флаги: изменяет CF в противоположное состояние Символьный код: CMC (операндов нет) Машинный код: 11110101 СМР: Сравнить (Compare) Сравнивает в двоичном виде содержимое двух полей данных. СМР вычитает значение операнда 2 из операнда 1 и устанавливает/сбрасывает флаги в зависимости от результата, но не сохраняет сам результат. Оба операнда должны быть байтами, словами или двойными словами. СМР можно применять для сравнения регистров, элементов данных в памяти или непосредственных значений с регистром или сравнений регистров или непосредственных значений с памятью (СМР применяется для численных сравнений; для сравнений строк используются инструкции группы CMPSn). Результаты сравнения могут быть следующими: Значения флагов: Операнд 1 < операнд 2 Операнд 1 = операнд 2 Операнд 1 > операнд 2 Флаги: влияет на AF, CF, OF, PF, SF и ZF Символьный код: СМР регистр/память, регистр/память/непосредственное значение Машинный код: три формата: SF 1 0 0 ZF 0 1 0 650 Часть VII. Обзорная информация
Регистр/память с регистром: |ooinodw|mod reg r/m| Непосредственное значение с аккумулятором: |0011110w|--data--|data, если w=l| Непосредственное значение с регистром/памятью: |100000sw|mod 111 r/m|—data—|data, если sw=01| CMPS/CMPSB/CMPSW/CMPSD: Сравнить строки (Compare Strings) Сравнивают строки любой длины в памяти. Обычно используются с префиксом REP, при этом количество сравнений указывается в регистре СХ. Инструкция CMPSB сравнивает байты, CMPSW - слова, CMPSD (только для процессоров 80386 и более поздних) сравнивает двойные слова. Операнд 1 адресуется парой регистров ES:DI, второй операнд - DS:SI. Если флаг направления DF сброшен в 0, инструкция сравнивает данные операндов слева направо и увеличивает после каждой операции значения в DI и SI на: 1 - для байта, 2 - для слова, и 4 - для двойного слова. Если DF установлен в 1, инструкция сравнивает данные справа налево и уменьшает после каждой операции значения в DI и SI на: 1 - для байта, 2 - для слова, и 4 - для двойного слова. REPn уменьшает значение в СХ на 1 после каждой операции. REPNE вызывает завершение инструкции при обнаружении первого совпадения, REPE - первого несовпадения. Операция завершается при любом префиксе, когда в СХ содержится 0. DI и SI содержат адреса на 1 больше (или меньше), чем адреса последних проверенных байтов. Последнее сравнение устанавливает/сбрасывает флаги. Флаги: влияют на AF, CF, OF, PF, SF и ZF Символьный код: [REP] CMPSB/CMPSW/CMPSD (операндов нет) Машинный код: loiooiiw CMPXCHG: Сравнить и обменять (80486+) Сравнивает значение в аккумуляторе (AL, АХ или ЕАХ) с операндом 1. Если они равны, CMPXCHG копирует значение операнда 2 в операнд 1 и устанавливает флаг ZF в 1; если не равны, CMPXCHG копирует значение операнда 1 в операнд 2 и сбрасывает флаг ZF в 0. Операнды 1 и 2 указываются явно, а аккумулятор подразумевается. Флаги: влияет на AF, CF, OF, PF, SF и ZF Символьный код: CMPXCHG регистр/память, регистр Машинный код: ooooiiii юноооо/г или ооооии ionoooi/r Глава 26. Набор инструкций PC 651
CMPXCHG8B: Сравнить и обменять (Compare and Exchange) (Pentium+) Сравнивает 64-разрядное значение в паре регистров EDX:EAX с операндом. Если операнды равны, CMPXCHG8B загружает содержимое пары EDX:EAX в операнд и устанавливает в 1 флаг ZF; если операнды различаются, операнд загружается в EDX:EAX и сбрасывает в 0 флаг ZF. Флаги: влияет на ZF Символьный код: CMPXCHG8B регистр/память (один операнд размером в 64 байта) Машинный код: ooooiiii нооош С WD: Преобразовать слово в двойное слово (Convert Word to Doubleword) Преобразовывает знаковое число в формате слова в двойное слово в паре регистров DX:AX, заполняя битом знака (битом 15) регистра АХ биты регистра DX. Обычно это делается для получения 32-разрядного делимого. См. также описания инструкций CWDE и MOVSX. Флаги: не влияет Символьный код: CWD (операндов нет) Машинный код: iooiiooi CWDE: Преобразовать слово в расширенное двойное слово (Convert Word to Extended Doubleword) (80386+) Преобразовывает знаковое число в формате слова в двойное слово в регистре ЕАХ, заполняя битом знака (битом 15) регистра АХ старшие разряды регистра ЕАХ. Обычно это делается для получения 32-разрядного делимого. См. также описания инструкций CWD и MOVSX. Флаги: не влияет Символьный код: CWDE (операндов нет) Машинный код: юонооо 652 Часть VII. Обзорная информация
DAA: Десятичная коррекция после сложения (Decimal Adjust After Addition) Корректирует результат в регистре AL после инструкций ADD или ADC, складывавших две упакованные BCD величины. Если значение четырех младших битов больше 9 или флаг AF установлен в 1, DAA прибавляет 6 к AL и устанавливает в 1 флаг AF. Затем, если AL содержит значение больше 99Н или CF установлен в 1, DAA прибавляет 60Н к AL и устанавливает в 1 флаг CF. Иначе он сбрасывает в 0 флаги AF и CF. После этого AL будет содержать правильный результат в виде двухразрядного упакованного десятичного числа. См. также описание инструкции DAS. Флаги: влияет на AF, CF, PF, SF и ZF (OF не определен) Символьный код: DAA (операндов нет) Машинный код: ооюош DAS: Десятичная коррекция после вычитания (Decimal Adjust After Subtraction) Корректирует результат в регистре AL после инструкций SUB или SBB, вычитавших две упакованные BCD величины. Если значение четырех младших битов больше 9, DAS вычитает 60Н из AL и устанавливает в 1 флаг CF. Иначе он сбрасывает в 0 флаги AF и CF. После этого AL будет содержать правильный результат в виде двухразрядного упакованного десятичного числа. См. также описание инструкции DAA. Флаги: влияет на AF, CF, PF, SF и ZF (OF не определен) Символьный код: DAS (операндов нет) Машинный код: ooioini DEC: Уменьшить на 1 (Decrement by 1) Уменьшает на 1 значение байта, слова или двойного слова в регистре или памяти. Воспринимает значения как беззнаковые. Противоположное действие выполняется инструкцией INC. Флаги: влияет на AF, OF, PF, SF и ZF (но не на CF) Символьный код: DEC регистр/память Машинный код: два формата: Регистр: |oiooireg| РеГИСТр/памЯТЬ: | lllllllw|mod OOl r/m| Глава 26. Набор инструкций PC 653
DIV: Беззнаковое деление (Unsigned divide) Делит беззнаковое делимое нацело на беззнаковый делитель. DIV воспринимает старшие (первые слева) биты как биты данных, а не знаков. Деление на ноль вызывает соответствующее прерывание. Ниже приведена таблица соответствия размеров операндов: Размер 8 битов (байт) 16 битов (слово) 32 бита (двойное слово) Делимое (операнд АХ AX:DX EDX:EAX 1) Делитель (операнд 2) 8 битов в регистре/памяти 16 битов в регистре/памяти 32 бита в регистре/памяти Частное AL АХ ЕАХ Остаток АН DX EDX Пример DIV BH DIV CX DIV ECX См. также описание инструкции IDIV. Флаги: влияет на AF,CF,OF,PF, SF и ZF- Символъный код: DIV регистр/память Машинный код: |1111011w|mod 110 r/m| ENTER: Создать кадр стека (Make Stack Frame) (80286+) Создает кадр стека, чтобы передать параметры вызываемой процедуре. Операнд 1 содержит размер кадра стека в байтах; операнд 2 указывает уровень вложения (0 для компиляторов С, BASIC и ФОРТРАНа). Операция помещает в стек содержимое регистра ВР, помещает в ВР значение из SP и вычитает из SP 4. Обратные действия выполняет инструкция LEAVE. Флаги: не влияет Символьный код: ENTER размер, уровень вложения Машинный код: 111001000 | --data-- | --data-- | HLT: Войти в состояние останова (Enter Halt State) Переводит процессор в состояние останова для ожидания аппаратного прерывания. Когда происходит прерывание, процессор помещает значения из регистров CS и IP в стек и выполняет программу обработчика прерывания. При возврате инструкция IRET извлекает значения регистров из стека и выполнение 654 Часть VII. Обзорная информация
продолжается с инструкции, следующей за HLT. (Предварительно необходимо применить инструкцию STI, чтобы установить в 1 флаг IF, разрешив аппаратные прерывания.) Флаги: не влияет Символьный код: HLT (операндов нет) Машинный код: шююо IDIV: Знаковое (целочисленное) деление (Signed (Integer) Divide) Делит знаковое делимое нацело на знаковый делитель. IDIV воспринимает в качестве знака старшие (перзые слева) биты (1 - отрицательное число, 0 - положительное число). Деление на ноль вызывает соответствующее прерывание. Чтобы увеличить разрядность знакового делимого, используйте инструкции CBW и MOVSX). Ниже приведена таблица соответствия размеров операндов: Размер 8 битов (байт) 16 битов (слово) 32 бита (двойное слово) Делимое (операнд 1) АХ AX:DX EDX:EAX / Делитель (операнд 2) 8 битов в регистре/памяти 16 битов в регистре/памяти 32 бита в регистре/памяти Частное AL АХ ЕАХ Остаток АН DX EDX Пример IDIV BH IDIV CX IDIV ECX См. также описание инструкции DIV. Флаги: влияет на AF,CF,OF,PF, SF и ZF Символьный код: IDIV регистр/память Машинный код: | 1111011w|mod 111 r/m| IMUL: Знаковое (целочисленное) умножение (Signed (Integer) Multiply) Выполняет умножение знакового множимого на знаковый множитель. Операция воспринимает старшие (первые слева) биты чисел в качестве знаков (О - положительное число, 1 - отрицательное число). Существует четыре формата этой инструкции. 1. Операция подразумевает, что множимое помещено в регистр AL, АХ или ЕАХ, и определяет его размер по размеру множителя (См. также описание инструкции MUL). Ниже приведены размеры множителей и множимых: Глава 26. Набор инструкций PC > 655
Размер 8 битов (байт) 16 битов (слово) 32 бита (двойное слово) Множимое AL АХ ЕАХ Множитель 8 битов в регистре/памяти 16 битов в регистре/памяти 32 бита в регистре/памяти Произведение АХ DX:AX EDX:EAX Пример IMUL BL IMUL слово IMUL ECX Три других формата используют любой 16- или 32-разрядный регистр общего назначения; размеры элементов данных должны быть одинаковыми. 2. Операнд 1 (регистр) содержит множимое, и именно в нем появляется произведение; операнд 2 - множитель (непосредственное значение). Например: IMUL CX,32 IMUL BX,50 3. Операнд 1 (регистр) указывает, где должно появиться произведение; операнд 2 (регистр или адрес в памяти) содержит множимое; операнд 3 - непосредственное значение. Например: IMUL CX,DX,25 IMUL EBX, двойное_слово_в_памяти, 100 4. Операнд 1 (регистр) содержит множимое, и именно в нем появляется произведение; операнд 2 - множитель (регистр или адрес в памяти). Например: IMUL DX, слово_в_памяти '• IMUL EBX,EDX Флаги: влияет на CF и OF (AF, PF, SF и ZF не определены) Символьный код: четыре формата: 1. IMUL регистр/память 2. IMUL регистр, непосредственное значение (для процессоров 80286 и более новых) 3. IMUL регистр, регистр, непосредственное значение (для процессоров 80286 и более новых) 4. IMUL регистр, регистр/память (для процессоров 80386 и более новых) Машинный код: | iinoiiw|mod 101 r/m| (первый формат) IN: Ввести байт или слово (Input Byte or Word) Передает из приемного порта байт в регистр AL, слово - в АХ, или двойное слово - в ЕАХ. Номер порта указывается как непосредственное значение (например, IN AX, 100) или как переменная в регистре DX (например, IN AX,DX). 656 Часть VII. Обзорная информация
Если номер порта больше 256, то необходимо использовать регистр DX. См. также описания инструкций INS и OUT. Флаги: не влияет Символьный код: IN AL/AX/EAX, номер jiopmafDX. Машинный код: два формата: Переменный номер порта: | liioilOw | Фиксированный номер порта: | niooiOw | --port-- | INC: Увеличить на 1 (Increment by 1) Увеличивает на 1 значение байта, слова или двойного слова в регистре или памяти. Воспринимает значения как беззнаковые. Пример - ШС ЕСХ. Противоположное действие выполняется инструкцией DEC. Флаги: влияет на AF, OF, PF, SF и ZF (но не на CF) Символьный код: INC регистр/память Машинный код: два формата: Регистр: |01000reg| РеГИСТр/памяТЬ: |lllllllw|mod 000 r/m| INS/INSB/INSW/INSD: Ввести строку (Input String) (80286+) Принимает строку из порта в указанное операндом место. Место назначения указывается парой регистров ES:DI, а в регистре DX содержится номер порта. Инструкции INSn обычно используются с префиксом REP, при этом в СХ помещается число элементов (байтов, слов или двойных слов), которые нужно принять из порта. В зависимости от состояния флага направления DF (0/1) операция увеличивает/уменьшает значение в DI соответственно размеру принимаемых элементов (См. также инструкции IN и OUTSn). Флаги: не влияют Символьный код: [REP] INSB/INSW/INSD (операндов нет) Машинный код: | oiioilOw | INT: Вызвать обработчик прерывания (Interrupt) Прерывает выполнение программы и передает управление по одному из 256 адресов в таблице векторов прерываний. Инструкция ENT выполняет следующие Глава 26. Набор инструкций PC 657 42 — 5-741
действия: (1) помещает в стек содержимое регистра флагов и сбрасывает в О флаги IF и TF; (2) помещает в стек значение регистра CS и помещает старшее слово адреса обработчика прерывания в регистр CS; (3) помещает слово из регистра IP в стек и заполняет IP младшим словом адреса обработчика прерывания. Для процессоров 80386 и более поздних INT помещает в стек 16 разрядов IP для 16-разрядных сегментов и 32 разряда IP для 32-разрядных/Для возвращения из обработчика прерываний к основной программе используется инструкция IRET. Флаги: сбрасывает в 0 флаги IF и TF Символьный код: INT номер Машинный код: |1100110v|--type--| (если v=0, то type - 3) INTO: Вызвать прерывание при ^переполнении (Interrupt on Overflow) Вызывает прерывание (обычно безвредное) при возникновении переполнения (флаг OF установлен в 1) и вызывает INT 04H. Адрес обработчика этого прерывания находится по адресу ЮН в таблице векторов прерываний (См. также описание инструкции INT). Флаги: влияет на IF и TF Символьный код: INTO (операндов нет) Машинный код: ноошо IRET/IRETD: Вернуться из обработчика прерывания (Interrupt return) Выполняет возврат типа far из программы обработчика прерывания. IRET выполняет следующие действия: (1) извлекает слово из вершины стека в регистр IP, увеличивает значение SP на 2 и извлекает слово из вершины стека в CS; (2) увеличивает значение IP на 2 и извлекает слово из вершины стека в регистр флагов. Эта процедура восстанавливает состояние, в котором был процессор перед вызовом прерывания, и возвращает управление в точку вызова прерывания. Для процессоров 80386 и более новых используйте инструкцию IRETD для извлечения из стека 32-разрядного значения в IP. См. также описание инструкции RET. Флаги: влияют на все Символьный код: IRET (операндов нет) Машинный код': liooini 658 Часть VII. Обзорная информация
J-условие: инструкции условного перехода (Jump on Condition) Здесь рассмотрены инструкции условного перехода, проверяющие различные флаги. Если условие выполняется, инструкция прибавляет смещение, указанное в операнде, к значению в регистре IP и передает управление по адресу, указанному парой CS:IP. Если условие не выполняется, управление передается к следующей инструкции. Для процессоров 8086 и 80286 переходы могут быть только типа short (от -128 до +127 байтов); для процессоров 80386 и последующих они по умолчанию принадлежат к типу near (от -32768 до +32767 байтов), но можно использовать оператор SHORT и превратить их в переходы типа short. Инструкции проверяют состояния флагов, но не изменяют их. Исходный код выглядит как J-условие метка. Все объектные коды выглядят как |dist mmmm|--disp--|. Первые 4 бита dist: 0111 - для переходов типа short, или 1000 - для типа near. В первой таблице собраны инструкции, обычно используемые при условных сравнениях, сравнивающих операнды 1 и 2. Исх. код Объектный код Проверяемые флаги Тип данных, условие JA JAE JB JBE JE JG JGE JL JLE JNA JNAE JNB JNBE JNE JNG JNGE JNL JNLE distOlir distOOll dist0010 distOHO distOlOO distllll distllOl distllOO distlllO distOllO distOOlO distOOll distOlll distOlOl distlllO distllOO distllOl distllll CF CF CF CF ZF ZF SF SF ZF CF CF CF CF ZF ZF SF SF ZF = = = = = = = 0, ZF 0 1 1 или 1 0, SF OF #)F = = = = = = = 1 или 1 или 1 0 0, ZF 0 1 или <> OF = = OF 0, SF = 0 AF=1 = SF AF = SF = OF #)F = 1 0 #)F OF Беззнаковые, больше Беззнаковые, больше/равно Беззнаковые, меньше Беззнаковые, меньше/равно' Знаковые/беззнаковые, равно Знаковые, больше Знаковые, больше/равно Знаковые, меньше Знаковые, меньше/равно Беззнаковые, не больше Беззнаковые, не больше/равно Беззнаковые, не меньше' Беззнаковые, не меньше/равно Знаковые/беззнак. , неравно Знаковые, не больше Знаковые, не больше/равно Знаковые, не меньше Знаковые, не меньше/равно Глава 26. Набор инструкций PC 42* 659
Во второй таблице собраны инструкции перехода, применяемые обычно после арифметических или других операций, устанавливающих значения битов в зависимости от результата. Исходный Объектный Проверяемые Использование для проверки код код флаги JC JNC JNO JNP JNS JNZ J0 JP JPE JPO JS JZ distOOlO distOOll distOOOl distlOll distlOOl distOlOl • distOOOO distlOlO distlOlO distlOll distlOOO distOlOO CF CF OF PF SF ZF OF PF PF PF SF ZF = = = = = = = = = = = = 1 0 0 0 0 0 1 1 1 0 1 1 Если CF установлен в 1 (то же, что и JB/JNAE) Если CF сброшен в 0 (то же, что и JAE/JNB) Если OF сброшен в 0 Если число битов, установленных в 1, нечетное в младших восьми битах Если знак положителен Если знаковые/беззнаковые данные - не ноль Если OF установлен в 1 Если число битов, установленных в 1, четное в младших восьми битах То же, что и JP То же, что и JNP Если знак отрицателен Если знаковые/беззнаковые данные - ноль JCXZ/JECXZ: Перейти, если СХ/ЕСХ содержит 0 (Jump if СХ/ЕСХ is Zero) Выполняют переход по указанному адресу, если СХ или ЕСХ содержат 0. Эти инструкции могут быть полезны перед началом цикла, хотя позволяют переходы только типа short. Флаги: не влияют Символьный код: JCXZ/JECXZ метка Машинный код: | шооон | —disp— | 660 Часть VII. Обзорная информация
JMP: безусловный переход (Unconditional Jump) Выполняет безусловный переход по указанному в операнде адресу. Адрес перехода может быть типа short (от -128 до +127 байт), near (от -32768 до +32767, по умолчанию) или far (переход в другой сегмент). Переходы типа short или near просто помещают в регистр IP адрес точки назначения. Переход типа far также заменяет и значение в регистре CS на адрес нового сегмента. Флаги: не влияет Символьный код: JMP регистр/память Машинный код: пять форматов: Прямой типа short: | шоюи | —disp-- | Прямой ТИПа near: | 11101001|disp-low|disp-high| Косвенный в пределах сегмента: | ill nil i| mod юо r/m| Косвенный в другой сегмент: | шиш |mod lоl r/m| Прямой в другой сегмент: |11101010|offset-low|offset-high|seg-low|seg-high| LAHF: Загрузить содержимое восьми младших битов регистра флагов в АН (Load АН from Flags) Загружает младшие 8 битов регистра флагов в регистр АН. См. также инструкцию SAHF. Флаги: не влияет Символьный код: LAHF (операндов нет) Машинный код: lOOlllll LDS/LES/LFS/LGS/LSS: Загрузить в сегментный регистр (Load Segment Register) В этих инструкциях операнд 1 указывает на любой регистр - указатель, общего назначения или индексный. Операнд 2 указывает два слова в памяти, содержащие адрес в форме сегмент:смещение. Инструкции загружают адрес сегмента в соответствующий сегментный регистр и смещение - в регистр из операнда 1. Например, инструкция LDS DI,SEG_ADDRESS загружает адрес сегмента в регистр DS, а смещение - в DI. Глава 26. Набор инструкций PC 661
Флаги: не влияют Символьный код: LDS/LES/LFS/LGS/LSS регистр/память . Машинный код: LDS: |11000101|mod reg r/m| LES: |11000100|mod reg r/m| LFS: |00001111|10110100|mod reg r/m| (только для процессора 80386 или более поздних) LGS: |00001111|10110101|mod reg r/m| (только для процессора 80386 или более поздних) LSS: | 00001111110110010|mod reg r/m| (только для процессора 80386 или более поздних) LEA: Загрузить эффективный адрес (Load Effective Address) Загружает двухбайтовое (near) смещение в регистр. Флаги: не влияет Символьный код: LEA регистр, память Машинный код: Ю001Ю1 LEAVE: Выйти из кадра стека (Terminate stack frame) (80286+) Закрывает кадр стека процедуры', созданный инструкцией ENTER. LEAVE выполняет действие, обратное ENTER (перемещает значение ВР в SP и извлекает из стека предыдущее значение ВР). Флаги: не влияет Символьный код: LEAVE (операндов нет) Машинный код: nooiooi LES/LFS/LGS: Загрузить в дополнительные сегментные регистры (См. описание LDS.) LOCK: закрыть шину (Lock Bus) Запрещает математическому сопроцессору изменять значение элемента данных в то время, когда это делает основной процессор. LOCK - это однобайтовый префикс, который можно указать прямо перед любой инструкцией. 662 Часть VII. Обзорная информация
Операция посылает сигнал сопроцессору, чтобы предотвратить использование им данных до завершения следующей инструкции. Флаги: не влияет Символьный код\ LOCK инструкция Машинный код: шюооо LODS/LODSB/LODSW/LODSD: Загрузить строку по байтам, словам или двойным словам (Load Byte, Word or Doubleword String) Загружает значение из памяти в аккумулятор (регистр АХ). Хотя LODS - строковая инструкция, она не требует префикса REP. DS:SI содержат адрес: байта - для LODSB, слова - для LODSW, и двойного слова - для LODSD (последняя - только для процессоров 80386 или более поздних), загружаемых, соответственно, в регистры AL, АХ и ЕАХ. Если флаг DF сброшен в 0, операция к значению в SI прибавляет: 1 - для байта, 2 - для слова, и 4 - для двойного слова; если DF установлен в 1, то отнимает. Флаги: не влияет Символьный код: LODS память или LODS сегмент:память или LODSB/LODSW/LODSD (операндов нет) Машинный код: loionow LOOP/LOOPW/LOOPD: Повторять до завершения (Loop Until Complete) Позволяют выполнить процедуру указанное число раз. СХ должен содержать число повторений перед тем, как управление будет передано циклу. LOOP размещается в конце повторяемого фрагмента и уменьшает СХ на 1. Если СХ содержит не 0, LOOP передает управление по адресу, указанному в операнде (значение из операнда прибавляется к значению в IP), т.е. на начало цикла; если в СХ 0, управление передается следующей за LOOP инструкции. LOOP в 16-разрядном режиме использует СХ, а в 32-разрядном - ЕСХ; LOOPW используется для 16-разрядного СХ, a LOOPD (для процессоров 80386 и более новых) - для 32-разрядного ЕСХ. Флаги: не влияют Символьный код: LOOPnn метка Машинный код: | шооою | --disp-- | , Глава 26. Набор инструкций PC 663
LOOPE/LOOPZ/LOOPEW/LOOPZW/LOOPED/ LOOPZD: Повторять, пока равно/повторять, пока ноль (Loop While Equal/Loop While Zero) Позволяет многократно выполнять фрагмент программы. LOOPE и LOOPZ аналогичны LOOP, за исключением того, что они выполняют возврат к точке начала цикла (переход типа short), если СХ содержит ненулевое значение, и флаг ZF установлен в 1 (нулевое значение, установленное другой инструкцией). В противном случае выполнение цикла завершается и начинается выполнение дальнейших инструкций. См. также описания инструкций LOOPNE/LOOPNZ. LOOPE и LOOPZ в 16-разрядном режиме используют регистр СХ, а ЕСХ - в 32-разрядном. LOOPEW и LOOPZW используют регистр СХ, a LOOPED/ LOOPZD - ЕСХ (только для процессоров 80386 или более поздних). Флаги: не влияют Символьный код: LOOPnn метка Машинный код: | mooooi | —disp--1 LOOPNE/LOOPNZ/LOOPNEW/LOOPNZW: Повторять, пока не равно/повторять, пока не ноль (Loop While Not Equal/Loop while Not Zero) Позволяет многократно выполнять фрагмент программы. LOOPNE и LOOPNZ аналогичны LOOP, за исключением того, что они выполняют возврат к точке начала цикла (переход типа short), если СХ содержит ненулевое значение и флаг ZF сброшен в 0 (ненулевое значение, установленное другой инструкцией). В противном случае выполнение цикла завершается и начинается выполнение дальнейших инструкций. См. также описания инструкций LOOPE/LOOPZ. LOOPNE и LOOPNZ используют регистр СХ в 16-разрядном режиме и ЕСХ - в 32-разрядном. LOOPNEW и LOOPNZW используют регистр СХ, a LOOPNED/ LOOPNZD - ЕСХ (только для процессоров 80386 или более поздних). Флаги: не влияют , Символьный код: LOOPNN метка Машинный код: | шооооо | --disp--1 LSS: Загрузить в регистр SS (См. описание LDS.) 664 Часть VII. Обзорная информация
MOV: Переместить данные (Move data) Перемещает данные между двумя регистрами или между регистром и памятью или помещает непосредственные значения в регистры или память. Размер перемещаемых данных (1,2 или 4 байта) определяется операндом 1 (источником). Операнды должны совпадать по размеру. MOV не может перемещать данные напрямую между двумя ячейками в памяти (используйте инструкции MOVSn), не может помещать непосредственные значения в сегментные регистры или из сегментного регистра в сегментный регистр (См. также инструкции MOVSX/MOVZX). Флаги: не влияет Символьный код: MOV регистр/память, регистр/память/непосредственное значение Машинный код: семь форматов: Регистр/память в/из регистра: | ioooiOdw|mod reg r/m| Непосредственное значение в регистр/память: |1100011w|mod 000 r/m|--data--|data, если w=l| Непосредственное значение в регистр: |1011 w reg|--data--|data, если w=l| Из памяти В аккумулятор: 11010000W | addr-low | addr-high | Из аккумулятора В память: 11010001w| addr-low | addr-high | Из регистра/памяти в сегментный регистр: |10001110|mod Osg r/m| (sg - код сегментного регистра) Из сегментного регистра в регистр/память: |10001100|mod Osg r/m| (sg - код сегментного регистра) MOVS/MOVSB/MOVSW/MOVSD: Переместить строку (Move String) Перемещают данные в памяти. Обычно используются с префиксом REP, при этом количество повторений указывается в регистре СХ. Инструкция MOVSB перемещает байты, MOVSW - слова, MOVSD (только для процессоров 80386 и более поздних) перемещает двойные слова. Операнд 1 адресуется парой регистров ES:DI, второй операнд - DS:SI. Если флаг направления DF сброшен в 0, инструкция перемещает данные в операнд 1 слева направо и после каждой операции увеличивает значения в DI и SI на: 1 - для байта, 2 - для слова, и 4 -• для двойного слова. Если DF установлен в 1, инструкция перемещает данные в операнд 1 справа налево и после каждой операции уменьшает значения в DI и SI Глава 26. Набор инструкций PC 665
на 1, 2 или 4 для байта, слова и двойного слова, соответственно. REP уменьшает значение в СХ на 1 после каждой операции. Операция завершается, когда в СХ содержится 0, при этом DI и SI содержат адреса на 1 больше (или меньше), чем адреса последних задействованных байтов. Флаги: не влияют Символьный код: [REP] MOVSB/MOVSW/MOVSD (операндов нет) Машинный код: ioiooiow MOVSX/MOVZX: Переместить с расширением знака или нуля (Move with Sign Extend or Zero Extend) (80386+) Копируют восьми или 16-разрядный операнд источника в 16- или 32-разрядный операнд назначения. MOVSX заполняет старшие разряды операнда назначения содержимым бита знака операнда источника, a MOVZX - нулями. Флаги: не влияют Символьный код: MOVSX/MOVZX регистр/память, регистр/память/непосредственное значение Машинный код: MOVSX: | 00001111 |1011111w|mod reg r/m| MOVZX: I 0000111111011011w|mod reg r/m| MUL: Беззнаковое умножение (Unsigned Multiply) Перемножает беззнаковые двоичные числа. MUL воспринимает старший бит в качестве бита данных, а не бита знака. Операция подразумевает, что множимое располагается в регистре AL, если операнд имеет размер байта, в регистре АХ, если - слова, и в ЕАХ, если - двойного слова. Результаты помещаются в следующие регистры: Размер операнда Байт Слово Двойное слово Множимое AL АХ ЕАХ Множитель 8-разрядный регистр/память 16-разрядный регистр/память 32-разрядный регистр/память Произведение АХ DX:AX ЕАХ:EDX Пример MUL BL MUL слово_в_памяти MUL ECX ч 666 Часть VII. Обзорная информация
(См. также описание инструкции IMUL.) Флаги: влияет на CF и OF (AF, PF, SF и ZF не определены) Символьный код: MUL регистр/память Машинный код: |1111011w|mod ЮО r/m| NEG: Изменить знак (Negate) Преобразует положительное двоичное число в соответствующее отрищ^ тельное и наоборот. NEG вычисляет двоичное дополнение своего операнда, вычитая операнд из 0 и прибавляя к результату 1. Операнд - это байт, слово или двойное слово в регистре или памяти. См. также описание инструкции NOTV Флаги: Влияет на AF, CF, OF, PF, SF и ZF. Символьный код: NEG регистр/память Машинный код: |H11011w|mod Oil r/m| NOP: Нет действий (No Operation) Используется для удаления или вставки машинных кодов или для замедления/синхронизации выполнения программ. NOP просто выполняет пустую операцию XCHG АХ,АХ. Флаги: не влияет Символьный код: NOP (операндов нет) Машинный код: юоюооо NOT: Логическое НЕ (Logical NOT) Изменяет биты, содержащие 0, на 1, и наоборот. Операнд - байт, слово или двойное слово в регистре или памяти (См. также инструкцию NEG). Флаги: не влияет Символьный код: NOT регистр/память Машинный код: |lill0llw|mod 010 r/m| OR: Логическое ИЛИ (Logical OR) Выполняет логическую операцию ИЛИ над битами двух операндов. Оба операнда должны быть байтами, словами или двойными словами. OR обрабатывает их побитно. Если хотя бы один из каждой пары обработанных битов Глава 26. Набор инструкций PC 667
содержит 1, бит первого операнда устанавливается в 1; в противном случае бит не изменяется. См. также описания инструкций AND и XOR. Флаги: Влияет на CF (сбрасывает в 0), OF (сбрасывает в 0), PF, SF и ZF (AF не определен) Символьный код: OR регистр/память, регистр/память/непосредственное значение Машинный код: три формата: Регистр/память И регистр: | 000010dw|mod reg r/m| Непосредственное значение и аккумулятор: |OOOOllOw|--data--|data, если w=l| Непосредственное значение и регистр/память: |l00000sw|mod 001 r/m|--data--|data, если w=l| OUT: Вывести байт или слово (Output Byte or Word) Передает байт из регистра AL, слово - из АХ, или двойное слово - из ЕАХ в порт для вывода. Порт - это фиксированный непосредственный операнд или переменное число Ь регистре DX. Используйте регистр DX, если номер порта больше, чем 256. См. также описания инструкций IN и OUTSn. Флаги: не влияет Символьный код: для фиксированного порта: OUT номер,АХ для переменного порта: OUT DX,AX Машинный код: для фиксированного порта: | lll00llw| -гпорт-- | для переменного порта: | iiioniw[ OUTS/OUTSB/OUTSW/OUTSD: Вывести строку (Output string) (80286+) Посылает строку из источника в порт. Источник указывается парой регистров DS:SI, а в регистре DX содержится номер порта. Инструкции OUTSn обычно используются с префиксом REP, при этом в СХ помещается число элементов (байтов, слов или двойных слов), которые нужно переслать в порт. В зависимости от состояния флага направления DF (0/1) операция увеличивает/уменьшает значение в SI соответственно размеру пересылаемых элементов (см. также инструкции IN и OUT). 668 Часть VII. Обзорная информация
Флаги: не влияют Символьный код\ [REP] OUTSB/OUTSW/OUTSD (операндов нет) Машинный код', onoillw POP: Извлечь слово/двойное слово из стека (Pop Word/Doubleword Off Stack) Извлекает слово или двойное слово, предварительно помещенное в стек, в указанное в операнде место - элемент данных в памяти, регистр общего назначения или сегментный регистр. Регистр SP указывает положение текущего слова на вершине стека; инструкция POP извлекает это слово и увеличивает значение SP на 2. Для 32-разрядных операндов, подразумевающих извлечение из стека двух слов, POP уменьшает значение SP на 4. См. также описание инструкции PUSH. Флаги: не влияет Символьный код: POP регистр/память Машинный код: три формата: Регистр: | о юн reg| Сегментный регистр: |ооо sg ill | (sg-это код сегментного регистра) Регистр/память: |ioooiin|mod ooo r/m| POP A/POP AD: Извлечь из стека значения во все регистры общего назначения (Pop All General Registers) РОРА (для процессоров 80286 или более поздних) извлекает восемь слов из вершины стека в регистры DI, SI, BP, SP, BX, CX, DX, АХ, в указанном порядке. POPAD (для процессоров 80386 или более поздних) извлекает из стека восемь двойных слов в регистры EDI, ESI, EBP, ESP, EBX, ECX, EDX, ЕАХ. Значения регистра SP/ESP отбрасываются, а не помещаются в регистр. Обычно для сохранения содержимого всех регистров в стеке используются инструкции PUSHA/ PUSHAD. Флаги: не влияют Символьный код: POPA/POPAD (операндов нет) Машинный код: ohooooi Глава 26. Набор инструкций PC 669
POPF/POPFD: Извлечь из стека значение в регистр флагов (Pop Flags Off Stack) POPF извлекает слово из вершины стека в 16-разрядный регистр флагов и увеличивает значение регистра SP на 2. POPFD (для процессоров 80386 и более поздних) извлекает двойное слово из стека в 32-разрядный расширенный регистр флагов и увеличивает значение SP на 4. Обычно для сохранения флагов в стеке используются инструкции PUSHF/PUSHFD. Флаги: влияет на все Символьный код: POPF/POPFD (операндов нет) Машинный код: Ю0Ш01 PUSH: Поместить в стек (Push onto stack) Помещает в стек слово или двойное слово для дальнейшего использования. SP указывает адрес слова (или двойного слова), находящегося в данный момент на вершине стека. PUSH уменьшает значение в SP на 2 или в ESP - на 4 и перемещает слово или двойное слово, соответственно, из операнда в вершину стека. Операнд может представлять собой регистр общего назначения, сегментный регистр или элемент данных в памяти. См. также описания инструкций POP и PUSHF. Флаги: не влияет Символьный код: PUSH регистр/память PUSH непосредственное значение (80286+) Машинный код: три формата: Регистр: |oioio reg| Сегментный регистр: |ооо sg lio| (sg - код сегментного регистра) Регистр/память: |ilium|mod no r/m| PUSHA/PUSHAD: Поместить в стек значения всех регистров общего назначения (Push all general registers) PUSHA (для процессоров 80286 или более поздних) помещает в стек значения регистров АХ, СХ, DX, BX, SP, BP, SI и DI, в порядке их перечисления, и уменьшает значение в SP на 16. PUSHAD (для процессоров 80386 или более поздних) помещает в стек значения ЕАХ, ЕСХ, EDX, EBX, ESP, EBP, ESI и EDI 670 Часть VII. Обзорная информация
и уменьшает значение SP на 32. Обычно в дальнейшем значения регистров извлекаются из стека инструкциями POPA/POPAD. Флаги: не влияют Символьный код: PUSHA/PUSHAD (операндов нет) Машинный код: онооооо PUSHF/PUSHFD: Поместить в стек значение регистра флагов (Push Flags onto Stack) Помещает в стек содержимое регистра флагов для дальнейшего использования. PUSHF уменьшает значение в регистре SP на 2. PUSHFD (процессор 80386 или более поздние) помещает в стек содержимое расширенного 32-разрядного регистра флагов и уменьшает значение SP на 4. См. также инструкции POPF, PUSH и POP. Флаги: не влияет Символьный код: PUSHF (операндов нет) Машинный код: юошоо RCURCR: Сдвинуть циклически влево/вправо через перенос (Rotate Left through Carry/Rotate Right through Carry) Циклически сдвигают влево или вправо биты байта, слова или двойного слова (для процессоров 80386 или более поздних) в регистре или в памяти через флаг переноса CF. Операнд - это непосредственное значение, содержащее величину сдвига, или ссылка на регистр CL. Для процессоров 8086/8088 непосредственным значением может быть только 1, а большие значения должны содержаться в регистре CL; в более поздних процессорах непосредственное значение может быть любым числом до 31 включительно. В инструкции RCL выдвигающийся за пределы операнда слева бит помещается во флаг CF, а бит из него помещается в крайний слева байт; при этом последующие биты сдвигаются на 1 влево; для RCR бит 0 последовательно перемещается во флаг CF, бит из флага CF помещается в крайний слева (старший) бит операнда, а все предыдущие биты сдвигаются на 1 вправо. См. также инструкции ROL и ROR. Флаги: влияют на CF и OF Символьный код: ROL/ROR регистр/память,СЬ/непосредственное значение Машинный код: Глава 26. Набор инструкций PC 671
ROL: | iioioocw|mod oio r/m| (если c=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) ROR: | noioocw|mod on r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) REP: Повторить строку (Repeat String) Повторяет следующую строковую инструкцию указанное количество раз. REP - это необязательный префикс повторения, используемый с инструкциями MOVS, STOS, INS и OUTS. При использовании этого префикса инструкция выполняется указанное в регистре СХ число раз. При каждом выполнении префикс REP уменьшает значение в СХ на 1 и повторяет выполнения инструкции, пока СХ не содержит 0. После этого начинает выполняться следующая инструкция. См. также инструкции-префиксы REPE/REPZ/REPNE/REPNZ. Флаги: в зависимости от используемой строковой инструкции Символьный код: REP строковая инструкция Машинный код: linooio REPE/REPZ/REPNE/REPNZ: Повторить строку при условии (Repeat String Conditionally) Повторяет следующую строковую инструкцию в зависимости от определенных условий (определенное количество раз или до некоторого события). REPE, REPZ, REPN и REPNZ - это необязательные префиксы повторений, указываемые перед строковыми инструкциями CMPS и SCAS, изменяющими флаг ZF. Загрузите в СХ число повторений перед использованием этих префиксов. Для REPE/REPZ (Repeat while equal/Repeat while zero - Повторять, пока равно/Повторять, пока ноль) инструкция будет выполняться, пока ZF установлен в 1 (равно/ноль) и значение в СХ отлично от нуля. Для REPNE/REPNZ (Repeat while not equal/Repeat while not zero - Повторять, пока не равно/Повторять, пока не ноль) инструкция будет выполняться, пока ZF сброшен в 0 (не равно/не ноль) и значение в СХ отлично от нуля. Пока условия выполняются, префикс повторяет выполнения инструкций и уменьшает величину в СХ пошагово на 1. Флаги: в зависимости от используемых совместно с префиксами строковых инструкций Символьный код: REPE/REPZ/REPNE/REPNZ строковая инструкция Машинный код: REPE/REPZ: niiooio REPNE/REPNZ: niiooii 672 Часть VII. Обзорная информация
RET/RETF/RETN: Вернуться из процедуры (Return from a Procedure) Выполняют возврат из процедуры, ранее вызванной с помощью инструкции CALL. Ассемблер использует возврат типа near, если он используется в процедуре, помеченной как NEAR, или типа far, если процедура отмечена как FAR. RET типа near извлекает слово из вершины стека в регистр IP и увеличивает значение IP на 2; RET типа far извлекает два слова из вершины стека в IP и CS и увеличивает значение SP на 4. Непосредственное значение в операнде (например, RET 4) прибавляется к значению в SP. RETN и RETF используются для явного указания возврата типа near или far, соответственно. Флаги: не влияют Символьный код: RET/RETN/RETF [непосредственное-значение] Машинный код: четыре формата: В пределах сегмента: | ноооон | В пределах сегмента с непосредственным значением: |11000010|data-low|data-high| Межсегментный: | нооюи | Межсегментный с непосредственным значением: |11001010|data-low|data-high| ROUROR: Сдвинуть циклически строку влево/вправо (Rotate Left/Rotate Right) Циклически сдвигают влево или вправо биты байта, слова или двойного слова (для процессоров 80386 или более поздних) в регистре или в памяти. Операнд- это непосредственное значение, содержащее величину сдвига, или ссылка на регистр CL. Для процессоров 8086/8088 непосредственным значением может быть только 1, а большие значения должны содержаться в регистре CL; в более поздних процессорах непосредственное значение может быть любым числом до 31 включительно. В инструкции ROL выдвигающийся за пределы операнда слева бит помещается в бит 0, а все последующие биты сдвигаются на 1 влево; для ROR бит 0 перемещается в крайний слева (старший) бит операнда, а все предыдущие биты сдвигаются на 1 вправо. Перемещаемый за пределы операнда бит также помещается во флаг CF. См. также инструкции RCL и RCR. Флаги: влияют на CF и OF Символьный код: ROL/ROR регистр/память,СЬ/непосредственное-значение Машинный код: Глава 26. Набор инструкций PC 673 43 — 5-741
ROL: | noioocw|mod ooo r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) ROR: | H0i00cw|mod ooi r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) SAHF: Поместить в регистр флагов значение из АН Помещает 8 битов регистра АН в младшие 8 разрядов регистра флагов (см. также инструкцию LAHF). Флаги: влияет на AF, CF, PF, SF и ZF. у Символьный код: SAHF (операндов нет) Машинный код: юошю SAUSAR: Алгебраически сдвинуть влево/вправо (Shift Algebraic Left/Shift Algebraic Right) Сдвигают влево или вправо биты байта, слова или двойного слова в, регистре или памяти. Операнд - это непосредственное значение, содержащее величину сдвига, или ссылка на регистр CL. Для процессоров 8086/8088 непосредственным значением может быть только 1, а большие значения должны содержаться в регистре CL; в более поздних процессорах непосредственное значение может быть любым числом до 31 включительно. . Инструкция SAL сдвигает биты влево на указанное в операнде число позиций и заполняет освободившиеся справа разряды нулями; она ничем не отличается от SHL. Инструкция SAR сдвигает биты вправо на указанное в операнде число позиций и заполняет освободившиеся слева разряды значением бита знака. Все биты, выдвигающиеся за пределы регистра, теряются. Флаги: влияет на CF, OF, PF, SF и ZF (AF не определен) Символьный код: SAL/SAR регистр/память,СЬ/непосредственное значение Машинный код: SAL: | noioocw|mod loo r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) SHR: |lioioocw|mod ill r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) 674 Часть VII. Обзорная информация
SBB: Вычесть с переносом (Subtract with Borrow) Используется при вычитании многоразрядных двоичных чисел для учета в следующем действии бита переноса. SBB сначала вычитает содержимое CF из операнда 1, а затем вычитает операнд 2 из операнда 1 точно так же, как инструкция SUB. Для сложения аналогичные функции выполняет инструкция ADC. Флаги: влияет на AF, CF, OF, PF и ZF Символьный код: SBB регистр/память, регистр/память/непосредственное значение Машинный код: три формата: Регистр/память С регистром: |000110dw|mod reg r/m| Непосредственное значение из аккумулятора: |0001110w|--data--'|data, если w=l | Непосредственное значение из регистра/памяти: |100000sw|mod Oil r/m|--data--[data, если sw=01| SCAS/SCASB/SCASW/SCASD: Просмотреть строку (Scan String) Просматривает строку в памяти в поисках определенного значения. Инструкция SCASB ищет в памяти байт из AL, SCASW - слово из АХ, SCASD - двойное слово из ЕАХ (только для процессоров 80386 или более новых). Пара регистров ES:DI указывает адрес в памяти, с которого нужно начинать поиск. Эти инструкции обычно используются с префиксами REPE/REPNE, в сочетании с величиной в регистре СХ; используйте REPE, если ищете первое несовпадение со значением из аккумулятора, и REPNE, если - первое совпадение. Если флаг DF сброшен в 0, то операция просматривает байты в памяти в порядке возрастания адреса и увеличивает значение в DI с каждым байтом на 1; если DF установлен в 1, то байты просматриваются в порядке уменьшения адреса и содержимое Е)1 уменьшается на 1 с каждым байтом. Префикс REPnn с каждым выполнением инструкции уменьшает значение в СХ на 1 и прекращает выполнять их, когда СХ содержит 0. Если при использовании REPNE искомое значение не обнаружено, или при использовании REPE строка сплошь состоит из искомого значения, то СХ будет уменьшаться, пока не достигнет 0. В противном случае DI и SI будут содержать адрес следующего за совпадением/несовпадением элемента. Флаги: влияет на AF, CF, OF, PF, SF и ZF Символьный код: [REPnn] SCASB/SCASW/SCASD (операндов нет) Машинный код: loioillw Глава 26. Набор инструкций PC 675 43*
SETnn: Установить значение байта при условии (Set Byte Conditionally) (80386+) Устанавливают байт в зависимости от определенных условий. Это группа из 30 инструкций, включая SET(N)E, SET(N)L, SET(N)C и SET(N)S, подобных набору инструкций условного перехода (Jnn). Если проверяемое условие истинно, операция устанавливает байт операнда в 1; если нет - устанавливает его в 0. Например: СМР АХ,ВХ /Сравнить содержимое АХ и ВХ SETE CL ;Если равны, то установить содержимое CL в 1, ; если не равны - в 0 Флаги: не влияет Символьный код: SETnn регистр/память Машинный код: | ooooini | iooicond|mod ооо r/m| (cond меняется в зависимости от того, какое условие проверяется) SHUSHR: Логически сдвинуть влево/вправо (Shift Logical Left/Shift Logical Right) Сдвигают влево или вправо биты байта, слова или двойного слова в регистре или памяти. Операнд - это непосредственное значение, содержащее величину сдвига, или ссылка на регистр CL. Для процессоров 8086/8088 непосредственным значением может быть только 1, а большие значения должны содержаться в регистре CL; в более поздних процессорах непосредственное значение может быть любым числом до 31, включительно. SHL и SHR - это операции логического сдвига, воспринимающие бит знака, как бит данных. Инструкция SHL сдвигает биты влево на указанное в операнде число позиций и заполняет освободившиеся справа разряды нулями; она ничем не отличается от SAL. Инструкция SHR сдвигает биты вправо на указанное в операнде число позиций и заполняет освободившиеся слева разряды нулями. Все биты, выдвигающиеся за пределы регистра, теряются. Флаги: влияет на CF, OF, PF, SF и ZF (AF не определен) Символьный код: SHL/SHR регистр/память,СЬ/непосредственное значение Машинный код: SHL: | noioocw|mod loo r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) SHR: | noioocw|mod 101 r/m| (если с=0, сдвиг на 1 разряд, если с=1, то сдвиг на число из CL) 676 Часть VII. Обзорная информация
SHLD/SHRD: Сдвиг с двойной точностью влево/вправо (Shift Double Precision) (80386+) Сдвигает несколько битов в операнд. Инструкции используют три операнда. Операнд 1-16- или 32-разрядный регистр или элемент данных в памяти, содержащий значение, подлежащее сдвигу. Операнд 2 - регистр того же размера, что и операнд 1, содержащий биты, которые необходимо сдвинуть в операнд 1. Операнд 3 - регистр CL или непосредственное значение - задает величину сдвига. Флаги: влияет на CF, OF, PF, SF и ZF (AF не определен) Символьный код: SHLD/SHRD регистр/память, регистр, CL/непосредственное значение Машинный код: | OOOOllll 110100100 |mod reg r/m| STC: Установить в 1 флаг переноса CF (Set Carry Flag) Устанавливает в 1 флаг CF. Инструкция CLC, наоборот, сбрасывает CF в 0. Флаги: устанавливает в 1 флаг CF Символьный код: STC (операндов нет) Машинный код: liniooi STD: Установить в 1 флаг направления DF (Set Direction Flag) Устанавливает в 1 флаг DF. Пока он установлен в 1, строковые инструкции (например, MOVS) обрабатывают строки справа налево. Инструкция CLD сбрасывает DF в 0. Флаги: устанавливает DF в 1 Символьный код: STD (операндов нет) Машинный код: liimoi STI: Установить в 1 флаг прерываний IF (Set Interrupt Flag) Устанавливает в 1 флаг IF, разрешая маскируемые прерывания после выполнения следующей инструкции. Инструкция CLI сбрасывает флаг IF в 0. Флаги: устанавливает IF в 1 Глава 26. Набор инструкций PC 677
Символьный код: STI (операндов нет) Машинный код: 11111011 STOS/STOSB/STOSW/STOSD: Сохранить строку (Store String) Сохраняет содержимое аккумулятора в памяти. При использовании с префиксом REP в сочетании со значением в регистре СХ инструкции повторяют содержимое аккумулятора в памяти указанное в СХ количество раз. Это полезно, например, в случаях, когда необходимо очистить память. Инструкция STOSB повторяет в памяти байт из AL, STOSW - слово из АХ, STOSD - двойное слово из ЕАХ (только для процессоров 80386 или более новых). Пара регистров ES:DI указывает адрес в памяти, с которого нужно начинать повторения. Если флаг DF сброшен в 0, то операция помещает байты в памяти в порядке возрастания адреса и увеличивает значение в DI с каждым байтом на 1; если DF установлен в 1, то байты размещаются в цорядке уменьшения адреса и содержимое DI уменьшается на 1 с каждым байтом. REP уменьшает значение в СХ на 1 с каждым выполнением инструкции и прекращает выполнять их, когда СХ будет содержать 0. Флаги: не влияет Символьный код: [REP] STOSB/STOSW/STOSD (операндов нет) Машинный код: loioioiw SUB: Вычитание двоичных чисел (Subtract Binary Values) Вычитает двоичные числа в регистрах, памяти или непосредственные значения из регистров, или вычитает значение в регистре или непосредственное значение из значения в памяти. Значения могут быть в формате байтов, слов или двойных слов. (См. также описание инструкции SBB.) Флаги: влияет на AF, CF, OF, PF, SF и ZF. % Символьный код: SUB регистр/память, регистр/память/непосредственное значение Машинный код: три формата: Регистр/память И регистр: | 001010dw|mod reg r/m| Непосредственное значение и аккумулятор: |0010110w|--data—|data, если w=l| Непосредственное значение и регистр/память: |100000sw|mod 101 r/m|--data—|data, если sw=01| 678 Часть VII. Обзорная информация
TEST: Проверить биты (Test) Использует логическое И для поиска в поле определенной конфигурации битов, но не изменяет операнд назначения. Оба операнда должны быть байтами, словами или двойными словами в регистре или памяти. Второй операнд может быть непосредственной величиной. Операция сбрасывает в 0 флаг ZF, если любая пара сравниваемых битов содержит 1; в противном случае ZF устанавливается в 1. После выполнения можно использовать инструкции Jnn для проверки состояния флагов. Флаги: сбрасывает в О CF и OF, изменяет PF, SF и ZF (AF не определен) Символьный код: TEST регистр/память, регистр/память/непосредственное значение Машинный код: три формата: РегиСТр/памЯТЬ И регистр: | 1000010w|mod reg r/m| Непосредственное значение и аккумулятор: |1010100w|--data--|data, если w=l| Непосредственное значение и регистр/память: |1111011w|mod 000 r/m|--data--|data, если w=l| WAIT: Перевести процессор в режим ожидания (Put Processor in Wait State) Позволяет процессору находиться в режиме ожидания до внешнего прерывания, чтобы синхронизировать его работу с работой сопроцессора. Процессор ожидает, пока сопроцессор закончит вычисления и продолжает работу, получив сигнал с входа TEST. Флаги: не влияет Символьный код: WAIT (операндов нет) Машинный код: looiion XADD: Сложить и обменять (Exchange and Add) (80486+) Суммирует значения операндов источника и назначения, сохраняет сумму в операнде назначения и пересылает исходный операнд назначения в источник. Флаги: влияет на AF, CF, OF, PF, SF и ZF Символьный код: XADD регистр/память, регистр Машинный код: | OOOOllll 11100000b|mod reg r/m| Глава 26. Набор инструкций PC 679
XCHG: Обменять (Exchange) Обменивает содержимое двух регистров (например, XCHG AH,BL) или регистра и памяти (например, XCHG СХ,слово). Флаги: не влияет Символьный код: XCHG регистр/память, регистр/память Машинный код: два формата: Регистр с аккумулятором: | looioreg | РегиСТр/паМЯТЬ С регистром: | 1000011w|mod reg r/m| XLAT/XLATB: Заменить (Translate) Заменяет байты соответствующими им согласно таблице, например, при шифровании. Загрузите адрес таблицы в регистр ВХ или ЕВХ для 32-разрядного размера, после чего загрузите в AL байт, подлежащий замене. Инструкция использует величину в AL в качестве смещения, помещая в AL байт из таблицы с указанным смещением. XLATB - это просто синоним XLAT. Флаги: не влияет Символьный код: XLAT/XLATB [AL] (операнд AL необязателен) Машинный код: ноюш XOR: Логическое Исключающее ИЛИ (Exclusive OR) Выполняет логическую операцию Исключающее ИЛИ над битами двух операндов. Оба операнда должны быть байтами, словами или двойными словами, и XOR выполняет побитовую операцию. Для каждой пары обработанных битов результат (бит первого операнда) устанавливается в 0, если исходные биты совпадают, и устанавливается в 1, если не совпадают. (См. также описания инструкций AND и OR.) Флаги: Изменяет CF (0), OF (0), PF, SF и ZF (AF неопределен) Символьный код: XOR регистр/память, регистр/память/непосредственное значение Машинный код: три формата: Регистр/память С регистром: |001100dw|mod reg r/m| Непосредственное значение в регистр/память: |1000000w|mod 110 r/m|--data--|data, если w=l| Непосредственное значение в аккумулятор: |OOllOlOw|—data--|data, если w=l| 680 Часть VII. Обзорная информация
Часть VIII Приложения Приложение А. Преобразования чисел между шестнадцатеричной и десятичной системами счисления Приложение В. Набор символов ASCII Приложение С. Программа DEBUG Приложение D. Зарезервированные слова Приложение Е. Трансляция и компоновка программ Приложение F. Клавиатурные скан-коды и ASCII-коды
Приложение А Преобразования чисел между шестнадцатеричной и десятичной системами счисления В этом приложении описаны процедуры преобразования чисел из десятичной системы счисления в шестнадцатеричную и обратно. В первом параграфе приложения продемонстрировано пошаговое преобразование шестнадцатеричного числа А7В8 в соответствующее ему десятичное 42936, а во втором - обратное преобразование. Преобразование шестнадцатеричного числа в десятичное Для преобразования шестнадцатеричного числа в десятичное начните со старшего (первого слева) разряда, последовательно умножая результаты на шестнадцать и суммируя результаты. Поскольку умножение и сложение производятся в десятичной форме, шестнадцатеричные цифры от А до F рассматривайте как числа от 10 до 15. Вот пошаговая процедура преобразования: Первый разряд - А (10) 10 Умножить на 16 х1б 160 Прибавить следующий разряд - 7 + 7 167 Умножить на 16 х 16 2672 Прибавить следующий разряд - В (11) + 11 2683 Умножить на 16 х 16 42928 Прибавить следующий разряд, 8' + 8 Десятичное значение 42936 Можно также воспользоваться таблицей преобразований. Для числа 0А7В8Н найдите младший разряд (8) в столбце 1, второй разряд (В) в столбце 2, третий (7) - в столбце 3, четвертый (А) - в столбце 4. Просуммируйте соответствующие им в таблице АЛ десятичные числа: 682 Ассемблер. Язык и программирование для IBM PC
8 в разряде 1, В в разряде .2, 7 в разряде 3, А в разряде 4, столбце 1 столбце 2 столбце 3 столбце 4 Сумма 8 176 1792 40960 * 42936 Преобразование десятичного числа в шестнадцатеричное Чтобы преобразовать десятичное число 42936 в шестнадцатеричную форму, сначала нацело разделите 42936 на 16. Остаток, 8 - это младший шестнадцате- ричный разряд результата. Разделите частное от предыдущей операции, 2683, на 16. Остаток, 11, или В, - второй разряд. Продолжайте последовательно делить, пока частное не станет равным нулю. Вот последовательность шагов: Делимое и делитель 42936/16 2683/16 167/16 10/16 Частное 2683 167 10 0 Остаток 8 11 7 10 Шестнадцатеричное число 8 (младший разряд) В 7 А (старший разряд) Можно также использовать таблицу АЛ для преобразований десятичных чисел в шестнадцатеричную систему счисления. Найдите в таблице число, равное тому, которое нужно преобразовать, или ближайшее к нему меньшее. Запишите соответствующий шестнадцатеричный разряд. Вычтите найденное в таблице десятичное число из преобразуемого. Найдите в таблице число, равное разности, или ближайшее, меньшее, чем она и т. д. Процедура выглядит так: Начальное десятичное число Вычесть ближайшее меньшее число Разность Вычесть ближайшее меньшее число Разность Вычесть ближайшее меньшее число Разность Окончательное шестнадцатеричное число Десятичное число 42936 -40960 1976 -1792 184 -176 8 Ше стнадцатеричное число А000 700 ВО 8 А7В8 Приложение А. Преобразования чисел... 683
Q (U 0 Ж (U x У $ о шах В 2 Ш Ф х Р $ Q И <U х У $ о ас ф х Ф 1 о Я (U х 1 м я о 1 Д ф х п 1 ф 1 Я <D X о о о о о о о о о о о о о о о о н гЧ V0 гЧ н tO ю СМ гЧ tO ел О хг н to ГО ю щ V0 н V0 с- ю со <* о н н to гЧ СМ с- г* г* VD н н to ю <з* in оо <* со to СМ гЧ CVJ см СМ 00 СМ СМ Н Щ СМ СМ ел н со СМ см г> о н го н см см ю гЧ г> СЛ о см см см го ^ *< ю ю го ГО см см н ел о г* со t0 го Ю см го со со ** го со to г- го со со см см н го со о to to ел н го со см г- ю <* н го го со *» tO и го го о ю го со tO го to 8 ю о со го ^1* ** Чф to *г ** см о н ^ «tf со 00 to н ч* «ф <* н см t0 см ^ <* о со 4tf ел н «* «* <* to со со о н г> tO *г «* см со н 4tf г** го Г"* о и ^ ю ю о со щ о со см гЧ ю о со ф о см ю о со to г* см го щ о со со см <* см ю ю о со о Ф со со со со ю S см г» г** н см *• го гЧ ю «О ю to ел to ю со ю н to to r- ю ** CM to VD н см го ел 00 VD to ю <ф н ел см 10 to 10 ел см го tO tO о о н to to со г- см н to о н to Н to г* г- см н гЧ г* см ел г* гЧ г» см г- to со см г» см in г* со ю ^ г- см го о о <* го г* г» см Н in о ч* ^ г* н и г-* см ел н со *г о ел г* со н г- со со со см н со со <* о см со со to г* см го со со со см х? см ю со со о to со 8 со со со см г* г> н см я н со со «ф to го со ч* г> «? н см со ел ел ф <ф н ел ^ о го см ел ф to со to 00 ел *ф см со ел со ю ел ^ со гЧ г- го ч* ел ел Tf чу ел ^f ел ел о ю н ел 4J» о н ел н ел If) гЧ ^ см ел о Н < о to гЧ < о to ю см < о to ел о <* < о to го ю ю to < о ф г^ ю со ^ о н < о to н см г** г- г* to н < о to 10 «tf in го <tf со to CM < н н PQ to t^ H PQ to H CO CM PQ to If) о Ю ^ PQ to ел CO о CM f* PQ to 00 CO 9! in H H PQ to r> CO СЛ *J» m ЧГ CO H PQ to H о о ел г** CM ю ел CM m CM гЧ u CM СЛ гЧ и CM r* о го о CM to H СЛ «* u CM CO «t to CO r* и CM H СЛ CM CO in CM гЧ о CM ел in w CM 00 H о CM и CM Г* ^ in CM CM H CM CM CO и го гЧ Q со о CM Q со CM го го Q со ^ см го ю о со to ел Н ю со Q со со кг гЧ го to ГО'' н о со о со го о н со н см о со см ел о to to ел со <* го о ** н ы ^ см см W ^ со ю со pq ^ ** 00 г- ю ы ^ о in |> н ел ы ^ to о о со to ч* Н W 4J» см о Н со со я см W ч« со го to СЛ о СО ю г** го ы ю н рц о ^ см b о ** со го ь о ^ ^ гЧ to fe о ^г о 00 со ел h о ^ to со см г* ю Н рц о ^ см со in to н in CM fe о «* со н 00 in to CM о «tf ь н см 00 ^ 1 in to г^ со 684 Ассемблер. Язык и программирование для IBM PC
Приложение В Набор символов ASCII Аббревиатура ASCII расшифровывается как American Standard Code for Information Interchange - Американский Стандартный Код для Обмена Информацией. В таблице В.1 в конце этого приложения приведены коды и графические представления всех 256 символов ASCII (от ООН до FFH). Символы ASCII делятся на следующие группы: • ООН—1FH - управляющие символы для дисплея, принтера и передачи данных, предназначенные для запроса определенных действий; • 20Н—7FH - символы алфавита (английского), цифры и знаки пунктуации, включая 20Н - стандартный символ пробела; • 80Н—FFH - символы расширенной таблицы ASCII, включая символы некоторых иностранных (не английских) букв, букв греческого алфавита, математических символов и символов псевдографики (для рисования рамок и таблиц). Ниже приведено назначение управляющих символов с кодами от ООН до 1FH: 00 - Null (пустой символ) 01 - Start of header (начало заголовка) 02 - Start of text (начало текста) 03 - End of text (конец текста) 04 - End of transmission (конец передачи) 05 - Enquiry (запрос) 06 - Acknowledge (принято к сведению) 07 - Bell (звуковой сигнал) 08 - Backspace (забой) 09 - Horizontal tab (горизонтальная табуляция) 0А- Line feed (перевод строки) 0В - Vertical tab (вертикальная табуляция) ОС - Form feed (прогон листа) 0D - Return (возврат каретки) 0Е - Shift out (сдвиг наружу) OF - Shift in (сдвиг внутрь) 10 - Data line escape (отключить линию данных) 11 - Device ctl 1 (Управление 1) 12 - Device ctl 2 (Управление 2) 13 - Device ctl 3 (Управление З) 14 - Device ctl (Управление 4) 15 -Neg acknowledge (не принято) 16 - Synch idle (Синхронизация) 17 - End trans block (Конец блока) 18 - Cancel (Аннулирование) 19 - End of medium (Конец носителя) 1A - Substitute (Замена) IB - Escape (Отмена) 1С - File separator (Разделитель файла) ID - Group separator (Разделитель группы) IE - Record separator (Разделитель записи) IF - Unit separator (Разделитель единицы) Приложение В. Набор символов ASCII 685
Табл. В.1. Таблица символов ASCII 00 01 © 02 О 03 Г 04 ♦ 05 * ' 06 4 07 08 09 0А ов ОС 0D ОЕ OF 10 п 11 iJ 12 t 13 !! 14 Ц 15 § 16 "1 17 | 18 Т 19 | 1А 1В 1С 1 ID <- IE ▲ IF 20 21 ! 22 " 23 # 24 $ 25 % 26 & 27- ' 28 ( 29 ) 2A * 2B + 2C , 3D - 2E . 2F / 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7 38 8 39 9 ЗА : ЗВ ; ЗС < 3D = ЗЕ > 3F ? 40 @ 41 А 42 В 43 С 44 D 45 Е 46 F 47 G 48 Н 49 I 4А J 4В К 4С L 4D М 4Е N 4F О 50 Р 51 Q 52 R 53 S 54 Т 55 U 56 V 57 W 58 X 59 Y 5А Z 5В [ 5С \ 5D ] 5Е Л 5F 60 х 61 а 62 b 63 с 64 d 65 е 66 f 67 g 68 h 69 I 6A j 6B к 6C 1 6D m 6E n 6F о 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w 78 x 79 у 7A z 7B { 7C | 7D } 7E ~, 7F о 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F 9 й ё a a a a 9 ё ё ё l i i A A Ё ae I , 6 6 6 u u У 6 0 Ф £ ¥ Pt f АО а Al i A2 6 A3 u A4 n A5 N A6 a A7 ° A8 L A9 - AA - AB Уг AC % AD j AE « AF » BO || •bi I , B2 | B3 | B4 -| B5 H B6 -И B7 -„ B8 =j B9 4 BA || BB <j| ВС =0 BD Л BE =J BF -, CO L CI ± C2 T C3 h C4 — C5 + C6 |= C7 |^ C8 It C9 t! CA ± CB ir cc \y CD = CE# CF i DO'-U- Dl T D2 T D3 И- D4 1= D5 r D6 п- D7 i D8 + D9 DA r DB DC I DD | DE | DF ■ E0 a El p E2 Г ЕЗ к E4 S E5 a E6 ц E7 t E8 Ф E9 0 EA Q EB 5 EC oo ED ЕЕ EF П F0 = Fl ± F2 > F3 < F4 f F5 J F6 + F7 * F8 ° F9 • FA • FB V FC » FD 2 FE ■ FF 686 Ассемблер. Язык и программирование для IBM PC
Приложение С Программа DEBUG DOS-программу DEBUG можно применять для написания совсем небольших программ, для отладки программ, или для просмотра содержимого файла либо памяти. Эту программу (файл DEBUG.EXE) можно найти в директории DOS для систем, работающих под управлением DOS, или вызвать в Windows, выбрав в меню Start команду MS-DOS Prompt. Для получения копий экрана удобнее всего запустить DEBUG в отдельном окне. Пользуясь командами из системного меню окна, можно копировать фрагменты его содержимого в буфер обмена или вставлять текст из буфера в окно. Для запуска DEBUG из командной строки наберите DEBUG и нажмите <Enter>. DEBUG загрузится с диска в память. Когда символ-приглашение появится на экране, DEBUG будет готов воспринимать ваши команды (приглашение представляет собой дефис, хотя и похоже на текстовый курсор). DEBUG можно запускать двумя способами: 1. DEBUG без указания аргументов, чтобы просмотреть содержимое памяти или создать новый файл. 2. DEBUG <имя файла>, чтобы отладить или модифицировать существующую программу. Загрузчик программ размещает DEBUG в памяти, и DEBUG выводит дефис (-) в качестве приглашения. Регистры CS, DS, ES и SS инициализированы адресом 256-байтового (100Н) префикса сегмента программы (PSP), а ваша рабочая область в памяти начинается с адреса PSP + 100H. DEBUG отображает значения флагов следующим образом: 1 0 Переполнение OF OV NV Направление F DN UP Прерывание IF EI DI Знак SF NG PL Нуль ZF ZR NZ Вспомог. перенос AF AC NA Четность PF PE PO Перенос CF CY NC Ссылка на память может задаваться в виде сегмент:смещение, например, DS:120, или в виде только смещения, например, 120. Можно также напрямую обращаться к адресам в памяти, например, 40:17, где 40[0]Н - адрес начала сегмента, а 17Н - смещение. Заметьте, что сегмент данных для программ типа .ЕХЕ начинается с адреса DS:0, а для программ типа .СОМ - с DS:100. DEBUG воспринимает все вводимые числа как шестнадцатеричные, т.е. вводить завершающую букву Н не нужно. Клавиша F1 воспроизводит последнюю Приложение С. Программа DEBUG 687
команду по одной букве за нажатие, a F3 - целиком. Кроме того, DEBUG не различает строчные и заглавные буквы. Далее в алфавитном порядке перечислены команды DEBUG. A (Assemble) Транслирует ассемблерные команды в машинный код. Операция особенно полезна для написания и тестирования маленьких программ на ассемблере и для изучения небольших фрагментов кода. Адрес начала кода по умолчанию - CS:0100H. Формат команды: а [<адрес>], где адрес по умолчанию - 0100Н. DEBUG поддерживает следующие возможности: • определение элементов данных в программах с помощью DB и DW; • операнд в квадратных скобках, например, [12Е], указывает адрес (смещение) в памяти; • оператор PTR можно использовать для указания байта или слова, например, INCBYTEPTR[12E]. Можно применять все виды косвенной адресации с использованием регистров, например, [BP+DI] или 25 [ВХ]. Ниже приведен пример создания маленькой ассемблерной программы из пяти инструкций. Вводить нужно только инструкции; DEBUG указывает адрес сегмента кода (показанный здесь в виде хххх:) и смещения, начиная со 100Н: А (или А 100) <Enter> /Начать ввод с адреса хххх:0100 MOV СХ,[10D] <Enter> ;Прочитать в СХ значение из DS:10D хххх:0104 ADD СХ, 1А <Enter> /Прибавить к содержимому СХ ; непосредственное значение хххх:0107 MOV [10D],CX <Enter> /Поместить содержимое СХ в DS:10D хххх:010В JMP 100 <Enter> xxxx:010D DW 2500 <Enter> <Enter> Вернуться к началу программы Определение элемента данных по адресу DS:10D Завершение ввода С учетом размера PSP DEBUG устанавливает IP равным 100Н, и инструкции программы начинаются с этого адреса. Последнее нажатие <Enter> (два <Enter> подряд) указывает DEBUG, что ввод программы завершен. Теперь можно при необходимости использовать команды U (Unassemble) для просмотра машинного кода или Т (Trace) для наблюдения за выполнением программы. Можно заменять любые введенные команды или элементы данных, при условии, что новые инструкции или элементы будут иметь ту же длину. Например, чтобы заменить инструкцию ADD, расположенную по адресу 104Н, на SUB, введите А 104 <Enter> хххх:0104 SUB CX,1A <Enter> <Enter> 688 Ассемблер. Язык и программирование для IBM PC
При повторном исполнении программы значение в IP будет продолжать увеличиваться. Для занесения в IP значения 100Н используйте команду R (Register). Для выхода используйте команду Q. С (Compare) Сравнивает содержимое двух областей памяти. По умолчанию используется регистр DS. В команде можно указывать длину сравниваемых участков или диапазон адресов: 1. С <начальный_адрес_1> Ъ<длин>а <начальный_адрес_2> Пример команды, сравнивающей 20Н байтов, начиная с адреса DS:050, с байтами, начинающимися с DS:200: С 050 L20 200 /Сравниваем 20Н байт 2. С <начальный_адрес_1> <конечный_адрес_1> <начальный_адрес_2> Пример команды, сравнивающей байты от DS:050H до DS:070H с байтами, начиная с DS:200: С 050 070 200 ;Сравниваем диапазоны Операция выводит на дисплей адреса и содержимое несовпадающих байтов. D (Display или Dump) Выводит на экран содержимое области памяти в шестнадцатеричной и ASCII-формах. Регистр по умолчанию - DS. Эту команду также можно использовать, указывая длину или диапазон: 1. D [<начальный_адрес> [ъ<длина>]]. Можно указывать начальный адрес с необязательной длиной. Длина по умолчанию - 80Н (128) байт. Вот несколько примеров: D 200 ;Вывести 80Н байт, начиная с адреса DS:200H D /Вывести 8ОН байт, начиная с адреса конца последней ; выведенной области D CS:150 ;Вывести 80Н байт, начиная с адреса CS:150H D DS:20 L5 /Вывести 5 байт, начиная с адреса DS-.20H 2. D [начальный_адрес конечный_адрес]. Пример: D 30Cf 32C ;Вывести байты, начиная с 3 00Н, и заканчивая 32СН Е (Enter) Позволяет вводить в память данные или инструкции машинного кода. Регистр по умолчанию - DS, формат команды - е <адрес> [<список>]. Команда предоставляет две возможности: Приложение С. Программа DEBUG 689 44 — 5-741
1. Заменять байты на указанные в списке, как показано ниже: Е 105 13 ЗА 21 ;Ввести три байта, начиная с адреса DS:105H Е CS:211 21 2А /Ввести два байта, начиная с адреса CS:211H Е 110 'anything' ;Ввести строку символов, начиная с адреса DS:110H Использовать для ограничения строк можно как одинарные, так и двойные кавычки. 2. Последовательно редактировать байты. Введите адрес, где расположен байт, который вы хотите просмотреть: Е 12С ;Просмотреть содержимое байта DS:12CH Операция будет ожидать ввода с клавиатуры. Введите один или несколько байтов в шестнадцатеричной форме, разделяя их пробелами, начиная с DS:12CH. F (Fill) Заполняет область памяти значениями из списка. Регистр по умолчанию - DS. Команду можно использовать как с длиной, так и с диапазоном: 1. F <начальный_адрес> Ъ<длина> ' <данные>'. Например: F 210 L19 'Help!' /Используем длину 19Н (25) байт 2. F <начальный_адрес> <конечный_адрес> ' <данные>'. Например: F 210 229 'Help!' /Используем диапазон от 210Н до 229Н Обе команды заполняют^ячейки памяти, начиная с адреса DS:210H байтами, содержащими повторения строки 'Help!'. G(Go) Выполняет отлаженную программу на машинном языке, вплоть до указанной точки останова (breakpoint). Обязательно убедитесь, что IP содержит корректный адрес для исследуемого участка кода (некорректный адрес может Привести к непредсказуемым результатам). Кроме того, устанавливайте точки останова только в своих программах, а не в модулях программ операционной системы или BIOS). Программа будет при необходимости вызывать требуемые обработчики прерываний и останавливаться в ожидании, например, ввода с клавиатуры. Регистр по умолчанию - CS. Формат команды: G [=<начальный_адрес>] <адрес_остацова> [<адрес_останова> ...] Начальный адрес указывать необязательно. Можно указывать до десяти точек останова. Команда G ПА требует выполнить программу, начиная с адреса, заданного в IP, и заканчивая адресом 11 АН. 690 Ассемблер. Язык и программирование для IBM PC
H (Hexadecimal) Вычисляет сумму и разность двух шестнадцатеричных величин, указанных как н <значение_1> <значение_2>. Например, команда Н 14F 22 выведет в качестве результата 171 (сумму) и 12D (разность). I (Input) Считывает и выводит на дисплей один байт из порта. Формат команды I <адрес_порта>. L(Load) Загружает файл или данные из секторов диска в память. Файл можно указать одним из двух способов: указав имя файла в качестве аргумента при запуске DEBUG, или из запущенного DEBUG командой N (Name). Существует два формата команды L. 1. Загрузка уже указанного файла: l [<адрес>]. Параметр адрес указывает, по какому адресу загрузить в память файл. Если адрес не указан, файл загружается, начиная с адреса CS:100H. Чтобы загрузить файл, не указанный при запуске DEBUG, его необходимо предварительно указать (смотрите команду N): N <имя_файла> /Указать файл L ;Загрузить указанный файл, начиная с адреса CS:100H Чтобы повторно загрузить файл, просто задайте команду L вновь, не указывая адреса. DEBUG вновь считает файл с диска и соответственно проини- циализирует регистры. 2. Загрузка данных из секторов диска: L [<адрес> [<диск> <начало> <количество>] ]. • Адрес указывает адрес в памяти, начиная с которого будут размещаться прочитанные данные (по умолчанию это адрес CS:100). • Диск указывает номер диска (0 - А, 1 - В и т.д.). • Начало указывает шестнадцатиричный номер первого сектора, который нужно прочитать (относительному сектору 0 соответствует цилиндр 1, дорожка О, сектор 1). • Количество - это шестнадцатеричное число, указывающее, сколько следующих друг за другом секторов нужно прочитать. Приведенный ниже пример загружает в память, начиная с адреса CS:100, 15Н секторов, начиная с сектора 20Н, с диска А: L 100 0 20 15 Приложение С. Программа DEBUG 691 44*
Команда L возвращает в пару регистров ВХ:СХ число прочитанных байтов. Для файла типа .EXE DEBUG игнорирует любой указанный адрес и использует адрес загрузки из ЕХЕ-заголовка. Он также стирает из памяти заголовок; чтобы сохранить его, перед вызовом DEBUG переименуйте файл, присвоив ему другое расширение. М (Move) Перемещает (точнее, копирует) содержимое ячеек памяти. По умолчанию используется, регистр DS. Команду можно использовать, указывая длину или диапазон. 1. М <начальный_адрес> Ъ<длина> <адрес_назначения>. Пример: М DS:50 LlOO DS:400 ;Перемещаем фрагмент длиной 100Н байтов 2. М <начальный_адрес> <конечный_адрес> <адрес_назначения>. Пример: М DS:50 150 DS.-400 /Перемещаем байты с DS:50H no DS:150H Обе команды выполняют одни и те же действия - перемещают байты с адресами от DS:050H до DS:150H по адресу DS:400H. N (Name) Задает имя программы или файла, которые вы хотите прочитать с диска или записать на диск. Команда используется как n <имя_файла>, например N C:\SAM.COM Операция сохраняет введенное имя по адресу CS:80H в PSP. Первый байт (по адресу CS:80H) содержит длину (ОАН), за ним следует пробел и имя файла. Для чтения файла используется команда L (Load), для записи - W (Write). О (Output) Посылает байт в порт, указанный в команде в виде о <адрес_порта> <байт>. Р (Proceed) Выполняет инструкцию CALL, LOOP, INT или повторяемую строковую инструкцию (с префиксами REPnn), переходя к следующей инструкции. Формат команды р [=<адрес>] [<значение>], где =adpec - необязательный адрес начала, а значение - необязательное количество инструкций, которые нужно выполнить. Если адрес не указывается, используется текущий адрес из пары CS:IP, а если не указано значение, выполняется одна инструкция. Например, если вам нужно, чтобы выполнился обработчик прерывания INT 10H, введите Р, и он выполнится вплоть до следующей инструкции в вашей программе. Смотрите также команды G и Т. 692 Ассемблер. Язык и программирование для IBM PC
Q (Quit) Завершает сеанс работы DEBUG. Операция не сохраняет файлы; для этого необходимо применять команду W. R (Register) Выводит на дисплей содержимое регистров и следующую инструкцию. Формат команды r [<имя_регмстра>]. Вот примеры ее использования. R Вывести значения из всех регистров. RDX Вывести значение DX; DEBUG предоставит вам возможность нажать <Enter>, чтобы оставить содержимое регистра неизменным, или ввести от одной до четырех шестнадцатеричных цифр, которые будут записаны в DX. R IP Вывести значение ЕР; можно ввести другое значение с клавиатуры. R F Вывести состояние флагов; в начале этого приложения приведена таблица, содержащая расшифровку сокращений. S (Search) Ищет в памяти символы из списка. Если символы найдены, будет выведен их адрес, если нет - операция не выдаст никакого результата. По умолчанию используется регистр DS. Команду можно использовать, указывая длину или диапазон: 1. S <начальный адрео <длина> ' <данные>1. Например: S 300 L2000 'VIRUS' л Эта команда будет искать строку "VIRUS" в 2000Н байтах, начиная с адреса DS:300H. 2. S <начальный адрео <конечный адрео ' <данные>'. Например: S CS:100 400 51 Эта команда ищет символ с ASCII-кодом 51Н в байтах от CS:100H до CS:400H. i Т(Trace) Пошагово выполняет программу. Заметьте, что для выполнения обработчиков прерываний INT лучше пользоваться командой Р (Proceed). По умолчанию используется пара регистров CS:IP. Формат команды т [ =<адрео] [<значение>]. Необязательный операнд =adpec указывает DEBUG, откуда начинать пошаговое выполнение, а значение - количество команд, которые необходимо выполнить. Если операнды отсутствуют, DEBUG выполняет одну инструкцию и выводит на дисплей значения из регистров. Вот два примера использования этой команды: Приложение С. Программа DEBUG 693
Т /Выполнить одну следующую инструкцию Т 10 ;Выполнить следующие ЮН (16) инструкций Команда Т будет пытаться выполнить что угодно, и если по указанному адресу будет находиться непригодный к исполнению код, процессор может войти в состояние останова, после чего придется перезагружать компьютер. U (Unassemble) Дизассемблирует машинный код, то есть преобразует его в соответствующие символьные инструкции ассемблера. По умолчанию используются регистры CS:IP, а формат команды следующий: U [<начальный_адрес>] или U [<начальный_адрес> <конечный_адрес>] Указанная область памяти должна содержать корректный машинный код, и команда отобразит его в виде символьных инструкций. Вот три примера: U 100 ;Дизассемблировать 32 байта, начиная с адреса CS:100 U /Дизассемблировать 32 байта, начиная с адреса, на ; котором-закончилась последняя команда U, если она была U. 100 140 ;Дизассемблировать команды из адресов от CS:100 до CS:140 DEBUG некорректно дизассемблирует некоторые инструкции условных переходов и инструкции, специфичные для процессоров 80286 и последующих, но они все равно выполняются корректно, хотя DEBUG и представляет их как выражения DB. W (Write) Записывает файл из DEBUG. Необходимо заранее задать имя файла (командой N), если он не был загружен. По умолчанию используется регистр CS. Формат команды W [<адрес> [<диск> <начальный_сектор> <число_секторов>]] Программы можно записывать только в виде файлов .СОМ. Чтобы изменить программу типа .ЕХЕ, необходимо временно изменить ее расширение. Записывать файлы из DEBUG можно в двух случаях. 1. Прочитав с диска существующую программу, необходимо изменить ее, а затем сохранить. Для этого выполните следующие действия. • Загрузите программу на машинном языке в память под ее именем: DEBUG <имя файла>. • С помощью команд D просмотрите программу, и с помощью команд Е внесите изменения. 694 Ассемблер. Язык и программирование для IBM PC
• Командой W запишите на диск измененную программу. 2. С помощью DEBUG необходимо написать маленькую программу в машинных кодах и затем сохранить. Для этого необходимо следующее. • Используя команды A (Assemble) и Е, введите требуемые инструкции. • Введите имя программы командой N <имя.сом>. Расширение.файла обязательно должно быть .СОМ. • Поскольку только вам известна длина всей программы, введите длину в байтах в пару регистров ВХ:СХ. Рассмотрим такой пример: хххх:0100 MOV CL,42 хххх:0102 MOV DL,2A xxxx:0104 ADD CL,DL xxxx:0106 JMP 100 Хотя вы вводите символьные инструкции, DEBUG преобразует их в машинный код, и именно машинный код будет записан в файл. Поскольку длина последней инструкции, JMP 100, составляет два байта, размер программы равен 8 байтам (от 100Н до 107Н включительно). • С помощью команды R ВХ просмотрите значение в ВХ и введите 0, чтобы очистить его. • Командой R СХ просмотрите содержимое СХ. DEBUG выведет значение СХ на дисплей и запросит новое. Введите размер программы, 8. • Введите команду W <Enter>, чтобы записать программу на диск. DEBUG выведет сообщение "Writing 8 bytes." Если указано число 0, возможно, вы некорректно ввели длину программы - попытайтесь еще раз. Внимательно определяйте размер программы, поскольку значение должно быть шестнадцатеричным, и последняя инструкция может быть длиннее одного байта. (Размер программы можно определить, вычтя 100Н из смещения, показанного DEBUG после ввода последней команды программы и нажатия <Enter>. Например, в рассматриваемом примере после инструкции JMP 100 DEBUG выведет адрес хххх:0108. Значит, размер программы равен 108Н - 100Н = 8 байт. - Прим.ред.) Приложение С. Программа DEBUG 695
Приложение D Зарезервированные слова Ассемблер считает, что некоторые слова имеют особое значение; эти слова можно применять только с определенными целями. Эти слова можно разделить на четыре категории. 1. Имена регистров, например, АХ и АН. 2. Символьные инструкции, например, ADD или MOV. 3. Директивы (команды для транслятора), например, PROC и END. 4. Операторы, например, DUP или SEG. Если зарезервированные слова попытаться использовать как имена, транслятор может неправильно воспринять программу или выдать сообщение об ошибке. Ниже перечислены основные зарезервированные слова, но в разных версиях ассемблера могут использоваться и другие. Имена регистров АН, AL, АХ, ВН, BL, ВР, ВХ, СН, CL, CS, CX, DH, DI, DL, DS, DX, ЕАХ, EBP, EBX, ECX, EDI, EDX, EIP, ES, ESI, FS, GS, ГР, SI, SP, SS Символьные инструкции AAA, AAD, AAM, AAS, ADC, ADD, AND, ARPL, BOUND, BSF, BSR, BTn, CALL, CBW, CDQ, CLC, CLD, CLI, CLTS, CMC, CMP, CMPSn, CMPXCHG, CMPXCHG8B, CWDn, DAA, DAS, DEC, DIV, ENTER, ESC, HLT, IDiV, MUL, IN, INC, INSn, INT, INTO, ШЕТ, JA, JAE, JB, JBE, JCXZ, JE, JECXZ, JG, JGE, JL, JLE, JMP, JNA, JNAE, JNB, JNBE, JNE, JNG, JNGE, JNL, JNLE, JNO, JNP, JNS, JNZ, JO, JP, JPE, JPO, JS, JZ, LAHF, LAR, LDS, LEA, LEAVE, LES, LFS, LGDT, LGS, LIDT, LLDT, LMSW, LOCK, LODSn, LOOP, LOOPE, LOOPNEn, LOOPNZn, LOOPZ, LSL, LSS, LTR, MOV, MOVSn, MOVSX, MOVZX, MUL, NEG, NOP, NOT, OR, OUTn, POP, POP A, POP AD, POPF, POPFD, PUSH, PUSHAD, PUSHF, PUSHFD, RCL, RCR, REN, REP, REPE, REPNE, REPNZ, REPZ, RET, RETF, ROL, ROR, SAHF, SAL, SAR, SBB, SCASn, SETnn, SGDT, SHL, SHLD, SHR, SHRD, SIDT, SLDT, SMSW, STC, STD, STI, STOSn, STR, SUB, TEST, VERR, VERRW, WAIT, XADD, XCHG, XLAT, XOR 696 Ассемблер. Язык и программирование для IBM PC
Директивы ALIGN, .ALPHA, ASSUME, BYTE, .CODE, COMM, COMMENT, .CONST, .CREF, .DATA, .DATA?, DB, DD, DF, DOSSEG, DQ, DT, DW, DWORD, ELSE, END, ENDIF, ENDM, ENDP, ENDS, EQU, .ERRnn, EVEN, EXITM, EXTRN, EXTERN, .FARDATA, .FARDATA?, FWORD, GROUP, IF, IF1, IF2, IFB, IFDEF, IFDIF, IFE, IFIDN, IFNB, IFNDEF, INCLUDE, INCLUDELIB, IRP, IRPC, LABEL, .LALL, .LFCOND, .LIST, LOCAL, MACRO, .MODEL, NAME, ORG, &OUT, PAGE, PROC, PUBLIC, PURGE, QWORD, .RADIX, RECORD, REPT, .SALL, SEGMENT, .SEQ, .SFCOND, .STACK, STRUC, SUBTTL, .TFCOND, TITLE, TFWORD, UNION, WORD, .XALL, .XCREF, .XLIST Операторы AND, BYTE, COMMENT, CON, DUP, EQ, FAR, GE, GT, HIGH, LE, LENGTH, LINE, LOW, LT, MASK, MOD, NE, NEAR, NOT, NOTHING, OFFSET, OR, PTR, SEG, SHL, SHORT, SHR, SIZE, STACK, THIS, TYPE, WHILE, WIDTH, WORD, XOR Приложение D. Зарезервированные слова 697
Приложение Е Трансляция и компоновка программ В этом приложении описываются способы трансляции и компоновки программ, создания файлов перекрестных ссылок, и преобразования .ЕХЕ-прог- рамм в формат .СОМ. Для преобразования исходной программы в исполняемый модуль необходимо выполнить следующие действия. 1. Оттранслировать исходную программу. В результате будет создан объектный файл (.OBJ), а также (хотя и не обязательно) файл перекрестных ссылок и файл листинга (.LST). 2. Скомпоновать полученный объектный файл. При этом будет создан исполняемый файл (.ЕХЕ), а также, возможно, файл карты связей (.MAP) и файл библиотеки (.LIB). Компоновка может соединить несколько объектных файлов в единый исполняемый файл. 3. Полученный файл типа .ЕХЕ можно преобразовать в .СОМ. Различные версии ассемблера выполняют эти операции по-разному и предоставляют практически бесконечный набор возможностей. Далеко не все эти возможности рассмотрены в данном приложении. Это приложение содержит описания Microsoft MASM 6.1 и 5.1 и Borland TASM. Термин путь в данном приложении означает директорию и файл в применении к файлам и программам; его можно заменять требуемым значением, например, С: или-С:\ТЕМР\. Microsoft MASM 6.1 Ассемблер (транслятор) Microsoft MASM 6.1 использует команду ML, но для обеспечения совместимости с предыдущими версиями использует также команду MASM. Команда ML позволяет транслировать и компоновать произвольное число программ в один исполняемый модуль. Формат команды ML для трансляции и компоновки следующий: ML [<опции>] файл1.asm [[<опции>] файл2.аБт] ... [/LINK <опции>] Опции командной строки нужно указывать, соблюдая регистр букв, и начинать с символа /. Доступны следующие опции. /AT - указывает, что программа использует модель памяти Tiny, а ассемблер должен преобразовать ее в файл типа .СОМ. /с - оттранслировать, но не компоновать. 698 Ассемблер. Язык и программирование для IBM PC
/Fl - сгенерировать файл листинга (.LST). /Fm - сгенерировать файл карты связей (.MAP). /Fr - сгенерировать файл с таблицей перекрестных ссылок (.SBR). /Sn - подавить вывод таблицы символов. /Zd - включить номера строк в отладочную информацию. /Zi - включить символьную отладочную информацию (для CodeView). Вот два примера: ml /sn /Fr program23 .asm (оттранслировать и скомпоновать) ml /at /zd program 34.asm (оттранслировать и скомпоновать в файл типа .СОМ) Полезна также команда ML -?, которая выводит на дисплей справку по опциям и синтаксису команды. Кроме того, можно использовать команду MASM, работающую точно так же, как и в MASM 5.1, описанном ниже. Программы типа .СОМ Если исходная программа использует модель памяти Tiny, ML может сгенерировать программу типа .СОМ, но если программа использует обычные сегменты, то будет создана программа .ЕХЕ, и ее можно будет преобразовать в .СОМ программой EXE2BIN. Компоновка Можно выполнять трансляцию без компоновки (используя опцию /с) или транслировать и компоновать сразу (это делается по умолчанию). Можно запускать программу LINK отдельно для компоновки. Формат ее вызова следующий: LINK [<опции>] <объектные_файлы>, [<исполняемый_файл>] , [<файл_карт>] , [ <библиотеки>] Файл перекрестных ссылок Опция /Fr в командной строке, которой вызывается ML, вызывает генерацию файла .SBR. Чтобы преобразовать его в сортированный файл перекрестных ссылок типа .CRF, используйте программу CREF: CREF < файл!.SBR> <файл2.REF> Приложение Е. Трансляция и компоновка программ * 699
Microsoft MASM 5.1 Эта версия требует выполнения нескольких отдельных операций для трансляции, компоновки и преобразования файлов из .ЕХЕ в .СОМ-формат. Формат командной строки для ее вызова следующий: MASM [<опции>] <исходный_файл> [, <объектный_файл>] [, <файл_лис- тингов>] [, <файл_перекрестных_ссьшок>] Опции описаны ниже. Исходный файл указывает файл исходной программы. Ассемблер подразумевает наличие расширения .ASM, поэтому его указывать не обязательно. Можно также ввести путь, например, C:\TEMP\1 .asm. Объектный файл - это имя генерируемого объектного файла. Файл листингов - это имя генерируемого файла листингов .LST, содержащего исходный июбъектный код. Файл перекрестных ссылок - это имя файла перекрестных ссылок .CRF, содержащего символы для перекрестных ссылок. Для каждого файла можно указывать полный путь. Например: MASM l.asm, l.obj, C:\LST\l.lst, l.crf Следующая команда позволяют создать объектный файл и файл перекрестных ссылок с одинаковыми именами по умолчанию без файла листинга: MASM 1файл,„. Для команды MASM доступны следующие опции: /С - Создать таблицу перекрестных ссылок в файле .LST. /L - Создать обычный файл листингов (.LST). /N - Подавить генерацию символьной таблицы в файле .LST. /Z - Вывести на экран строки исходного текста с ошибками. /ZD - Включить в объектный файл информацию о ' номерах строк для CodeView. /ZI - Включить в файл информацию о номерах строк и символьную информацию для CodeView. Файл перекрестных ссылок Файл .CRF используется для создания листинга меток, символов и переменных программы. Используйте программу CREF для преобразования файла .CRF в отсортированный файл .REF: CREF Oaun.CRF Файл-REF Файл .CRF - это файл перекрестных ссылок, сгенерированный при трансляции. Программа подразумевает наличие расширения .CRF по умолчанию, и указывать его не обязательно. 700 Ассемблер. Язык и программирование для IBM PC
Файл .REF- это имя генерируемого файла. Путь и имя файла могут быть те же, что и у исходного файла .CRF, или другие. Компоновка Командная строка для компоновки программы в MASM 5.1 выглядит так: LINK объектный файл, исполняемый файл [, файл карт] [, файл библиотеки] Объектный файл - это файл, который необходимо скомпоновать в исполняемый. Компоновщик подразумевает наличие расширения .OBJ по умолчанию, и указывать его не обязательно. Исполняемый файл - это имя генерируемого .ЕХЕ-файла. Можно указать для него отдельный путь. Файл карт - это имя генерируемого файла .MAP, указывающего взаимное расположение и размеры сегментов, а также ошибки, обнаруженные компоновщиком. Если в качестве этого имени задать CON (то есть консоль), то компоновщик будет выводить карту на экран. Файл библиотек - это имя для файла библиотеки. Если объектный файл должен иметь то же имя, что и исходный, не нужно повторять его; достаточно указать путь, чтобы заказать генерацию файла. Например, строка LINK a:A05ASMl, a:, CON компонует объектный файл A05ASM1.OBJ в файл типа .ЕХЕ на диск а:, и выводит карту связей на экран. Если исходная программа была написана для формата .СОМ, компоновщик выдаст предупреждение об отсутствии стекового сегмента "Warning: no stack segment." Чтобы скомпоновать более одного объектного файла в один исполняемый, разделите их знаками плюс (+), например, так: LINK progl+prog2+prog3 Если вы собираетесь использовать CodeView, включите в командную строку ключ /ZI. Для компоновки используйте ключ /СО, в виде LINK /СО имя файла. Преобразование объектных файлов в файлы типа .СОМ Используйте программу EXE2BIN, чтобы преобразовать файлы типа .ЕХЕ в .СОМ. Однако необходимо, чтобы исходная программа должна требованиям, предъявляемым к программам .СОМ. EXE2BIN <имя_файла>, <имя_файла>.СОМ Приложение Е. Трансляция и компоновка программ 701
Первый операнд - это всегда файл типа .ЕХЕ, поэтому не обязательно указывать расширение. Второй операнд - это любое корректное имя файла с расширением .СОМ. Если вы пропустите окончание .СОМ, по умолчанию результирующему файлу будет присвоено расширение .BIN, и его придется переименовывать в .СОМ вручную. Borland Turbo Assembler (TASM) Turbo Assembler позволяет вам транслировать несколько файлов, каждый со своими собственными опциями, одной командной строкой. В командной строке можно использовать символы замены (* и ?). Для трансляции всех исходных файлов в текущей директории используйте команду TASM *. Для трансляции программ с именами PROG1.ASM, PROG2.ASM, и т.д. введите TASM PROG?. Можно вводить группы имен файлов, разделяя их знаком +. Следующая командная строка транслирует PROG1 и PROG2 с ключом /L и PROG3 с ключом /Z: TASM /L PROG1 PROG2+ /Z PROG3 Ключ /L указывает TASM сгенерировать файл .LST, а ключ /Z - вывести на экран все строки исходного кода с ошибками. Ввод команды TCREF без параметров выводит справку по этой программе (ключи.и параметры командной строки). Режим Ideal имеет много дополнительных возможностей. Borland предлагает еще две версии ассемблеров, TASMX и TASM32, предназначенные для защищенного режима (protected mode). Файлы перекрестных ссылок Файл .XRF используется для создания листингов перекрестных ссылок, меток, символов и переменных программы. Используйте программу TCREF для преобразования его в отсортированный файл перекрестных ссылок: TCREF «£aifoil>.XRF <файл2>.11ЕР' Правила использования TCREF аналогичны таковьш для CREF из комплекта MASM 5.1. Например, команда TCREF CiASMPROG, С: записывает файл перекрестных ссылок ASMPROG.REF на диск С:. Компоновка Командная строка для компоновки программ TASM следующая: TLINK <объектный_файл>, <исполняемый_файл> [, <файл_карт>] [, <файл_библиотеки>] 702 Ассемблер. Язык и программирование для IBM PC
Правила использования TLINK аналогичны таковым для LINK. Преобразование объектных файлов в файлы типа .СОМ Используйте программу TLINK для прямого преобразования объектного файла в файл .СОМ, если исходная программа была написана с учетом требований, предъявляемых к файлам типа .СОМ. Используйте ключ /Т: TLINK /T <объектный_файл>, <файл>.СОМ, CON Опции отладки Если вы собираетесь использовать Turbo Debugger для отладки, используйте ключ /ZI при трансляции и ключ /V - для компоновки, например, TLINK /V имя файла. Обозначения в файлах .LST За листингами в файлах .LST следуют таблицы сегментов и групп и таблицы символов. Таблица сегментов и групп Эта таблица имеет заголовок, подобный следующему: Name Length Align Combine Class Name (имя) - это столбец, в котором перечислены имена всех сегментов и групп в алфавитном порядке. Столбец Length (длина) указывает в шестнадцатеричном виде длины сегментов. Столбец Align (выравнивание) указывает типы выравнивания сегментов, например, BYTE, \УОМУили PARA. Столбец Combine (совмещение) указывает определенные для сегментов типы совмещения, например, STACK для стековых сегментов, NONE, если совмещение не определено, PUBLIC - для внешних определений, или шестнадцате- ричный адрес - для определений AT. Столбец Class (класс) указывает имена классов сегментов, указанные в директивах SEGMENT. Таблица символов Эта таблица имеет приблизительно такой заголовок: Name Type Value Attribute Столбец Name (имя) перечисляет имена всех определенных в программе элементов в алфавитном порядке. Столбец Туре (тип) указывает типы элементов, например: • L NEAR или L FAR - для меток типа near и far, соответственно; Приложение Е. Трансляция и компоновка программ 703
• N PROC или F PROC - для процедур типа near и far, соответственно; • BYTE, WORD, DWORD, FWORD, QWORD или TBYTE - для элементов данных; • ALIAS - псевдоним для другого символа; • NUMBER - абсолютная метка; • OPCODE - подстановка (замена) для операнда инструкции; • TEXT - подстановка для текста. Столбец Value (значение) содержит смещение начала сегмента для имен, меток и процедур в шестнадцатеричной форме. Столбец Attribute (атрибут) содержит атрибуты символа, включая его сегмент и длину. MASM 6.1 также создает таблицу, в которой перечислены процедуры, локальные метки/переменные и параметры программы. 704 Ассемблер. Язык и программирование для IBM PC
Приложение F Клавиатурные скан-коды и ASCII-коды В таблицах этого приложения клавиши распределены по четырем группам (в значительной мере произвольно). Для каждой клавиши в столбцах указаны коды, передаваемые клавиатурой при нажатии этой клавиши самостоятельно и в сочетании с различными клавишами (Shift, Ctrl и Alt). В столбцах "Shift", "Ctrl" и "Alt" байты, помещаемые клавиатурой в регистры АН и AL, указываются в шестнадцатеричной форме. Например, при нажатии клавиши "a" BIOS помещает в АН 1ЕН (это скан-код), а в AL- 61 (ASCII-код). Если одновременно нажата клавиша Shift (т.е. передается символ "А"), клавиатура поместит в АН 1ЕН, а в AL - 41Н. Скан-коды 85Н и выше относятся к расширенной клавиатуре. Буквы Аи а ВиЬ Сие Dnd Ей е FHf 6wg НиЬ I и i J И]' Кик Ьи1 Мит Nhii Оио Рир QHq Яиг Обычные Скан- код IE 30 2Е 20 12 21 22 23 17 24 25 26 32 31 18 19 10 13 ASCII- код 61 62 63 64 65 66 67 68 69 6А 6В 6С 6D 6Е 6F 70 71 72 Скан код IE 30 2Е 20 12 21 22 23 17 24 25 26 32 31 18 19 10 13 Shift - ASCII- код 41 42 43 44 45 46 47 48 49 4А 4В 4С 4D 4Е 4F 50 51 52 Скан код IE 30 2Е 20 12 21 22 23 17 24 25 26 32 31 18 19 10 13 Ctrl - ASCII- код 01 02 03 04 05 06 07 08 09 0А OB ОС 0D 0Е ■ 0F 10 11 12 Скан код IE 30 2Е 20 12 21 22 23 17 24 25 26 32 31 18 19 10 13 Alt - ASCII- код 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Приложение F. Клавиатурные скан-коды и ASCII-коды 705 45 — 5-741
Буквы Shs THt Uhu V и v W и w Хих Уиу Z и z Пробел Обычные Скан- код IF 14 16 2F 11 2D 15 2С 39 ASCII- код 73 74 75 76 77 78 79 7А 20 Скан код IF 14 16 2F 11 2D 15 2С 39 Shift - ASCII- код 53 54 55 56 57 58 59 5А 20 Скан код IF 14 16 2F 11 2D 15 2С 39 Ctrl - ASCII- код 13 14 15 16 17 18 19 1А 20 Скан- код IF 14 16 2F 11 2D 15 2С 39 Alt - ASCII- код 00 00 00 00 00 00 00 00 20 Функц. клавиши F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 Обычные Скан- код ЗВ зс 3D ЗЕ 3F 40 41. 42 43 44 85 86 ASCII- код 00 00 00 00 00 00 00 00 00 00 00 00 Скаь код 54 55 56 57 58 59 5А 5В 5С 5D 87 88 Shift 1- ASCII- код 00 00 00 00 00 00 00 00 00 00 00 00 Скан код 5Е 5F 60 61 62 63 64 65 66 67 89 8А Ctrl - ASCII- код 00 00 00 00 00 00 00 00 00 00 00 00 Скан код 68 69 6А 6В 6С 6D 6Е 6F 70 71 8В 8С Alt - ASCII- код 00 00 00 00 00 00 00 00 00 00 00 00 Цифровая клавиатура Ins иО End и 1 ■1 и2 Обычные Скан- код 52 4F 50 ASCII- код 00 00 00 Скан код 52 4F 50 Shift - ASCII- код 30 31 32 Скан код 92 75 91 Ctrl - ASCII- код 00 00 00 скан- код 00 00 Alt ASCII- код 01 02 706 Ассемблер. Язык и программирование для IBM PC
Цифровая клавиатура PgDn и 3 <-и4 5 -> иб Ноте и 7 Ти8 PgUp и 9 + - Del и. * Обычные Скан- код 51 4В 4С 4D 47 48 49 4Е 4А 53 37 ASCII- код 00 00 00 00 00 00 00 2В 2D 00 2А Скан код 51 4В 4С 4D 47 48 49 4Е 4А 53 37 Shift - ASCII- код 33 34 35 36 37 38 39 2В 2D 2Е 2А Скан код 76 73 8F 74 77 8D 84 90 8Е 93 96 Ctrl - ASCII- код 00 00 00 00 00 00 00 00 00 00 ч00 скан- код 00 00 00 00 00 .00 00 00 4Е 4А 37 Alt ASCII- код 03 04 05 06 07 08 09 00 00 00 Верхняя строка ' и~ 1и! 2 и" Зи# 4и$ 5и% 6иЛ 7и& 8и* 9и( Ои) - и_ = и + Обычные Скан- код 29 02 03 04 05 06 07 08 09 0А ов ОС 0D ASCII- код 60 31 32 33 34 35 36 37 38 39 30 2D 3D Скан код 29 02 03 ^04 05 06 07 08 09 0А ОВ ОС 0D Shift - ASCII- код 7Е 21 40 23 24 25 5Е 26 2А 38 29 5F 2В Ctrl Скан- ASCII- код код 03 00 07 IE ОС IF Скан- код 29 78 79 7А 7В 7С 7D 7Е 7F 80 81 82 83 Alt • ASCII- код 00 00 00 00 00 00 00 00 00 00 00 00 00 Приложение F. Клавиатурные скан-коды и ASCII-коды 707 45*
Спец. клавиши Esc Backspace Tab Enter Обычные Скан- код 01 0Е OF 1С ASCII- код IB 08 09 0D Скан код 01 ОЕ OF 1С Shift - ASCII- код IB 08 00 OD Скан код 01 ОЕ 94 1С Ctrl - ASCII- код IB 7F 00 OA Скан- код 01 ОЕ А5 1С Alt ASCII- код 00 00 00 00 Знаки пунктуации [и{ ]и} ;и: •И" \и| ,и< .и> /и? Обычные Скан- код 1А 1В 27 28 2В 33 34 35 ASCII- код 5В 5D ЗВ 27 5С 2С 2Е 2F Скан код 1А 1В 27 28 2В 33 34 35 Shift - ASCII- код 7В 7D ЗА 22 7С зс 3E 3F Скан- код 1А 1В 2В Ctrl ■ ASCII- код IB ID 1С Скан- код 1А IB 27 28 '2В 33 34 35 Alt ASCII- код 00 00 00 00 00 00 00 00 Далее перечислены дублирующие клавиши на расширенной клавиатуре (первая строка, символ / представляет собой обычный символ ASCII, а остальные - командные клавиши). Клавиша / (слеш) Del 1 End Enter Home Ins <- PageDown Обычные Скан- код E0 53 50 4F E0 47 52 4B 51 ASCII- код 2F E0 E0 E0 0D E0 E0 E0 E0 Скан код E0 53 50 4F E0 47 52 4B 51 Shift - ASCII- код 2F E0 E EO OD EO EO EO EO Скан- код 95 93 91 75 EO 77 92 73 76 Ctrl ASCII- код 00 EO EO EO OA EO EO EO EO Скан- код A4 A3 АО 9F A6 97 A2 9B Al Alt ASCII- код 00 00 00 00 00 00 00 00 00 У 708 Ассемблер. Языки программирование для IBM PC
Клавиша PageUp -» т Обычные Скан- код 49 4D 48 ASCII- код ЕО ЕО ЕО Скан код 49 4D 48 Shift - ASCII- код ЕО ЕО ЕО Скан- код 84 74 8D Ctrl ASCII- код ЕО ЕО ЕО Скан- код 99 9D 98 Alt ASCII- код 00 00 00 Некоторые управляющие клавиши имеют скан-коды, но BIOS не помещает их в буфер клавиатуры. Эти клавиши перечислены ниже: CapsLock NumLock ScrollLock Shift (левый) Shift (правый) Alt Ctrl PrintScreen ЗА 45 46 2A 36 38 ID 37 Приложение F. Клавиатурные скан-коды и ASCII-коды 709
Предметный указатель Символ %OUT/ECHO-директивы 629 .286, .386, .486, .586-директивы процессора 90,630 .ALPHA-директива 619 .CODE-директива 96,620 .СОМ-программы 126 -загрузка 559 -инициализация 128 - преобразование из .ЕХЕ 129 -сегменты 127 .СОМ-программы, создание - с помощью MASM 5.1 701 - с помощью М ASM 6.1 699 - с помощью TASM 703 .CONST-директива 621 .CREF - директива 621 .CRF-файл 699,700 .DATA-директива 96,621 .DATA?-директива 621 .DB -директива 102 .DOSSEG-директива 621 .ERR - директивы 624 .ЕХЕ-программы -заголовок 559 -загрузка 559 .EXIT - директива 97,625 .FARDATA - директива 626 .FARDATA?-директива 626 .LALL - директива 509 .LIST-директива 628 .LST-файлы -обозначения 703 -таблица сегментов и групп 703 -таблица символов 703 .MODEL-директива 96,628 .NOCREF-директива 638 .NOLIST-директива 638 См. директива .XLIST .REF-файл 700,702 .SALL-директива 509 .SBR-файл 699 .SEQ-директива 636 .STACK-директива 96,636 .STARTUP -директива 97,636 .STRUC-директива 636 .STRUCT-директива 636 .XALL - директива 509 .XCREF-директива 638 .XLIST- директива 638 .XRF-файл 702 @code - предопределенное имя 118 @data- предопределенное имя 118 ©filename - предопределенное имя 118 = (знак равенства) - директива 106 Цифра 80286 - процессор 28 80386-процессор 29 80486 - процессор 29 8086 - процессор 28 8088-процессор 28 AAA - инструкция 136, 326,645 AAD - инструкция 136,332,645 ААМ - инструкция 136, 330, 645 AAS - инструкция 136,326,329 ABS - тип (константа) 530 ADC - инструкция 134,303,646 ADD - инструкция 134, 141,646 AF - флаг вспомогательного переноса 41 AF - флаг дополнительного переноса 161 АН-регистр 38 AL-регистр 38 ALIGN-директива 150,619 Alt-клавиша 255,271 - использование для ввода символов 263 AND - инструкция 135, 175,647 ASCII 27 - ввод символов 263 - вывод символов на экран 195 - обработка чисел 326 -описание 685 - символы для рамок и меню 250 - символы псевдографики 250 -таблица символов 686 - управляющие символы 685 710 Ассемблер. Язык и программирование для IBM PC
ASCIIZ-строки 413 ASCII-строки, чтение из файлов 432 ASCII-числа -вычитание 329 -деление 332 - получение из двоичных 338 -преобразование в двоичные 336 -сложение 326 -умножение 330 ASSUME-директива 90,619 АХ-регистр 38 в BCD-формат чисел 325 - неупакованные числа 325 -упакованные числа 325, 334 ВН-регистр 39 BIOS 32 - байт состояния дисковых операций 473 - байты состояния клавиатуры 271 - буфер клавиатуры 256 -дата производства 61 - информация о портах 60 - области данных клавиатуры 255 - область данных 46,59,584 - печать (INT17H) 499 -прерывания 591 - просмотр copyright 61 - серийный номер 61 BL- регистр 39 ВР- регистр 38 BSF - инструкция 137,647 BSR- инструкция 137,647 ВТ - инструкция 137,648 ВТС-инструкция 137,648 BTR-инструкция 137,648 BTS - инструкция 137,648 ВХ-регистр 39 BYTE. См. DB-директива С CALL- инструкция 136, 167,648 CBW - инструкция 136, 300, 649 CDQ- инструкция 136,301,649 Celeron - процессор 29 CF - флаг переноса 41, 160 СН-регистр 39 CL-регистр 39 CLC - инструкция 135,649 CLD - инструкция 135, 649 CLI - инструкция 135,650 CMC - инструкция 135,650 СМР - инструкция 137, 162,650 CMPS - инструкция 137,651 CMPSB-инструкция 288,651 CMPSD-инструкция 288,651 CMPSn-набор инструкций 137,651 CMPSW-инструкция 288,651 CMPXCHG-инструкция 137,651 CMPXCHG8B - инструкция 137, 652 СОММ-директива 620 COMMENT-директива 621 CREF, программа (. 699,700 CS-регистр 36 CS - сегмент кода 34 Ctrl-клавиша 271 CWD-инструкция 136,301,652 CWDE- инструкция 136, 301, 652 СХ- регистр 39 о DAA - инструкция 136, 334,653 DAS - инструкция 136, 334,653 DB-директива 99 DD-директива 99, 104 DEBUG, программа 57,687 - ввод символьных инструкций 72 - дизассемблирование 72 - значения флагов 65 - команда А 72,688 - команда D 58,689 - команда Е 62 - команда F 690 - команда G 690 -командаН 71,691 - команда I 691 - команда L 691 -командам 692 - команда N 692 -команда О 692 - команда Р 692 - команда Q 693 - команда R 693 - команда S 693 - команда Т 66,693 - команда U 72, 694 -команда W 694 Предметный указатель 711
-команда С 689 -команды 58,688 - описание экрана 687 - повторное выполнение инструкций 71 - просмотр памяти с помощью 59 -символьные инструкции, ввод 72 -сохранение программ в 71 -чтение программ в 71 DEC-инструкция 134, 141,653 DF-директива 99 DF - флаг направления 40, 161,278 DH-регистр 39 DI-регистр 40 DIV - инструкция 134,654 DIV- инструкция: ,.- 313 DL- регистр 39 DOSSEG- директива 621 DQ-директива 99, 105 DS-регистр 36 DS - сегмент данных 34 DT-директива 99 DTA (адрес обмена с диском) 458 DW-директива 99, 104 DWORD. См. DD-директива DX-регистр 39 d-бит 640 Е ЕАХ- регистр 38 ЕВР - регистр 38 ЕВХ-регистр 39 ЕСХ-регистр 39 EDI-регистр 40 EDX-регистр 39 Е1Р-регистр 37 END - директива 90,622 ENDP-директива 89,622 ENDS - директива 622 ENTER - инструкция 137, 543, 654 Enter, клавиша -ввод 201 - удаление символов 201 EQU - директива 107,622 ES- регистр 37 ESI-регистр 40 ESP-регистр 38 EVEN - директива 624 EXE2BIN, программа 701 EXITM - директива 519 EXTERN-атрибут 529 EXTRN/EXTERN - директивы 625 F FAT - таблица размещения файлов 403 - вторая запись 404 - записи указателей 405 - обработка записей FAT12 406 -обработказаписей FAT 16 407 - первая запись , 404 - просмотр с помощью DEBUG 408 Flags - регистр флагов 40, 160 FS - регистр 37 FWORD. См. DF-директива G GROUP-директива 626 GS- регистр 37 н HIGH-оператор 613 HIGHWORD-оператор 613 HLT- инструкция 138, 654 / IDIV - инструкция 134,313,655 IF - флаг прерываний 40, 161 IMUL- инструкция 134, 305,655 IN - инструкция 134, 605,656 INC- инструкция 134, 141,657 INCLUDE-директива 513,626 INS - инструкция 606, 657 INSB - инструкция 606,657 INSD - инструкция 606,657 INSn-набор инструкций 134 INSW - инструкция 606,657 INT-инструкция 73, 136, 143,657 INT ООН - деление на ноль 591 INT 01Н - пошаговый режим 591 INT 02H - немаскируемое прерывание 591 INT03H-точка останова 591 INT 04H - переполнение 591 INT 05H - печать экрана 591 INT 08H - системный таймер 592 712 Ассемблер. Язык и программирование для IBM PC
INT 09H - прерывание клавиатуры 592 INT OBH - управление последовательными устройствами 592 INT 0DH - управление параллельными устройствами 592 INT 0EH - управление дискетами 592 INT 0FH - управление параллельными устройствами 592 INT ОСЫ - управление последовательными устройствами 592 INT 1 ОН - вывод на экран 592 - функция ООН - установка видеорежима... 218 - функция 01Н - установка размера курсора 218 - функция 02Н - установка курсора 193 - функция 02Н - установка положения курсора 219 - функция 03Н - получение состояния курсора 219 - функция 05Н - установка видеорежима...220 - функция 06Н - очистка экрана 193 - функция 06Н - прокрутка экрана вверх.... 220 - функция 07Н - прокрутка экрана вниз 222 - функция 08Н - чтение символа с атрибутами из позиции курсора 222 - функция 09Н - вывод символа в позиции курсора 223 - функция 09Н - вывод символа с атрибутами в позиции курсора 222 - функция 0BH - нарисовать пиксел 224 - функция 0BH - установка цветовой палитры 224 - функция 0DH - прочитать пиксел 225 - функция 0EH - вывод в режиме телетайпа 225 - функция 0FH - получить текущий видеорежим 225 - функция 1 ОН - работа с регистрами палитры 229 - функция 1 ОН, подфункция ООН - обновить регистр палитры 229 - функция ЮН, подфункция 01Н - обновить цвет рамки 229 - функция ЮН, подфункция 03Н - установить интенсивность фона 229 - функция 1 ОН, подфункция 07Н - прочитать регистр палитры 229 - функция 1 ОН, подфункция 08Н - прочитать регистр рамки 230 - функция 1 ОН, подфункция 09Н - обновить значения регистров цветов ЦАП 230 - функция 1 ОН, подфункция 09Н - прочитать таблицу регистров палитры 230 -функция ЮН, подфункция 12Н- обновить блок регистров ЦАП 231 -функция ЮН, подфункция 12Н- перевести регистры ЦАП в режим шкалы серого 232 -функция ЮН, подфункция 15Н- прочитать значение регистра цвета ЦАП 231 -функция ЮН, подфункция 17Н- обновить значения регистров цветов ЦАП 231 - функция ПН- выбор таблицы определения символов 233 - функция ПН- работа со знакогенератором 232 - функция ПН- чтение данных из знакогенератора 233 -функция 12Н-управление видеоадаптером 234 - функция 1ЗН - вывод строки символов ....235 - функция 1ВН - получение информации о видео-BIOS 239 INT ПН- определение оборудования 74,592 INT 12H — определение размера памяти 592 INT 1 ЗН - работа с дисковыми накопителями 592 - функция ООН - сброс состояния накопителя 474 - функция 01Н - чтение состояния накопителя 474 - функция 02Н - чтение сектора накопителя 474 - функция 04Н - проверка секторов накопителя 479 - функция 05Н - форматирование дорожек накопителя 480 - функция 08 Н - чтение параметров накопителя 481 - функция 09Н - инициализация накопителя 481 - функция 0AH - прочитать расширенный буфер сектора 482 - функция 0BH - записать расширенный буфер сектора 482 - функция 0DH - альтернативный сброс состояния накопителя 483 - функция 0EH - прочитать буфер сектора 483 - функция 0FH - записать буфер сектора 483 Предметный указатель 713
- функция ОСН - перейти к цилиндру 482 - функция 15Н - проверить тип накопителя 483 - функция 16Н - изменение состояния дискеты 484 - функция 17Н - установка типа дискеты... 484 - функция 18 Н - установка типа носителя перед форматированием 485 - функция 19Н - парковка головок накопителя 485 INT 14H - последовательный ввод/вывод 593 INT 15Н-системные функции 594 INT 16Н-ввод с клавиатуры 594 - функция 03Н - установить параметры автоповтора 258 - функция 05Н - запись в буфер клавиатуры 259 - функция ЮН - ввод символов с клавиатуры 75 - функция 1 ОН - прочитать символ с клавиатуры 259 - функция 11Н - определить, есть ли символ в буфере 260 - функция 12Н - проверка состояния клавиатуры 260 INT 17Н-вывод на принтер 595 - функция ООН - напечатать символ 500 - функция 01Н - инициализировать принтерный порт 500 - функция 02Н - определить состояние принтерного порта 500 INT 18Н - вызов ROM BASIC 595 INT 19Н- стартовый загрузчик 595 INT 1 АН - прочитать/установить время 595 INT 1ВН - получить управление при прерывании с клавиатуры 595 INT21H-прерывание DOS 595 - функция 01Н - ввод с клавиатуры с отображением на экране 257 - функция 02Н - вывод символов на экран 203 - функция 03 Н - запись секторов накопителя 476 - функция 07Н - прямой ввод с клавиатуры без отображения на экране 257 - функция 08Н - ввод с клавиатуры без отображения на экране 258 - функция 09Н - вывести строку на экран 75 - функция 09Н - вывод строк на экран 194 - функция 0АН - ввод с клавиатуры 197 - функция 0 АН - ввод с клавиатуры с использованием буфера 258 - функция 0BH - проверка состояния клавиатуры 258 - функция ОСН - очистить буфер и вызвать функцию 258 - функция 0DH - сброс состояния дискового накопителя 441 - функция 0EH - выбор дискового накопителя по умолчанию 442 - функция 19Н - определение дискового накопителя по умолчанию 442 - функция 1 АН - установить адрес для обмена с диском 458 - функция 1 АН - установить/прочитать атрибуты файла 461 - функция 1FH - получить DPB дискового накопителя 442 - функция 25 Н - установить вектор прерывания 577 - функция 29Н - преобразовать имя файла 459 - функция 2АН - получить текущие дату и время 73 - функция 2ЕН - установить/сбросить проверку диска при записи 443 - функция 32Н - получить DPB дискового накопителя 444 - функция 35Н - получить вектор прерывания 577 - функция 36Н - получить объем свободного пространства на дисковом накопителе 444 - функция 39Н- создать поддиректорию....455 - функция ЗАН - удалить поддиректорию..456 - функция ЗВН - сменить текущую поддиректорию 456 -функция ЗСН- создание файла 416 - функция 3DH - открытие файла 422 - функция ЗЕН - закрытие файла 418 - функция 3FH - ввод с клавиатуры ....206 - функция 3FH - прочитать запись из файла 423 - функция 40Н - вывод на экран 205 - функция 40Н - запись в файл 417 - функция 40Н - печать символов 488 - функция 41Н - удалить файл 460 - функция 42Н - переместить указатель файла 427 - функция 44Н - управление вводом/выводом 445 - функция 44Н, подфункция ООН - получить информацию об устройстве....445 714 Ассемблер. Язык и программирование для IBM PC
- функция 44Н, подфункция 01Н - установить информацию об устройстве ..445 - функция 44Н, подфункция 04Н - прочитать управляющие данные с накопителя 446 - функция 44Н, подфункция 05Н - записать управляющие данные на накопитель 446 - функция 44Н, подфункция 06Н - проверить состояние ввода 446 - функция 44Н, подфункция 06Н - проверить состояние вывода 446 - функция 44Н, подфункция 08Н - проверить, сменный ли носитель в устройстве 446 - функция 44 Н, подфункция 0DH, код 41Н - записать сектор диска 447 - функция 44Н, подфункция 0DH, код 41Н - отформатировать дорожку диска 447 - функция 44Н, подфункция 0DH, код 46Н - установить идентификатор носителя 448 - функция 44Н, подфункция 0DH, код 60Н - получить параметры устройства 449 - функция 44Н, подфункция 0DH, код 61Н- прочитать сектор диска 449 - функция 44Н, подфункция 0DH, код 66Н - получить идентификатор носителя 450 - функция 44Н, подфункция 0DH, код 68Н - распознать тип носителя 450 - функция 45Н - создать дубликат дескриптора файла 462 - функция 46Н - принудительно создать дубликат дескриптора файла 462 - функция 47Н - получить текущую директорию 456 - функция 48Н - выделить память 565 - функция 49Н - освободить выделенную память 565 - функция 4АН - изменить выделенный блок памяти 566 - функция 4ВН - загрузить и выполнить подпрограмму 566 - функция 4СН - завершение выполнения программы 93 - функция 4DH - получить код возврата подпрограммы 570 - функция 4ЕН - найти первый подходящий файл 463 - функция 4FH - найти следующий подходящий файл.. 464 - функция 54Н - получить состояние флага проверки записи 451 - функция 56Н - переименовать файл или директорию 464 - функция 57Н - получить/изменить дату и время файла 465 - функция 5801Н - установить стратегию выделения памяти 558 - функция 5802Н - связь с верхней памятью 558 - функция 5803Н - установить связь с верхней памятью 558 - функция 58Н - выделение памяти 557 - функция 59Н - получить дополнительную информацию об ошибках 451 - функция 5 АН - создать временный файл 465 - функция 5ВН - создать новый файл 466 INT33H - функция ООН - инициализация мыши 378 - функция 01Н - отображение указателя мыши 379 - функция 02Н - сокрытие указателя мыши 379 -- функция 03Н - получение состояния кнопок и текущей позиции указателя мыши 379 - функция 04Н - установка указателя мыши в заданную позицию 380 - функция 05Н - получение информации о нажатии кнопок мыши 384 - функция 06Н - получение информации об отпускании кнопок мыши 384 - функция 07Н - установить пределы перемещения указателя мыши по горизонтали 385 - функция 08Н - установить пределы перемещения указателя мыши по вертикали 385 - функция 0BH - прочитать содержимое счетчиков перемещения мыши 386 - функция 0CH - установить обработчик прерывания для событий мыши 386 - функция 1 ОН - установить область исключения указателя мыши 387 - функция 1ЗН - установить пороговую скорость мыши 387 - функция 1 АН - установить значение чувствительности мыши 387 - функция 1ВН - получить значение чувствительности мыши 388 - функция 1DH - выбор страницы для отображения указателя мыши ~ 388 Предметный указатель 715
- функция 1 EH - получение номера страницы отображения указателя мыши 388 - функция 24Н - получение информации о мыши 388 INTO - инструкция 135,658 IOCTL 445 IP - регистр 37 IRET- инструкция 136, 658 IRETD - инструкция 658 IRP-директива 516 IRPC - директива 516 J J A - инструкция 135, 659 JAE - инструкция 135,659 JB - инструкция 135 JBE- инструкция 135,659 JC - инструкция 135,660 JCXZ - инструкция 135, 660 JE - инструкция 136, 659 JECXZ - инструкция 660 JG - инструкция 136,659 JGE - инструкция 136, 659 JL- инструкция 136, 659 JLE-инструкция 136,659 JMP - инструкция 136, 156,661 JN А - инструкция 136, 659 JNAE - инструкция 136, 659 JNВ - инструкция 136 JNBE-инструкция 136,659 JNC - инструкция 135, 660 JNE - инструкция 135,659 JNG - инструкция 135,659 JNGE- инструкция 135, 659 JNL - инструкция , 135,659 JNLE - инструкция 135,659 JNO - инструкция 135,660 JNP - инструкция 136, 660 JNS - инструкция А 136 JNZ - инструкция 136, 660 JO - инструкция 136, 660 JP - инструкция 136,660 JPE - инструкция 136, 660 JPO - инструкция 136,660 JS - инструкция 136, 660 JZ - инструкция 136, 660 L LABEL-директива 627 LAHF- инструкция 135,661 LDS - инструкция 135,661 LEA - инструкция 135, 140, 662 LEAVE - инструкция 137, 543, 662 LENGTH-оператор 350,614 LES - инструкция 135, 661 LFS - инструкция 661 LGS - инструкция 661 LOCAL - директива 512 LOCK-инструкция 138,662 LODS - инструкция 135, 137,663 LODSB - инструкция 281,663 LODSD-инструкция 281,663 LODSW - инструкция 281,663 LOOP - инструкция 158,663 LOOP - инструкция 137 LOOPD - инструкция 663 LOOPE-инструкция 137,664 LOOPED - инструкция 664 LOOPEW - инструкция 664 LOOPNE-инструкция 138,664 LOOPNEW - инструкция 137, 664 LOOPNZ - инструкция 138,664 LOOPNZW - инструкция 137, 664 LOOPW - инструкция 663 LOOPZ- инструкция 138, 664 LOOPZD - инструкция 664 LOOPZW - инструкция 664 LOW-оператор 615 LOWWORD-оператор 615 LSS - инструкция 135, 661 м MACRO - директива 505 MASK - оператор 633 MASM 5.1, программа - компоновка программ 701 -описание 700 - опции командной строки 700 - создание программ .СОМ 701 - файл перекрестных ссылок 700 716 Ассемблер. Язык и программирование для IBM PC
MASM 6.1, программа - компоновка программ 699 -описание 698 - опции командной строки 698 - создание программ .СОМ 699 -файл перекрестных ссылок 699 mod-биты 641 MOV - инструкция 135, 138,665 MOVS - инструкция 135, 137, 665 MOVSB - инструкция 279, 665 MOVSD - инструкция 279, 665 MOVSn - набор инструкций 279 MOVSW - инструкция 279, 665 MOVSX-инструкция 135,139,666 MOVZX-инструкция 135,139,666 MUL- инструкция 134, 305, 666 MUL- инструкция N NEG - инструкция 134, 319, 667 NOP - инструкция 138, 667 NOT- инструкция 135, 176, 667 о OF - флаг переполнения 40,161 OFFSET-оператор 615 OR - инструкция 135, 175,667 ORG - директива 628 OUT-инструкция 134,605,668 OUTS - инструкция 606, 668 OUTSB - инструкция 606, 668 OUTSD- инструкция 606, 668 OUTSn - набор инструкций 134 OUTSW - инструкция 606,668 Р PAGE - директива 87,630 Pentium - процессор 29 Pentium II - процессор 29 Pentium III - процессор 29 Pentium IV - процессор 29 PF - флаг четности 41, 160 POP - инструкция 137, 669 POP A - инструкция 137, 669 POP AD - инструкция 669 POPF-инструкция 135, 137,670 POPFD-инструкция 670 PROC-директива 89,630 PSP (префикс сегмента программы) 552 PTR-оператор 76,615 PUBLIC-атрибут 529 PUBLIC-директива 631 PURGE - директива 514 PUSH - инструкция 137,670 PUSHA- инструкция 137,670 PUSHAD-инструкция 670 PUSHF-инструкция 135, 137,671 PUSHFD-инструкция 671 Q QWORD. См. DQ-директива R r/m-биты 642 RCL-инструкция 137, 184,671 RCR- инструкция 137, 183,671 RECORD-директива 631 - оператор MASK 633 - оператор WIDTH 632 - оператор счетчика сдвига 633 reg-биты 642 REP-префикс 137,278,672 REPE- префикс 137,278,672 REPNE-префикс 137,278,672 REPNZ - префикс 137,278, 672 REPT - директива 515 REPZ - префикс Л 137,278,672 RET - инструкция 136, 167,673 RETF - инструкция 136, 167, 673 RETN - инструкция 136, 673 ROL- инструкция 137, 184,673 ROR-инструкция 137, 183,673 S SAHF - инструкция 135,674 SAL-инструкция 137, 181,674 SAR-инструкция , 137, 179,674 SBB - инструкция 134,675 SCAS - инструкция 137,675 SCASB - инструкция 290, 675 SCASD - инструкция 290, 675 SCASW - инструкция 290, 675 Предметный указатель 717
SEG-оператор 616 SEGMENT - директива 88, 525, 633 - выравнивание (align) 88, 634 - класс ('class') 88,635 - совмещение (combine) 88, 634 SETnn - набор инструкций 676 SF-флаг знака 41,161 Shift - клавиши 271 SHL-инструкция 137, 181,676 SHL-оператор 616ч SHLD-инструкция 137, 181,677 SHORT-оператор 616 SHR-инструкция 137, 179,676 SHR-оператор 616 SHRD- инструкция 137, 179,677 SI-регистр 40 SIZE - оператор 350,617 SP-регистр 38 SS - регистр 37 SS - сегмент стека 34 Static Functionality table 240 STC - инструкция 135,677 STD - инструкция 135, 677 STI - инструкция 135, 677 STOS - инструкция 135, 678 STOSB - инструкция 282, 678 STOSD - инструкция 282, 678 STOSW- инструкция 282, 678 SUB - инструкция 134, 141,678 SUBTITLE-директива 638 SUBTTL-директива 638 т TASM, программа - компоновка программ 702 -описание 702 - опции отладки 703 -создание программ .СОМ 703 TCREF, программа 702 TEST-инструкция 137, 176,679 TEXTEQU-директива 108,638 TF - флаг пошагового режима 40, 161 THIS - оператор 617 TITLE - директива 87,638 TWORD. См. DT-директива TYPE-оператор 350,617 W WAIT - инструкция 138, 679 WIDTH - оператор 632 WORD. См. DW-директива W-бит 639 X XADD-инструкций 134,679 XCHG-инструкция 135, 140,680 XLAT- инструкция 135, 360 XLAT/XLATB - инструкции 680 XOR-инструкция 135, 176,680 Z ZF- флаг нуля 41, 161 А Абсолютный адрес 33 Автоповтор 258 Адрес -far 155 -near 155 - short 155 -выравнивание 150 -таблицы 368 -тип 155 -типа FAR 149 -типа NEAR 149 Адресация 32, 36, 51, 54, 144 - базовая со смещением 148 - базово-индексная 148 - базово-индексная со смещением •. 148 - байт режима 639, 641 -биты режима 641 - защищенный режим 54 - непосредственная 145 -непрямая 147 - прямое обращение к памяти 146 -регистровая^ 144 -режимы 144 - со смещением 146 - элементов таблицы 351 Аккумулятор (регистр АХ) 38 Альтернативное кодирование строковых инструкций 291 Альтернативный сброс состояния накопителя 483 Аппаратные прерывания 41 718 Ассемблер. Язык и программирование для IBM PC
Арифметика 296 - в ASCII-формате 326 -в таблицах » 370 -двоичная 22 -деление 313 -деление вычитанием 317 -деление сдвигами 318 - инструкции 141 - математический сопроцессор 319 - обращение знака числа 319 -операторы 612 -перенос 298 - переполнение 298,316 - преобразование байтов в слова 300 - преобразование двойных слов в счетверенные 301 - преобразование слов в двойные слова 301 - умножение сдвигами 312 Арифметические инструкции 141 Арифметический перенос 298 Ассемблер -выражения 99 - двухпроходный 118 - диагностика ошибок 124 - директивы 84, 85, 86, 618 -допустимые сим волы 85 -зарезервированные слова 84,696 - идентификаторы 84, 85 -имена 84 - инструкции 84, 85 -комментарии 83 -макрокоманды 504 -метки 84 -операнды 86 -операторы 84 -операции 86 -предложения 85 - предопределенные символы 84 - счетчик положения 126 - таблица сегментов и групп 117 -таблица символов 117 - язык, описание 83 Ассемблерная программа 82 Ассемблирование программы 113 Атрибут -EXTERN 529 -PUBLIC 529 Атрибуты символов > 215 Атрибуты файлов 461 Базовая адресация со смещением 148 Базово-индексная адресация 148 Базово-индексная адресация со смещением.... 148 Базовый регистр (ВХ) 39 Базовый указатель (ВР) 38 Базовый указатель (IP) 37 Байт 19 -атрибутов 215 -младший 21 - преобразование в слово 300 - режима адресации 639,641 -состояния 271 -дисковых операций 473 -старший 21 Байты, перемножение 305 Беззнаковые данные 163 Беззнаковые числа 297 -деление 315 -умножение 306 Безусловный переход 661 Библиотеки макрокоманд 512 Бит 19 Бит направления 640 Бит ширины 639 Битовые операции 175 Биты -сдвиг 178 - циклический сдвиг 182 Биты регистра 642 Биты режима 641 Буквы, изменение регистра 177 Булевы операции 175 Буфер клавиатуры 256,259,268 в Ввод символов с клавиатуры 75 Верхняя память 556 Видео-BIOS 214 - получение информации 239 Видеоадаптер, дополнительные функции управления 234 Видеоконтроллер 214 Видеорежим - получение текущего 225 -установка 218 Видеосистема 212 - Static Functionality table 240 - графические режимы 241 -знакогенератор 232 - непосредственный вывод на экран 247 Предметный указатель 719
- палитры 224, 229 -память 213 - получение информации 239 - режимы 214, 241 Видеостраницы, выбор активной 220 Видеоустройство, проверка состояния 61 Винчестер. См. Дисковый накопитель Вложенные макрокоманды 511 Возврат из процедуры 168 Время - получение текущего 73 - чтение/установка 595 - доступа 398 -ожидания 398 Выбор активной видеостраницы 220 Вывод на экран, управляющие символы 203 Вывод строк на дисплей 75 Выделение памяти 557, 564 Вызов процедур 167 Вызовы -внутрисегментные 527 -межсегментные 528 Выполнение программы 122 Выравнивание 88, 525 -директива ALIGN 150 -директива EVEN 150 Выравнивание (align) 634 Выражения 99 Вычитание -ASCII-чисел 329 -двоичных чисел 24,299 г Генерация случайных чисел 606 Гигабайт 21 Горизонтальная табуляция 488 Графические режимы 241 Д Данные -беззнаковые 163 -директивы определения 99, 102 -директивы присваивания 106 -знаковые 163 - инструкции перемещения 141 -константы 98 - непосредственные 62 -определение 98 - определение размера оператором PTR 76 -перемещение 141 -предопределенные 67 -присваивание 106 Дата, получение текущей 73 Двоичная арифметика 22 Двоичная система счисления 21 Двоичные числа 21 -арифметика 22 - вычитание 24, 299 -деление 313 - обращение знака 23 -отрицательные 23 - получение из ASCII-чисел 336 - преобразование в ASCII-формат 338 -сложение 23,299 -умножение 305 Двойное слово 21 -арифметика 301 - преобразование в счетверенное слово 301 Двойные слова -сдвиг 185 - умножение 306, 309 -циклический сдвиг 185 Двухбайтовые инструкции 642 Деление -ASCII-чисел 332 - беззнаковых чисел 315 -вычитанием 317 -двоичных чисел 313 - знаковых чисел 315 -остаток 314 -сдвигами 318 Дескрипторы файлов 204,414 - создание дубликатов 462 - таблица по умолчанию 553 Десятичные числа -BCD-формат 325 - преобразование в шестнадцатеричные....683 Диагностика ошибок 124 Дизассемблирование в DEBUG 72 Директива -%OUT 629 -.ALPHA 619 - .CODE 96, 620 -.CREF 621 - .DATA 96, 621 -.DATA? 621 -.DOSSEG 621 -.ERR 624 - .EXIT 97, 625 -.FARDATA 626 -.FARDATA? 626 720 Ассемблер. Язык и программирование для IBM PC
-.LALL 509 -.LIST 628 - .MODEL 96,628 -.NOCREF 638 -.NOLIST 638 См. директива .XLIST -.SALL 509 -.SEQ 636 -.STACK 96,636 - .STARTUP 97, 636 -.XALL 509 -.XCREF 638 -.XLIST 638 - = (знак равенства) 106 -ALIGN 619 - ASSUME 90, 619 -COMM 620 -COMMENT 621 -CONST 621 -DB 99,102 - DD 99, 104 -DF 99 -DOSSEG 621 - DQ 99,105 -DT 99 - DW 99, 104 -ECHO 629 -END 90,622 - ENDP 89,622 -ENDS 622 - EQU 107, 622 -EVEN 624 -EXITM 519 - EXTRN/EXTERN 625 -GROUP 626 -INCLUDE 513,626 -IRP 516 -IRPC 516 -LABEL 627 -LOCAL 512 -MACRO 505 -ORG 628 - PAGE 87, 630 - PROC 89, 630 -PUBLIC 631 -PURGE 514 -RECORD 631 -REPT 515 - SEGMENT 88, 525, 633 - STRUC/STRUCT 636 - SUBTTIVSUBTITLE 638 -TEXTEQU 108,638 -TITLE 87,638 Директивы 84, 86,618 -рбнаружения ошибок 624 - определения данных 102 - определения сегментов 91 -повторения 515 -приравнивания 622 -присваивания 106 - процессора 90,630 -сегментные 628,633,636 - сегментные упрощенные 95 -условные 516 Директории - переименование 464 - получение текущей i 456 -смена текущей 456 -создание 455 -удаление 456 Директории (дисковый накопитель) 402 Дисковый накопитель - DPB (блок параметров) 442 -DTA 458 -FAT 403 -IOCTL 445 - альтернативный сброс состояния 483 - байт состояния в BIOS 473 - время доступа 398 - время ожидания 398 -выбор 442 -директории 455 -дорожки 395 -емкость 398 - загрузочная запись 401 - закрытие файлов 418 - запись в файлы 417 - запись секторов 476 -кластеры 398 - коды ошибок при работе 414 - контроллер 396,397 - область данных 401 -поиск файлов 463 - проверка при записи 443 - проверка типа 483 - произвольный доступ к файлам 426 - сброс состояния 441,474 -свободное пространство 444 - сектора 395, 397 - системная область 399 - создание директорий 455 - создание файлов 416,466 - структура директорий 402 - удаление директорий 456 - файловые системы 398 -файлы 410 - характеристики 395 Предметный указатель 46 — 5-741 721
-цилиндры 396 - чтение записей из файлов 423 - чтение секторов 474 Дисплей 192 Дополнительные клавиши 261 Дополнительные функции управления видеоадаптером 234 Дорожки (дисковый накопитель) 395 Драйвер мыши 376 - флаг указателя 379 з Завершение выполнения программы 93 Заголовок (программы .ЕХЕ) 559 Загрузка компьютера 583 Загрузка подпрограмм 566 Загрузка программ 47, 113 Загрузочная запись (дисковый накопитель) 401 Загрузчик программ 47, 559 Запись в файлы 417 Зарезервированные слова 84, 696 Защищенный режим 54 - инициализация сегментов 98 Звук, генерация , 607 Знак арифметический 297,346 Знаковые данные 163 Знаковые числа 297 -деление 315 - обращение знака 319 -умножение 307 Знакогенератор 232 и Идентификаторы 84, 85 Изменение регистра букв 177 Имена процедур , 89 Индексные регистры 40 Инициализация мыши 378 Инициализация сегментов в защищенном режиме 98 Инструкции 84, 85, 134 -CMPSn 651 -$ETnn 676 - алгебраического сдвига 674 -арифметические 141 - блочного перемещения 141 - ввод в DEBUG..' 63 - возврата из процедуры 673 - выполнение в DEBUG 63 -двухбайтовые 642 -для работы со стеком 50 - загрузки сегментных регистров 661 - логического сдвига 676 - набор, описание 644 - обращение к регистрам 639 -операнды 53 -описание 644 -перехода 659 - регистр-указатель (IP) 37 -трехбайтовые 643 -условного перехода 162,659 -установки битов 676 - циклического сдвига 671,673 - циклов 663, 664 . - четырехбайтовые 643 Инструкция - AAA 136, 326,645 - AAD 136,332, 645 - ААМ 136, 330,645 - AAS 136, 326,329 - ADC 134, 303,646 -ADD 134,141,646 - AND 135, 175,647 -BSF 137,647 - BSR 137, 647 - ВТ 137, 648 - BTC 137,648 -BTR 137,648 - BTS. 137,648 - CALL 136, 167, 648 -CBW 136,300,649 -CDQ 136,301,649 - CLC 135,649 -CLD 135,649 - CLI 135, 650 -CMC 135,650 -CMP 137,162,650 -CMPS 137,651 -CMPSB 288,651 -CMPSD 288,651 -CMPSn 137 -CMPSW 2(88,651 -CMPXCHG 137,651 -CMPXCHG8B 137,652 - CWD 136, 301, 652 -CWDE 136,301,652 - DAA 136, 334, 653 -DAS 136,334,653 -DEC 134,141,653 - DIV 134, 313,654 - ENTER 137, 543,654 -HLT 138,654 722 Ассемблер. Язык и программирование для IBM PC
- IDIV 134, 313, 655 - IMUL 134, 305, 655 - IN 134, 605, 656 -INC 134,141,657 - INS 606, 657 - INSB 606, 657 - INSD 606, 657 -INSn 134 - INSW 606, 657 -INT 73, 136, 143,657 - INTO 135,658 - IRET 136,658 -IRETD 658 -JA 135,659 -JAE 135,659 -JB 135 - JBE 135, 659 - JC 135,660 - JCXZ 135, 660 - JE 136, 659 -JECXZ 660 - JG 136, 659 -JGE 136,659 - JL 136, 659 -JLE 136,659 - JMP 136, 156, 661 - JNA 136, 659 -JNAE 136,659 -JNB 136 - JNBE 136, 659 - JNC 135,660 -JNE 135,659 - JNG 135, 659 -JNGE 135,659 -JNL 135,659 - JNLE 135, 659 - JNO 135, 660 - JNP 136, 660 - JNS 136 -JNZ 136,660 - JO 136, 660 - JP 136, 660 -JPE 136,660 - JPO 136, 660 - JS 136, 660 - JZ 136, 660 - LAHF 135, 661 - LDS 135, 661 - LEA 135, 140,662 - LEAVE 137,543, 662 -LES 135,661 -LFS 661 -LGS 661 - LOCK 138,662 - LODS 135, 137,663 - LODSB 281, 663 - LODSD 281, 663 - LODSW 281, 663 -LOOP 137,158,663 -LOOPD 663 - LOOPE 137,664 -LOOPED 664 -LOOPEW 664 - LOOPNE 138, 664 - LOOPNEW 137,664 - LOOPNZ 138, 664 - LOOPNZW 137, 664 -LOOPW 663 - LOOPZ 138,664 -LOOPZD 664 -LOOPZW 664 -LSS 135, 661 - MOV 135, 138,665 -MOVS 135,137,665 - MOVSB 279, 665 - MOVSD 279, 665 - MOVSW 279, 665 -MOVSX 135,139,666 -MOVZX 135,139,666 -MUL 134,305,666 -NEG 134, 319,667 -NOP 138,667 -NOT 135, 176, 667 -OR 135,175,667 -OUT 134,605,668 - OUTS 606, 668 - OUTSB 606,668 - OUTSD 606,668 -OUTSn 134 - OUTSW 606, 668 -POP 137,669 -POPA 137,669 -POP AD 669 - POPF 135,137, 670 -POPFD 670 - PUSH 137,670 -PUSHA 137,670 -PUSHAD 670 - PUSHF. 135, 137,671 -PUSHFD 671 - RCL 137, 184, 671 -RCR 137,183,671 - RET 136, 167, 673 - RETF 136, 167, 673 - RETN 136, 673 r ROL 137, 184, 673 -ROR 137,183,673 - SAHF * 135, 674 Предметный указатель 46* 723
-SAL 137,181,674 -SAR 137,179,674 - SBB 134, 675 - SCAS 137, 675 , - SCASB 290, 675 - SCASD 290, 675 - SCASW 290, 675 -SETnn ... 676 - SHL 137, 181, 676 -SHLD 137,181,677 - SHR 137, 179, 676 - SHRD 137, 179, 677 - STC 135, 677 - STD 135, 677 - STI 135, 677 -STOS 135,678 - STOSB 282, 678 - STOSD 282, 678 - STOSW 282, 678 - SUB 134, 141, 678 -TEST 137,176,679 -WAIT 138,679 -XADD 134,679 - XCHG 135, 140, 680 -XLAT 135,360 -XLAT/XLATB 680 -XOR 135,176, 680 -безусловного перехода 661 Исполняемый файл, получение 698 Исходный текст - ассемблирование 113 -комментарии 83 -трансляция 113 к Кадр стека 49 Карта связей 121 Килобайт 21 Клавиатура 255 -автоповтор 258 -байты состояния 271 - буфер 256, 259,268 - ввод символов 75, 197,257 - области данных в BIOS 255 - проверка состояния 256, 258, 260 - проверка состояния регистра 61 Клавиши 255 - Shift, Alt и Ctrl 271 -дополнительные 261 -скан-коды .-.705 -функциональные „ 261 - букв, скан-коды 705 - цифровой клавиатуры, скан-коды 706 Класс ('class') 88, 526, 635 Кластеры (дисковый накопитель) 398 Кнопки мыши 379, 384 Коды ошибок (при работе с дисковыми накопителями) 414 Комментарии, в макрокомандах 509 Комментарии в программах 83 Компилятор , 83 Компоновка 120, 545 Компоновка программ 113 - С помощью MASM 6.1 699, 701 - с помощью TASM 702 Компоновщик 83 Компьютер - внутренняя память 31 - загрузка 46, 583 -материнская плата 27 -оперативная память (ОЗУ) 31 - системная плата 27 -шина 27 Константы .. 99, 101 Контроллер (дисковый накопитель) 396, 397 Контроль четности 20 Курсор - получение состояния 219 -установка 193 - установка положения 219 - установка размера 218 л Листинг 123 Логические диски 398 Логические операторы 614 Логические операции 175 м Макрокоманды 504 - вложенные 511 -директивы повторения 515 -комментарии 509 -определение 505 -параметры 507 - подключение из библиотек 512 - условные директивы 516 Макросы. См. Макрокоманды Массивы. См. Таблицы Математический сопроцессор 319 Материнская плата 27 724 Ассемблер. Язык и программирование для IBM PC
Машинные инструкции 63 Мегабайт 21 Мерцание, включение/отключение 229 Микки, счетчик 376 Младший байт 21 Модели памяти 96,545 Монитор 192,212 Мышь ,. 376 -драйвер 376 -инициализация 378 - кнопки 379, 384 -курсор 376 - получение информации о 388 - пороговая скорость 376, 387 - пределы перемещения указателя 385 - счетчики перемещения 386 - указатель 376,379 - флаг указателя (в драйвере) 379 -чувствительность 387 н Набор инструкций 644 - INSn 606, 657 -LODSn 663 - LOOPn 663, 664 -MOVSn 665 - OUTSn 606, 668 -SCASn 675 -SETnn 676 -STOSn 678 Набор символов ASCII 685 Непосредственная адресация 145 Непосредственные данные 62 Непосредственный вывод на экран 247 Непрямая адресация 147 Неупакованные BCD-числа 325 Область видеоадаптера 192 Область данных (дисковый накопитель) 401 Область данных BIOS г 46, 584 Оборудование, определение установленного.... 74 Обработчики прерываний 589 Обращение знака 23 Обращение к регистрам в инструкциях 639 Оверлеи 570 ОЗУ 31,32 - абсолютный адрес 33 - адресация 32,36,51,54, 144 - адресация со смещением 146 -базовая адресация со смещением 148 - базово-индексная адресация 148 - базово-индексная адресация со смещением 148 - непосредственная адресация 145 - непрямая адресация 147 -просмотр 59 - просмотр содержимого 66 - прямое обращение 146 - регистровая адресация 144 -режимы адресации 144 -сегменты 33 Округление 339 Операнды 86 Операнды инструкций 53 Оперативная память 31, 32 Оператор -HIGH 613 -HIGHWORD 613 - LENGTH 350, 614 -LOW 615 -LOWWORD 615 -MASK 633 -OFFSET 615 - PTR 76,615 -SEG 616 -SHL 616 -SHORT 616 -SHR 616 - SIZE 350, 617 -THIS 617 -TYPE 350,617 -WIDTH 632 - переопределения сегмента 616 - счетчика сдвига 633 Операторы 84 -арифметические 612 -индексов 613 -логические 614 Операции 85, 86 Операционная система 45 -загрузка 46 - загрузка программ 47 -задачи 45 - область данных BIOS 46 -таблица векторов прерываний 46 Определение данных, директивы 99, 102 Определение таблиц 349 Определение установленного оборудования...74 Организация программ 185 Освобождение памяти 564 Предметный указатель 725
Остаток при делении 314 Открытие файлов 422 Относительные сектора (дисковый накопитель) 397 Отрицательные числа 23,346 Очистка экрана 193 Ошибки, диагностика 124 п Палитра - работа с регистрами 229 -установка 224 - чтение регистров 229,230 Память - абсолютный адрес 33 - адресация 32, 36, 51, 54, 144 - адресация со смещением 146 - базовая адресация со смещением 148 - базово-индексная адресация 148 - базово-индексная адресация со смещением 148 -буфер клавиатуры 256,259,268 -верхняя 556 -видеобуфер 213 - выделение 557, 564 - модели 96, 545 - непосредственная адресация 145 - непрямая адресация 147 -освобождение 564 -просмотр 59 - просмотр содержимого 66 - прямое обращение 146 - регистровая адресация 144 - режимы адресации 144 -сегменты 33 - структура адресов 31 Параграф 21 Параметры - в подпрограммах 539 -макрокоманд 507 -передача 546 - передача по значению 172,539 - передача по ссылке 173,543 Передача параметров 172 - по значению 172 - по ссылке 173 Переименование файлов и директорий 464 Перемещение данных, инструкции 141 Перемещение строк 279 Перенос, в арифметике 298 Переполнение, в арифметике 298 Переполнение стека 172 Переполнения при делении 316 Перехват прерываний 577 Переход -безусловный 661 -таблица адресов 368 -условный 162 Переходы 156 -near 156 -short 156 -вперед 156 -назад 156 Печать 487 - возврат каретки 488 - вывод страницы 488 - инициализация принтерного порта 500 - перевод строки 488 - с помощью BIOS (INT 17H) 499 - управляющие символы 487,498 ПЗУ 32 Пиксель , 241,376 -рисование 224 -чтение 225 -цвет 245 Подпрограммы 524 -загрузка 566 - передача параметров 539 Поиск в таблицах 355, 358 Поиск файлов 463 Получение исполняемого файла 698 Получение текущего видеорежима 225 Получение текущих даты и времени 73 Порты 60,603 Постоянное запоминающее устройство (ПЗУ) 32 Предложения 85 Предопределенные имена -@code 118 -@data 118 -@filename 118 Предопределенные символы 84 Преобразование байтов в слова 300 Преобразование двойных слов в счетверенные 301 Преобразование слов в двойные слова 301 Преобразование чисел между системами счисления 682 Прерывание, инструкция INT 143 Прерывание INT ООН -деление на ноль 591 726 Ассемблер. Язык и программирование для IBM PC
Прерывание INT 01Н - пошаговый режим....591 Прерывание INT 02Н - немаскируемое прерывание 591 Прерывание INT03H-точка останова 591 Прерывание INT 04H - переполнение 591 Прерывание INT 05H - печать экрана 591 Прерывание INT 08H - системный таймер....592 Прерывание INT 09H - прерывание клавиатуры 592 Прерывание INT 0BH - управление последовательными устройствами 592 Прерывание INT 0DH - управление параллельными устройствами 592 Прерывание INT 0EH - управление дискетами 592 Прерывание INT 0FH - управление параллельными устройствами 592 Прерывание INT 0CH - управление последовательными устройствами 592 Прерывание INT 10H - вывод на экран ..218, 592 - функция ООН - установка видеорежима... 218 - функция 01Н - установка размера курсора 218 - функция 02Н - установка курсора 193 - функция 02Н - установка положения курсора 219 - функция 03Н - получение состояния курсора 219 - функция 05Н - установка видеорежима...220 - функция 06Н - очистка экрана 193 - функция 06Н - прокрутка экрана вверх....220 - функция 07Н - прокрутка экрана вниз ....222 - функция 08Н - чтение символа с атрибутами из позиции курсора 222 - функция 09Н - вывод символа в позиции курсора 223 - функция 09Н - вывод символа с атрибутами в позиции курсора 222 - функция 0BH - установка цветовой палитры 224 - функция 0CH - нарисовать пиксель 224 - функция 0DH - прочитать пиксель 225 - функция 0EH - вывод в режиме телетайпа 225 - функция 0FH - получить текущий видеорежим 225 - функция 1 ОН - работа с регистрами палитры 229 - функция 1 ОН, подфункция ООН - обновить регистр палитры 229 - функция 1 ОН, подфункция 01Н - обновить цвет рамки 229 - функция ЮН, подфункция 03 Н - установить интенсивность фона 229 - функция 1 ОН, подфункция 07 Н - прочитать регистр палитры 229 - функция ЮН, подфункция 08Н - прочитать регистр рамки 230 - функция 1 ОН, подфункция 09Н - обновить значения регистров цветов ЦАП 230 - функция 1 ОН, подфункция 09Н - прочитать таблицу регистров палитры 230 -функция ЮН, подфункция 12Н- обновить блок регистров ЦАП 231 - функция ЮН, подфункция 12Н - перевести регистры ЦАП в режим шкалы серого 232 -функция ЮН, подфункция 15Н- прочитать значение регистра цвета ЦАП 231 -функция ЮН, подфункция 17Н- обновить значения регистров цветов ЦАП 231 - функция ПН- работа со знакогенератором 232 - функция 11Н, подфункция 03 Н - выбор таблицы определения символов....233 - функция 11Н, подфункция ЗОН - чтение данных из знакогенератора 233 -функция 12Н-управление видеоадаптером , 234 - функция 13Н - вывод строки символов ....235 - функция 1ВН - получение информации о видео-BIOS 239 Прерывание INT 11H- определение оборудования 74, 592 Прерывание INT 12H - определение размера памяти 592 Прерывание INT 13H - работа с дисковыми Накопителями 592 - функция ООН - сброс состояния накопителя 474 - функция 01Н - чтение состояния накопителя ._. 474 - функция 02Н - чтение сектора накопителя 474 - функция 03Н - запись секторов накопителя 476 - функция 04Н - проверка секторов накопителя 479 - функция 05 Н - форматирование дорожек накопителя 480 - функция 08Н - чтение параметров накопителя 481 Предметный указатель 727
- функция 09Н - инициализация накопителя 481 - функция ОАН - прочитать расширенный буфер сектора 482 - функция ОВН - записать расширенный буфер сектора 482 - функция 0DH - альтернативный сброс состояния накопителя 483 - функция ОЕН - прочитать буфер сектора 483 - функция 0FH - записать буфер сектора....483 - функция ОСН - перейти к цилиндру 482 - функция 15Н - проверить тип накопителя ; 483 - функция 16Н - изменение состояния дискеты 484 - функция 17Н - установка типа дискеты ...484 - функция 18Н - установка типа носителя перед форматированием 485 - функция 19Н - парковка головок накопителя 485 Прерывание INT 14H - последовательный ввод/вывод 593 - функция ООН - инициализация коммуникационного порта 593 - функция 01Н - передать символ 593 - функция 02Н - принять символ 593 - функция 03Н - вернуть состояние коммуникационного порта 593 Прерывание INT 15H - системные функции...594 Прерывание INT 16H - ввод с клавиатуры....594 - функция 03 Н - установить параметры автоповтора 258 - функция 05Н - запись в буфер клавиатуры 259 - функция ЮН - ввод символов с клавиатуры 75 - функция ЮН - прочитать символ с клавиатуры 259 - функция 11Н - определить, есть ли символ в буфере 260 - функция 12Н - проверка состояния клавиатуры 260 Прерывание INT 17H - вывод на принтер 595 - функция ООН - напечатать символ 500 - функция 01Н - проинициализировать принтерный порт 500 - функция 02Н - определить состояние принтерного порта 500 Прерывание INT 18Н - вызов ROM BASIC..595 Прерывание INT 19H - стартовый загрузчик..595 Прерывание INT 1 АН - прочитать/установить время 595 Прерывание INT 1ВН - получить управление при прерывании с клавиатуры 595 Прерывание INT 21Н - прерывание DOS 595 - функция 01Н - ввод с клавиатуры с отображением на экране 257 - функция 02Н - вывод символов на экран 203 - функция 07Н - прямой ввод с клавиатуры без отображения на экране 257 - функция 08Н - ввод с клавиатуры без отображения на экране 258 - функция 09Н - вывести строку на экран...75 - функция 09Н - вывод строк на экран 194 - функция ОАН - ввод с клавиатуры 197 - функция ОАН - ввод с клавиатуры с использованием буфера 258 - функция ОВН - проверка состояния клавиатуры 258 - функция ОСН - очистить буфер и вызвать функцию 258 - функция 0DH - сброс состояния дискового накопителя 441 - функция ОЕН - выбор дискового накопителя по умолчанию 442 - функция 19Н - определение дискового накопителя по умолчанию 442 - функция 1 АН - установить адрес для обмена с диском 458 - функция 1FH - получить DPB дискового накопителя 442 - функция 25Н - установить вектор прерывания 577 - функция 29Н - преобразовать имя файла 459 - функция 2АН - получить текущие дату и время 73 - функция 2ЕН - установить/сбросить проверку диска при записи 443 - функция 32Н - получить DPB дискового накопителя 444 - функция 35Н - получить вектор прерывания 577 - функция 36Н - получить объем свободного пространства на дисковом накопителе ♦. 444 - функция 39Н - создать поддиректорию....455 - функция ЗАН - удалить поддиректорию..456 - функция ЗВН - сменить текущую директорию 456 -функция ЗСН - создание файла 416 - функция 3DH - открытие файла 422 - функция ЗЕН - закрытие файла 418 - функция 3FH - ввод с клавиатуры 206 728 Ассемблер. Язык и программирование для IBM PC
- функция 3FH - прочитать запись из файла 423 - функция 40Н - вывод на экран 205 - функция 40Н - запись в файл 417 - функция 40Н - печать символов 488 - функция 41Н - удалить файл 460 - функция 42Н - переместить указатель файла 427 - функция 43Н - установить/прочитать атрибуты файла 461 - функция 44Н - управление вводом/выводом 445 - функция 44Н, подфункция ООН - получить информацию об устройстве....445 - функция 44Н, подфункция 01Н - установить информацию об устройстве ..445 - функция 44Н, подфункция 04Н - прочитать управляющие данные с накопителя 446 - функция 44Н, подфункция 05 Н - записать управляющие данные на накопитель 446 - функция 44Н, подфункция 06Н - проверить состояние ввода 446 - функция 44Н, подфункция 06Н - проверить состояние вывода 446 - функция 44Н, подфункция 08Н - проверить, сменный ли носитель в устройстве 446 - функция 44Н, подфункция 0DH, код 41Н - записать сектор диска 447 - функция 44Н, подфункция 0DH, код 42Н - отформатировать дорожку диска 447 - функция 44Н, подфункция 0DH, код 46Н - установить идентификатор носителя 448 - функция 44Н, подфункция 0DH, код 60Н - получить параметры устройства 449 - функция 44Н, подфункция 0DH, код 61Н - прочитать сектор диска 449 - функция 44Н, подфункция 0DH, код 66Н - получить идентификатор носителя 450 - функция 44Н, подфункция 0DH, код 68Н - распознать тип носителя 450 - функция 45Н - создать дубликат дескриптора файла 462 - функция 46Н - принудительно создать дубликат дескриптора файла 462 - функция 47Н - получить текущую директорию 456 - функция 48Н - выделить память 565 - функция 49Н - освободить выделенную память 565 - функция 4АН - изменить выделенный блок памяти 566 - функция 4ВН - загрузить и выполнить подпрограмму 566 - функция 4СН - завершение выполнения программы 93 - функция 4DH - получить код возврата подпрограммы 570 - функция 4ЕН - найти первый подходящий файл 463 - функция 4FH - найти следующий подходящий файл 464 - функция 54Н - получить состояние флага проверки записи 451 - функция 56Н - переименовать файл или директорию 464 - функция 57Н - получить/изменить дату и время файла 465 - функция 5801Н - установить стратегию выделения памяти 558 - функция 5802Н - связь с верхней памятью 558 - функция 5803Н - установить связь с верхней памятью 558 - функция 58Н - выделение памяти 557 - функция 59Н - получить дополнительную информацию об ошибках 451 - функция 5 АН - создать временный файл 465 - функция 5ВН - создать новый файл 466 Прерывание INT 33Н - функция ООН - инициализация мыши 378 - функция 01Н - отображение указателя мыши 379 - функция 02Н - сокрытие указателя мыши 379 - функция 03Н - получение состояния кнопок и текущей позиции указателя мыши 379 - функция 04Н - установка указателя мыши в заданную позицию 380 - функция 05Н - получение информации о нажатии кнопок мыши 384 - функция 06Н - получение информации об отпускании кнопок мыши 384 - функция 07Н - установить пределы перемещения указателя мыши по горизонтали 385 - функция 08Н - установить пределы перемещения указателя мыши по вертикали 385 Предметный указатель 729
- функция ОВН - прочитать содержимое счетчиков перемещения мыши 386 - функция ОСН - установить обработчик прерывания для событий мыши 386 - функция ЮН - установить область исключения указателя мыши 387 - функция 13Н - установить пороговую скорость мыши 387 - функция 1 АН - установить чувствительность мыши 387 - функция 1ВН - получить чувствительность мыши 388 - функция 1DH - выбор страницы для отображения указателя мыши 388 - функция 1DH - получение информации о мыши 388 - функция 1ЕН - получение номера страницы для отображения указателя мыши 388 Прерывания 41 -BIOS 591 -аппаратные 41 -внешние 590 -внутренние 590 - обработка 590 -обработчики 589 -перехват 577 - таблица векторов 46, 589 Префикс - REP 137, 278, 672 -REPE 137,278,672 -REPNE 137,278,672 - REPNZ 137,278, 672 - REPZ 137,278, 672 Префикс переопределения сегмента 149 Префикс сегмента программы 552 Принтеры, управляющие символы 487 Приравнивание, директивы 622 Присваивание, директивы 106 Проверка четности 20 Программа - DTA- буфер по умолчанию 554 -PSP ...552 -ассемблерная 82 -листинг 123 - префикс сегмента 552 - файл перекрестных ссылок 123 Программы -.СОМ 126 - ассемблирование 113 - взаимодействие со стеком 170 -выполнение 122 - диагностика ошибок 124 - завершение выполнения 93 -загрузка 113, 122,568 -загрузкадля исполнения 47 - загрузка подпрограмм 566 -загрузчик 559 - карты связей 121 -комментарии 83 - компоновка 113, 699, 701, 702 -оверлеи 570 -организация 185 - подготовка к исполнению 111 -резидентные 575 -трансляция 113 Программы .СОМ, стек 48 Программы .ЕХЕ, стек 48 Проектирование программ 185 Просмотр памяти 59, 66 Просмотр строки 290 Процедуры 89 -возврат 168 -вызов 167, 168 -имена 89 -инструкции RETn 167 - инструкция CALL 167 -типа far 168 -типа near 168 Процессор 28 -80286 28 -80386 29 -80486 29 -8086 28 -8088 28 -Celeron 29 -Pentium 29 -Pentium II 29 -Pentium III 29 - Pentium IV 29 -директивы выбора 90, 630 - набор инструкций 644 -устройство исполнения 30 - шинный интерфейс 30 Прямое обращение к памяти 146 Прямой доступ к памяти 397 Псевдографика 250 Рамка вокруг экрана 229,230 Рамки и меню, символы псевдографики 250 Регистр -АН 38 730 Ассемблер. Язык и программирование для IBM PC
-AL 38 -*AX 38 -BH 39 -BL 39 -BP 38 -BX 39 -CH 39 -CL 39 -CX 39 -DH 39 -DI 40 -DL 39 -DS 36 -DX 39 -EAX 38 -EBP 38 -EBX 39 -ECX 39 -EDI 40 -EDX 39 -EIP 37 -ES 37 -ESI 40 -ESP 38 -Flags 160 -FS 37 -GS , 37 -IP ! 37 -SI 40 -SP 38 -SS 37 - аккумулятор (АХ) 38 - базовый (BX) 39 - базовый указатель (ВР) 38 - базовый указатель (IP) 37 - данных (DX) л 39 - индекса источника (SI) 40 - индекса назначения (DI) 40 -CS 36 - счетчик (СХ) 39 - указатель стека (ВР) 38 - установка значения 71 - флагов (Flags) 40, 160 Регистровая адресация 144 Регистры 35 -w-бит 639 - бит ширины 639 - биты выбора 642 -индекса 40 - машинные коды обращения 639v - обнуление инструкцией AND 175 - обнуление инструкцией XOR 176 - обращение в инструкциях 639 -общего назначения 38 -сегментные 36 - сегментные, загрузка 661 -указатели 37 Режимы адресации 144 Режимы видео , 214 Резидентные программы 575 Рисование пикселей 224 с Сброс состояния дискового накопителя.. 441,474 Сдвиг -битов 178 - в 32-разрядных регистрах 180, 182 -двойных слов 185 -округление 339 -циклический 182 Сдвиг, инструкции 671, 673, 674, 676,677 Сдвиги -для деления 318 - для умножения 312 Сегмент -границы 34 -данных 116 - кода 116, 534 -смещение в 35 -стека 114 -структура 34 Сегмент данных (DS) 34 - регистр начала (DS) 36 Сегмент кода (CS) 34 - регистр начала (CS) 36 Сегмент стека (SS) 34 Сегментные директивы 628,633,636 Сегментные регистры 36 Сегменты 33 - в защищенном режиме 98 - в программе .СОМ 127 - выравнивание 88, 525 -директива .MODEL 628 - директива SEGMENT 88, 633 -директива STACK 63(5 - инициализация адресов 93 -класс 88,526 - оператор переопределения 616 -определение 91 - префикс переопределения 149 -совмещение 88, 526 - упрощенные директивы определения 95 Сектора (дисковый накопитель) 395 -относительные 397 -физические 397 Предметный указатель 731
Символ - вывод в позиции курсора 222,223 - чтение из позиции курсора 222 Символы - ASCII, вывод на экран 195 -атрибуты 215 - ввод с клавиатуры 75, 197 - ввод с отображением на экране 257 - ввод с помощью Alt 263 - возврат каретки 488 - вывод страницы 488 - вывод строк 235 - горизонтальная табуляция 488 -знакогенератор 232 - непосредственный вывод на экран 247 - перевод строки 488 - предопределенные 84 -строки 100 - управления печатью 487,498 -управляющие 203 -фон 216 -цвет 216 -яркость 216 Символьные строки , 100 Система счисления, указатель 101 Системная область (дисковый накопитель)...399 Системная плата 27 Системы счисления 26 Скан-коды 261, 705 Слова, умножение 305 Слово, преобразование в двойное слово ....21,301 Сложение -ASCII-чисел 326 -двоичных чисел 23,299 -с переносом 301 Случайные числа 606 Смещение в сегменте 35 Совмещение (combine) 88,526, 634 Создание файлов 416 Сопроцессор 319 Сортировка элементов таблицы 364 Спикер 607 Сравнение строк 288 Сравнение чисел, инструкцией СМР 162 Старший байт 21 Стек 48, 114 - в исполняемых программах 170 - запись данных 49 - инструкции для работы 50 -кадр 49 -организация 48 - передача параметров 172 -переполнение 172 -регистр начала(SS) 37 - регистр-указатель (SP) 38 -управление 48 - чтение данных 50 Строка, вывод на дисплей 75 Строки 100 -ASCIIZ 413 - ввод с клавиатуры 197 -ввод/вывод 606 -вывод 235 -загрузка 281 -заполнение 292 -обработка 276 -перемещение 279 - повторение фрагментов 292 -поиск 290 -просмотр 290 -сохранение 282 -сравнение 288 Строковые инструкции, альтернативное кодирование 291 Счетверенное слово 21 Счетчик микки 376 Счетчик положения 126 Таблица - Static Functionality table 240 - векторов прерываний 46,589 - глобальных дескрипторов 54 -дескрипторов прерываний 54 -дескрипторов файлов по умолчанию 553 - локальных дескрипторов 54 -сегментов и групп 117, 703 -символов 117,703 Таблицы 349 - адресация элементов 351 -адресов 368 - арифметическая обработка 370 -двумерные 369 -определение 349 - поиск сравнением строк 358 - поиск элементов 355 - с диапазонами значений 357 - с уникальными элементами 355 - сортировка элементов 364 Табуляция 488 Tnn,ABS 530 732 Ассемблер. Язык и программирование для IBM PC
Транслятор 83 - двухпроходный 118 Трансляция (ассемблирование) программы.... 113 Трехбайтовые инструкции 643 у Удаление файлов 460 Указатель мыши 376 -отображение 379 - пределы перемещения 385 -сокрытие 379 -текущая позиция 379 - флаг (в драйвере) 379 Указатель системы счисления 101 Указатель стека (SP) 38 Умножение -ASCII-чисел 330 -байтов 305 - беззнаковых чисел 306 -двоичных чисел 305 -двойныхслов 306, 309 - знаковых чисел 307 -сдвигами 312 -слов 305 Упакованные BCD-числа 325 Упрощенные сегментные директивы 95, 537 Условные директивы 516 Условный переход 162 -по нескольким условиям 166 Установка курсора 193 Установка палитры 224 Устройство исполнения 30 ф Файл перекрестных ссылок 123,699,700,702 Файловые системы' 398 Файловый указатель 415 Файлы 410 -атрибуты 461 -временные 465 -дата/время 465 -дескрипторы 204,414 -закрытие 418 -запись 417 -открытие 422 -переименование 464 -поиск 463 - преобразование имен 459 - произвольный доступ 426 - создание 416,466 - создание дубликатов дескрипторов 462 -удаление 460 - чтение ASCII-последовательностей 432 - чтение записей 423 Файлы .LST 703 Физические сектора (дисковый накопитель)...397 Флаг - AF - вспомогательный перенос 41 - АР - дополнительный перенос 161 -CF-перенос 160 - DF - направление 40, 161,278 - IF - прерывание 40 - IF - прерывания 161 - OF - переполнение 40, 161 -PF- четность 41, 160 -SF-знак 41,161 - TF - пошаговый режим 40, 161 -ZF-ноль 41,161 - CF - перенос 41 Флаги 40 Фон символов 216 Форматы чисел 101 Функциональные клавиши 255 -скан-коды 706 Ц Цвет 216 -пикселей 245 Цикл -LOOP 137 -LOOPE 137 -LOOPNE 138 -LOOPNEW 137 -LOOPNZ 138 -LOOPNZW 137 -LOOPZ 138 Циклический сдвиг 182 -двойных слов 185 Циклы 158 - инструкции организации 663, 664 - инструкция LOOP 158 Цилиндры (дисковый накопитель) 396 Цифровая клавиатура, скан-коды 706 ч Четность 20 Четырехбайтовые инструкции 643 Числа -BCD-формат 325 -беззнаковые 297 Предметный указатель 733
-двоичные 21 -десятичные 325 -знаковые 297 - обращение знака 319 -округление 339 -отрицательные 346 - преобразование между системами счисления 682,683 - сравнение инструкцией СЫР 162 - умножение сдвигами 312 - форматы 101 - шестнадцатеричные 25 ш Шестнадцатеричная система счисления 25 Шестнадцатеричные числа 25 - преобразование в десятичные 682 Шина 27 Шинный интерфейс » 30 Экран 192,212 - вывод символа в позицию курсора... 222,223 - вывод символов 195 - вывод строк 194 - вывод строк символов 235 - графические режимы 241 - непосредственный вывод 247 -очистка 193 -прокрутка 220,222 -режимы 214 - символы управления выводом 203 - установка палитры 224 -фон 216 - чтение символа 222 я Языки программирования -Ассемблер 83 -высокоуровневые 82 -низкоуровневые 82 Яркость символов 216 734 Ассемблер. Язык и программирование для IBM PC
Учебное издание Питер Абель АССЕМБЛЕР Язык и программирование для IBM PC Перевод с английского под ред. С. М. Молявко Ответственный исполнитель Легейда В.В. Технический редактор Самотовка А.Л. Литературный редактор Калиниченко Н.В. Обложка Легейда В.В. Подписано в печать 19.04.05. Формат 700 х 100 1/16. Печать офсетная. Тираж 3000 экз. Усл. печ. л. 46. Гарнитура тайме. Заказ № 5-741 Товарищество с ограниченной ответственностью "ВЕК+" 252056, г. Киев, пер. Ковальский, 22 й тел. (044) 440-94-70 E-mail: vek-plus@ln.ua Отпечатано в ЗАО "Випол", ДК № 15 03151, г. Киев, ул. Волынская, 60
Издательство "ВЕК+" предлагает книги CorelDRAW Graphics Suite 12. Практическое руководство В. В. Мельниченко, А. В. Легеида В практическом руководстве CorelDraw Graphics Suite 12 основной акценг сделан на работе в CorelDraw 12. Подробно рассмотрены все этапы работы с программой и ее инструментальными средствами - от установки и настройки до поэтапного создания рисунков. Простые примеры и дружественный "интерфейс" книги сделают ваше обучение легким и увлекательным занятием. В доступной форме рассмотрена работа с пакетом графических программ, позволяющим создавать изображения, обрабатывать векторные и битовые рисунки, выполнять операции импорта и экспорта, трассировку, захват и анимацию изображений, а также многое другое. В ней не только описаны все компоненты графического пакета CorelDraw Graphics Suite 12, но и изложены основные правила дизайна и проектирования макетов. Рекомендуется широкому кругу читателей: начинающим дизайнерам, художникам и просто пользователям, переходящим к последней версии графического пакета. 528 стр. Настоящий САМОУЧИТЕЛЬ работы на ПК К В. Мельниченко, А. В, Легеида Независимо от того, делаете вы первые шаги в мире компьютеров или давно стали его полноправным гражданином, эта книга - для вас. С ее помощью начинающий пользователь освоит работу на персональном компьютере и выработает профессиональный подход к решению многих задач. Подготовленный пользователь систематизирует свои знания и ознакомится с новыми программными продуктами. "Продвинутого" пользователя заинтересуют некоторые аспекты и хитрости рассмотренного программного обеспечения. В общем, меню - на любой вкус. Вам предлагается исчерпывающий набор операционных систем, офисных приложений, файловых менеджеров, антивирусов, архиваторов, переводчиков, утилит, браузеров и почтовых приложений; кроме того, устройство компьютера, подключение периферийного оборудования, сканирование и многое другое. Рекомендуется самому широкому кругу пользователей. 3-ое издание 640 стр. QuarkXPress 6.0. Полное руководство Под ред. И. П. Охотцева Данная книга - руководство по использованию современной издательской системы QuarkXPress 6.O. В ней описаны все возможности программы и способы ее настройки. С помощью подробно и доступно описанных примеров читатель познакомится с методами макетирования и верстки, работы с большими документами и цветными иллюстрациями, подготовки цветного издания к печати. Книга рассчитана как на начинающих пользователей, так и на профессионалов, осваивающих новые возможности издательской системы, а так же будет полезна студентам, художникам, дизайнерам, специалистам по компьютерной графике. 464 стр. Delphi. Учимся правильно программировать В. В. Попов Данная книга - учебное пособие по программированию в среде Delphi. Вначале книги в сжатом виде, но достаточно полно изложены синтаксис классического языка Pascal и основы объектно-ориентированного программирования на базе синтаксиса Object Pascal. Основная и большая часть книги учит программированию в среде Delphi, причем особое внимание уделено именно "правильному стилю программирования". В книге систематизировано рассматриваются различные варианты решения практических задач для создания законченного приложения. Материал рассматривается на большом количестве практических примеров, исходные коды которых можно скачать с сайта издательства. Книга рассчитана как на начинающих программистов, студентов, изучающих программирование в Delphi, так и на профессионалов, желающих углубить свои знания по рассматриваемой теме, и тем самым усовершенствовать качество и эффективность программных решений при создании новых проектов. 352 стр. Киев, издательство "ВЕК+" Москва, "КОРОНА принт" e-mail: vek-plus@ln.ua e-mail: coronapr@online.ru Телефон в Киеве (Украина) (044): 440-94-70 Телефон в Москве (Россия): (095) 148-35-12
Классика программирования Питер Абель Пятое издание В пятой, расширенной и дополненной, редакции своей популярной книги Питер Абель последовательно, в легкой и доступной форме описывает все уровни создания программ. В ней описаны основные элементы языка и аппаратного обеспечения, рассмотрены арифметические операции, ввод/вывод, подпрограммы, оверлеи, печать, а также приведена обширная справочная информация, включающая наборы операторов, директив, инструкций и многое другое. Пятая редакция содержит большое количество обновленной информации, включая более детальное описание видеосистемы и приемов работы с ней, пересмотренные и дополненные примеры программ, дополнительную информацию о работе со стеком, режимах адресации и обработке массивов. Кроме того, в книге рассмотрены вопросы: Загрузка программы в память и резидентные программы Использование программы отладчика DEBUG Компоненты аппаратуры IBM PC Компоновка раздельно оттранслированных программ в единый исполняемый модуль Компоновка программы на языке C/C++ с программой на языке Ассемблер Логические операции и управляющие структуры языка Машинный язык и шестнадцатеричная система счисления Обработка строковых данных Трансляция, компоновка и выполнение программ Требования к написанию программ на языке Ассемблер Написание обработчика прерываний Написание программ для работы с клавиатурой, дисплеем, мышью и накопителями Печать средствами языка Ассемблер Поиск данных в таблицах и их сортировка Преобразование данных между различными форматами Создание макроинструкций и подпрограмм для упрощения создания длинных и сложных программ ISBN 966-7140-30-Х