Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 июня!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

Ассемблер для крэкера

Под редакцией Еpшова В.Г.
     Так, как регистр CS содержит 13C6, то CS:100 содержит первую  команду
A10000. Отладчик интерпретирует эту команду как MOV и определяет ссылку  к
первому адресу [0000] в сегменте данных.  Квадратные скобки необходимы для
указания ссылки к адресу памяти, а не к непосредственным данным.
     Если бы квадратных скобок не было, то команда

                               MOV AX,0000

oбнулила бы регистр AX  непосредственным значением 0000.
     Теперь введем команду T.  Команда MOV AX,[0000]  перешлет  cодержимое
слова, находящегося по нулевому смещению в сегменте данных, в регистр  AX.
Содержимое 2301 преобразуется командой в 0123 и помещается в регистр AX.
     Следующую команду ADD можно выполнить, введя еще  раз  команду  T.  В
результате содержимое слова в DS по смещению 0002 прибавится в регистр AX.
Теперь регистр AX будет содержать сумму 0123 и 0025, т.е. 0148.
     Следующая команда MOV [0004],AX выполняется опять  по  вводу  T.  Эта
команда пересылает содержимое регистра AX в слово по  смешению  0004.  Для
просмотра изменений содержимого сегмента данных введите  D  DS:00.  Первые
девять байт будут следующими:

          значение в сегменте данных: 23 01 25 00 48 01 2A 2A 2A
          величина смещения:          00 01 02 03 04 05 06 07 08

Значение 0148, которое было занесено из регистра AX в  сегмент  данных  по
смещению 04 и 05, имеет обратное  представление  4801.  Заметьте  что  эти
шест. значения представлены в правой части  экрана  их  символами  в  коде
ASCII.  Например, шест.23 генерируeтся в символ #, а шест.25 - в символ %.
Три байта с шест. значениями 2A высвечиваются в виде трех звездочек (***).
Левая  часть  дампа  показывает  действительные  машинные  коды,   которые
находятся в памяти.  Правая часть дампа только помогает проще локализовать
символьные (срочные) данные.
     Для просмотра содержимого сегмента кодов введите D  DS:100  так,  как
показано на рис.2.3. В  заключении  введите  Q  для  завершения  работы  с
программой.


     МАШИННАЯ АДРЕСАЦИЯ
     ________________________________________________________________

     Для доступа к машинной  команде  процессор  определяет  ее  адрес  из
содержимого  регистра  CS  плюс  смещение  в  регистре    IP.    Например,
предположим, что  регистр  CS  содержит  шест.04AF  (действительный  адрес
04AF0), а регистр IP содержит шест. 0023:

                          CS:      04AF0
                          IP:       0023
                                   -----
               Адрес команды:      04B13

     Если, например, по адресу 04B13 находится команда:

               A11200         MOV  AX,[0012]
               |
          Адрес 04B13

то в памяти по адресу 04B13  содержится  первый  байт  команды.  Процессор
получает доступ к этому байту и по  коду  команды  (A1)  oпределяет  длину
команды - 3 байта.
     Для доступа к данным по смещению [0012] процессор  определяет  aдрес,
исходя из содержимого регистра DS (как правило) плюс cмещение  в  операнде
команды.  Если  DS  содержит  шест.04B1  (реальный  адрес    04B10),    то
результирующий адрес данных определяется cледующим образом:

                     DS:      04B10
               Смещение:       0012
                              -----
           Адрес данных:      04B22

     Предположим, что  по  адресам  04B22  и  04B23  содержатся  следующие
данные:

               Содержимое:         24        01
                                   |         |
               Адрес:           04B22     04B23

     Процессор выбирает значение 24 из ячейки по адресу 04B22  и  помещает
его в регистр AL, и значение 01 по адресу 04B23 - в регистр AH. Регистр AX
будет содержать в  результате  0124.  В  процессе  выборки  каждого  байта
команды процессор увеличивает значение регистра IP на единицу, так  что  к
началу выполнения следующей команды в нашем  примере  IP  будет  содержать
смещение  0026.  Таким  обpазом  процессор  теперь  готов  для  выполнения
следующей команды, которую он получает по адресу из  регистра  CS  (04AF0)
плюс текущее смещение в регистре IP (0026), т.е. 04B16.


                             Четная адресация
                            ------------------
     Процессор 8086, 80286 и 80386  действуют  более  эффективно,  eсли  в
программе обеспечиваются доступ к словам, расположенным по четным адресам.
В  предыдущем примере процессор может сделать oдну выборку слова по адресу
4B22 для загрузки его непосредственно в регистр.  Но если слово начинается
на  нечетном  адресе,  процессор  выполняет двойную выборку.  Предположим,
например,  что команда должна выполнить выборку  слова,  начинающегося  по
адреcу 04B23 и загрузить его в регистр AX:

               Содержимое памяти:  |хх|24|01|хх|
                                       |
                           Адрес:  04B23

Сначала процессор получает доступ к  байтам  по  адресам  4B22  и  4B23  и
пересылает байт из ячейки 4B23 в регистр AL.  Затем он получает  доступ  к
байтам по адресам 4B24 и 4B25 и пересылает байт из ячейки 4B23  в  регистр
AH. В результате регистр AX будет содержать 0124.
     Нет необходимости в каких-либо специальных  методах  программирования
для получения четной или нечетной адресации,  не обязательно  также  знать
является  адрес  четным  или нет.  Важно знать,  что,  во-первых,  команды
обращения к памяти меняют слово  при  загрузке  его  в  регистр  так,  что
получается правильная последовательность байт и, во-вторых, если программа
имеет частый  доступ  к  памяти,  то  для  повышения  эффективности  можно
определить данные так, чтобы они начинались по четным адресам.
     Например, поскольку  начало  сегмента  должно  всегда  находиться  по
четному адресу, первое поле данных начинается также по  четному  адресу  и
пока следующие поля определены как слова, имеющие четную  длину,  они  все
будут  начинаться  на  четных  адресах.  В  большинстве  случаев,  однако,
невозможно заметить ускорения работы  при  четной  адресации  из-за  очень
высокой скорости работы процессоров.
     Ассемблер имеет директиву EVEN,  которая вызывает выравнивание данных
и команд на четные адреса памяти.


     ПРИМЕР МАШИННЫХ КОДОВ: ОПРЕДЕЛЕНИЕ РАЗМЕРА ПАМЯТИ
     ________________________________________________________________

     В первом упражнении  в  данной  главе  проводилась  проверка  размера
памяти (RAM), которую имеет компьютер. BIOS (базовая система ввода/вывода)
в  ROM  имеет  подпрограмму,  которая  определяет  pазмер  памяти.   Можно
обратиться в BIOS по команде INT, в данном cлучае  по  прерыванию  12H.  В
результате BIOS возвращает  в  регистр  AX  размер  памяти  в  килобайтах.
Загрузите в память DEBUG и введите для INT 12H и  RET  следующие  машинные
коды:

                            E CS:100 CD 12 CB

Нажмите R (и  Return)  для  отображения  содержимого  регистров  и  первой
команды. Регистр IP содержит 0100, при этом высвечивается команда INT 12H.
Теперь нажмите T  (и  Return)  несколько  раз  и  просмотрите  выполняемые
команды BIOS  (отладчик  показывает  мнемокоды,  хотя  в  действительности
выполняются машинные коды):

               STI
               PUSH  DS
               MOV   AX,0040
               MOV   DS,AX
               MOV   AX,[0013]
               POP   DS
               IRET

В этот момент  регистр  AX  содержит  размер  памяти  в  шестнадцатиpичном
формате.  Теперь введите еще раз команду T для выхода из BIOS и возврата в
вашу программу.  На экране появится команда RET  для  машинного  кода  CB,
который был введен вами.


     СПЕЦИАЛЬНЫЕ СРЕДСТВА ОТЛАДЧИКА
     ________________________________________________________________

     В операционной системе DOS версии 2.0  и  старше  можно  использовать
DEBUG для ввода команд ассемблера так же, как и команд машинного языка. На
практике можно пользоваться обоими методами.


                                Команда A
                               -----------
     Команда отладчика A (Assemble) переводит DEBUG в режим приема  команд
ассемблера и перевода  их  в  машинные  коды.  Установим  начальный  адрес
следующим образом:

               A 100 [Return]

Отладчик выдаст   значение   адреса  сегмента  кодов  и  смещения  в  виде
хххх:0100.  Теперь можно вводить каждую команду, завершая клавишей Return.
Когда вся программа будет введена, нажмите снова клавишу Return для выхода
из режима ассемблера. Введите следующую программу:

               MOV  AL,25     [Return]
               MOV  BL,32     [Return]
               ADD  AL,BL     [Return]
               RET            [Return]

по завершению на экране будет следующая информация:

               хххх:0100      MOV  AL,25
               хххх:0102      MOV  BL,32
               хххх:0104      ADD  AL,BL
               хххх:0106      RET

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


                                Команда U
                               -----------
     Команда отладчика U (Unassemble) показывает машинные коды для  команд
ассемблера.  Необходимо  сообщить  отладчику  адреса  первой  и  последней
команды, которые необходимо просмотреть  (в  данном  cлучае  100  и  106).
Введите:

               U 100,106 [и Return]

и на экране появится

               хххх:0100      B025      MOV  AL,25
               хххх:0102      B332      MOV  BL,32
               хххх:0104      00D8      ADD  AL,BL
               хххх:0106      C3        RET

Теперь проведем трассировку выполнения программы, начиная с команды R  для
вывода содержимого регистров и первой команды программы.  С помощью команд
T выполним последовательно все команды программы.
     Теперь вы знаете, как вводить программу в машинном коде или на  языке
ассемблера.  Обычно используется ввод на языке ассемблера, когда  машинный
код неизвестен, а ввод в машинном коде - для изменения программы во  время
выполнения.  Однако в действительности программа DEBUG  предназначена  для
отладки программ и в следующих  главах  основное  внимание  будет  уделено
использованию языка ассемблера.


                    Сохранение программы из отладчика
                   -----------------------------------
     Можно использовать DEBUG для сохранения программ на диске в следующих
случаях:

          1. После загрузки программы в память  машины  и  ее  модификации
     необходимо сохранить измененный вариант. Для этого следует:

               - загрузить  программу  по  ее  имени:  DEBUG  n:имя  файла
          [Return]
               - просмотреть  программу  с  помощью  команды  D  и  ввести
          изменения по команде E,
               - записать измененную программу: W [Return]

          2. Необходимо с  помощью  DEBUG  написать  небольшую  по  объему
     программу и сохранить ее на диске. Для этого следует:

               - вызвать отладчик DEBUG,
               - с помощью  команд  A  (assemble)  и  E  (enter)  написать
          программу,
               - присвоить  программе имя:  N имя файла.COM [Return].  Тип
          программы  должен  быть  COM  (см.   гл.6   для   пояснений   по
          COM-файлам),
               - так  как  только  программист  знает,  где  действительно
          кончается его программа, указать  отладчику  длину  программы  в
          байтах. В последнем примере концом программы является команда

               хххх:0106      C3   RET

          Эта команда однобайтовая и поэтому размер программы будет  равен
          106 (конец) минус 100 (начало), т.е. 6.
               - запросить регистр CX командой: R CX [Return]
               - отладчик выдаст на этот запрос CX 0000 (нулевое значение)
               - указать длину программы - 6,
               - записать измененную программу: W [Return]

     В обоих случаях DEBUG выдает сообщение "Writing nnnn bytes."  (Запись
nnnn байтов).  Если nnnn равно 0, то  произошла  ошибка  при  вводе  длины
программы, и необходимо повторить запись cнова.


     ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
     ________________________________________________________________

     Отладчик DOS DEBUG это  достаточное  мощное  средство,  полезное  для
отладки  ассемблерных  программ.  Однако  следует  быть  осторожным  с  ее
использованием, особенно для команды E (ввод).  Ввод данных в неправильные
адреса  памяти  или  ввод  некорректных   данных    могут    привести    к
непредсказуемым результатам.  На экране  в  этом  случае  могут  появиться
"странные" символы, клавиатура заблокирована или даже DOS прервет DEBUG  и
перезагрузит себя  с  диска.  Какие-либо  серьезные  повреждения  вряд  ли
произойдут,  но возможны некоторые неожиданности,  а также потеря  данных,
которые вводились при работе с отладчиком.
   Если данные, введенные в сегмент данных или  сегмент  кодов,  оказались
некорректными, следует, вновь используя команду E, исправить  их.  Однако,
можно не заметить ошибки  и  начать  трассиpовку  программы.  Но  и  здесь
возможно еще использовать команду E для изменений.  Если необходимо начать
выполнение с первой команды, то следует установить в  регистре  командного
указателя (IP) значение 0100. Введите команду  R  (register)  и  требуемый
регистр в следующем виде:

                              R IP [Return]

     Отладчик выдаст на экран содержимое регистра IP и перейдет в ожидание
ввода. Здесь следует ввести значение 0100 и нажать для проверки результата
команду R (без IP). 0тладчик выдаст содержимое регистров, флагов и  первую
выполняемую команду.  Теперь можно, используя команду T,  вновь  выполнить
трассировку программы.
     Если  ваша  программа  выполняет  какие-либо  подсчеты,  то  возможно
потребуется очистка некоторых областей памяти и регистров.  Но yбедитесь в
сохранении  содержимого  регистров  CS,  DS,  SP  и  SS,  которые    имеют
специфическое назначение.
     Прочитайте в руководстве по DOS главу о программе DEBUG.  В настоящий
момент рекомендуется: вводный материал и следующие команды oтладчика: дамп
(D), ввод (E), шестнадцатиричный (H), имя (N), выход  (Q),  регистры  (R),
трассировка (T) и  запись  (W).  Можно  oзнакомиться  также  и  с  другими
командами и проверить как они работают.



     ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
     ________________________________________________________________

     2.1. Напишите машинные команды для
          а) пересылки шест. значения 4629 в регистр AX;
          б) сложения шест. 036A с содержимым регистра AX.

     2.2. Предположим, что была введена следующая е команда:

               E CS:100 B8 45 01 05 25 00

     Вместо шест.  значения 45 предполагалось 54.  Напишите команду E  для
корректировки   только   одного   неправильно   введенного   байта,   т.е.
непосредственно замените 45 на 54.

     2.3. Предположим, что введена следующая е команда:

               E CS:100 B8 04 30 05 00 30 CB

          а) Что  представляют  собой  эти  команды?  (Сравните  с  первой
     программой в этой главе).
          б) После выполнения этой программы в  регистре  AX  должно  быть
     значение 0460, но в действительности оказалось 6004. В чем  ошибка  и
     как ее исправить?
          в) После исправления команд необходимо снова выполнить программу
     с первой команды. Какие две команды отладчика потребуются?

     2.4. Имеется следующая программа в машинных кодах:

               B0 25 D0 E0 B3 15 F6 E3 CB

     Программа выполняет следующее:

          - пересылает шест. значение 25 в регистр AL;
          -  сдвигает  содержимое  регистра  AL  на  один  бит  влево   (в
     результате в AL будет 4A);
          - пересылает шест. значение 15 в регистр BL;
          - умножает содержимое регистра AL на содержимое регистра BL.

Используйте отладчик для ввода (E) этой программы  по  адресу  CS:100.  Не
забывайте, что все значения представлены в шестнадцатиричном  виде.  После
ввода программы наберите D  CS:100  для  просмотра  сегмента  кода.  Затем
введите команду R и необходимое число команд T для  пошагового  выполнения
программы до команды RET.  Какое значение будет в регистре AX в результате
выполнения программы?

     2.5.  Используйте  отладчик  для  ввода  (E)  следующей  программы  в
машинных кодах:

               Данные:        25 15 00 00
               Машинный код:  A0 00 00 D0 E0 F6 26 01 00 A3 02 00 CB

     Программа выполняет следующее:

          - пересылает содержимое одного байта  по  адресу  DS:00  (25)  в
     регистр AL;
          - сдвигает содержимое регистра AL влево на один бит  (получая  в
     результате 4A);
          - умножает AL на содержимое одного байта по адресу DS:01 (15);
          - пересылает результат из AX в  слово,  начинающееся  по  адресу
     DS:02.

     После ввода программы используйте команды D  для  просмотра  сегмента
данных и сегмента кода. Затем введите команду R и необходимое число команд
T для достижения конца программы (RET).  В этот момент регистр  AX  должен
содержать результат 0612. Еще раз используйте команду D DS:00 и  заметьте,
что по адресу DS:02 значение записано как 1206.

     2.6. Для предыдущего  задания  (2.5)  постройте  команды  для  записи
программы на диск под именем TRIAL.COM.

     2.7. Используя команду A отладчика, введите следующую про грамму:

               MOV  BX,25
               ADD  BX,30
               SHL  BX,01
               SUB  BX,22
               NOP
               RET

сделайте  ассемблирование  и  трассировку  выполнения  этой  программы  до
команды NOP.



ГЛАВА 3                                         Требования языка ассемблер
__________________________________________________________________________

     Ц е л ь:   показать  основные  требования  к  программам   на   языке
ассемблера и этапы ассемблирования, компановки и выполнения программы.


     ВВЕДЕНИЕ
     ________________________________________________________________

     В гл.2 было показано как ввести и  выполнить  программу  на  машинном
языке.  Несомненно  при  этом ощутима трудность расшифровки машинного кода
даже для очень небольшой программы.  Сомнительно,  чтобы кто-либо серьезно
кодировал  программы  на  машинном  языке,  за исключением разных "заплат"
(корректировок)  в  программе  на  языках  высокого  уровня  и  прикладные
программы.  Более высоким уровнем кодирования является уровень ассемблера,
на  котором  программист  пользуется  символическими  мнемокодами   вместо
машинных команд и описательными именами для полей данных и адресов памяти.
     Программа написанная символическими мнемокодами, которые используются
в языке ассемблера, представляет собой исходный модуль.  Для  формирования
исходного модуля применяют программу DOS EDLIN или любой другой подходящий
экранный редактор.  Затем с помощью  программы  ассемблерного  транслятора
исходный текст транслируется  в  машинный  код,  известный  как  объектная
программа.  И наконец, программа DOS LINK определяет все  адресные  ссылки
для объектной программы, генерируя загрузочный модуль.
     В данной  главе  объясняются  требования  для  простой  программы  на
ассемблере и показаны этапы ассемблирования, компановки и выполнения.


     КОММЕНТАРИИ В ПРОГРАММАХ НА АССЕМБЛЕРЕ
     ________________________________________________________________

     Использование комментариев в программе улучшает ее ясность,  oсобенно
там, где назначение набора команд непонятно. Комментаpий всегда начинаются
на любой строке исходного модуля с символа точка с запятой (;) и ассемблер
полагает в этом случае, что все символы, находящиеся справа от ;  являются
комментарием.  Комментарий может содержать любые печатные символы, включая
пробел.
     Комментарий может занимать всю строку или следовать  за  командой  на
той же строке, как это показано в двух следующих примерах:

     1.        ;Эта строка полностью является комментарием
     2.        ADD AX,BX  ;Комментарий на одной строке с командой

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


     ФОРМАТ КОДИРОВАНИЯ
     ________________________________________________________________

     Основной формат кодирования команд ассемблера имеет следующий вид:

               [метка]   команда   [операнд(ы)]

     Метка (если имеется), команда и операнд (если имеется) pазделяются по
крайней мере одним пробелом или  символом  табуляции.  Максимальная  длина
строки  -  132  символа,  однако,  большинство  предпочитают  работать  со
строками  в  80  символов  (соответственно   ширине    экрана).    Примеры
кодирования:

               Метка     Команда   Операнд
               COUNT     DB        1    ;Имя, команда, один операнд
                         MOV       AX,0 ;Команда, два операнда


                                  Метки
                                 -------
     Метка в языке ассемблера может содержать  следующие символы:

               Буквы:         от A до Z и от a до z
               Цифры:         от 0 до 9
               Спецсимволы:   знак вопроса (?)
                              точка (.) (только первый символ)
                              знак "коммерческое эт" (@)
                              подчеркивание (-)
                              доллар ($)

     Первым символом в метке должна быть буква или  спецсимвол.  Ассемблер
не делает различия между  заглавными  и  строчными  буквами.  Максимальная
длина метки - 31 символ. Примеры меток: COUNT, PAGE25, $E10. Рекомендуется
использовать описательные и смысловые метки.  Имена  регистров,  например,
AX, DI или  AL  являются  зарезервированными  и  используются  только  для
указания соответствующих регистров. Например, в команде

               ADD  AX,BX

ассемблер "знает", что AX и BX относится к регистрам. Однако, в команде

               MOV  REGSAVE,AX

ассемблер воспримет имя REGSAVE  только  в  том  случае,  если  оно  будет
определено в   сегменте   данных.   В   прил.3   приведен   cписок    всех
зарезервированных слов ассемблера.


                                 Команда
                                ---------
     Мнемоническая команда  указывает  ассемблеру  какое  действие  должен
выполнить данный оператор.  В  сегменте  данных  команда  (или  директива)
определяет поле, рабочую oбласть или константу.  В сегменте  кода  команда
определяет действие, например, пересылка (MOV) или сложение (ADD).


                                 Операнд
                                ---------
     Если команда   специфицирует   выполняемое   действие,   то   операнд
определяет  а)  начальное  значение  данных или б) элементы,  над которыми
выполняется  действие  по  команде.  В  следующем  примере  байт   COUNTER
определен в сегменте данных и имеет нулевое значение:

          Метка     Команда   Операнд

          COUNTER   DB   0    ;Определить байт (DB) с нулевым значением

     Команда может иметь один  или  два  операнда,  или  вообще  быть  без
операндов. Рассмотрим следующие три примера:

                              Команда Операнд     Комментарий

               Нет операндов    RET               ;Вернуться
               Один операнд     INC     CX        ;Увеличить CX
               Два операнда     ADD     AX,12     ;Прибавить 12 к AX

     Метка,  команда  и  операнд  не  обязательно  должны  начинаться    с
какой-либо определенной позиции в строке. Однако, рекомендуется записывать
их в колонку для большей yдобочитаемости программы.  Для этого,  например,
редактор DOS EDLIN обеспечивает табуляцию через каждые восемь позиций.


     ДИРЕКТИВЫ
     ________________________________________________________________

     Ассемблер имеет ряд операторов, которые позволяют упpавлять процессом
ассемблирования  и  формирования  листинга.  Эти   операторы    называются
псевдокомандами  или  директивами.  Они  действуют  только   в    процессе
ассемблирования программы и  не  генерируют  машинных  кодов.  Большинство
директив показаны  в  следующих  разделах.  В  гл.24  подробно описаны все
директивы ассемблера и  приведено  более  чем  достаточно  соответствующей
информации. Гл.24 можно использовать в качестве справочника.


               Директивы управления листингом: PAGE и TITLE
              ----------------------------------------------
     Ассемблер содержит ряд директив,  управляющих  форматом  печати  (или
листинга).  Обе  директивы  PAGE  и  TITLE  можно  использовать  в   любой
программе.


     Д и р е к т и в а  PAGE.  В начале программы можно указать количество
строк,  распечатываемых  на  одной  странице,  и  максимальное  количество
символов на одной строке.  Для этой цели cлужит директива PAGE.  Следующей
директивой устанавливается 60 строк на страницу и 132 символа в строке:

                               PAGE 60,132

Количество строк на странице может  быть  в  пределах  от  10  до  255,  а
символов в строке - от 60 до 132. По умолчанию  в  ассемблере  установлено
PAGE 66,80.
     Предположим, что счетчик  строк  установлен  на  60.  В  этом  случае
ассемблер, распечатав 60 строк, выполняет прогон листа на начало следующей
страницы и  увеличивает  номер  страницы  на  eдиницу.  Кроме  того  можно
заставить ассемблер сделать прогон листа на конкретной строке, например, в
конце  сегмента.  Для  этого  необходимо  записать  директиву  PAGE    без
операндов.  Ассемблер автоматически  делает  прогон  листа  при  обработке
диpективы PAGE.


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

                              TITLE   текст

     Рекомендуется в  качестве  текста  использовать  имя  программы,  под
которым она находится  в  каталоге  на  диске.  Например,  если  программа
называется  ASMSORT,  то  можно  использовать  это  имя  и    описательный
комментарий общей длиной до 60 символов:

          TITLE ASMSORT - Ассемблерная программа сортировки имен

     В ассемблере также имеется  директива  подзаголовка  SUBTTL,  которая
может оказаться полезной для  очень  больших  программ,  содержащих  много
подпрограмм.


                            Директива SEGMENT
                           -------------------
     Любые ассемблерные программы содержат по крайней мере один сегмент  -
сегмент кода.  В некоторых программах используется  сегмент  для  стековой
памяти и сегмент данных для определения данных. Асcемблерная директива для
описания сегмента SEGMENT имеет следующий формат:

               Имя       Директива      Операнд
               имя       SEGMENT        [параметры]
                         .
                         .
                         .
               имя       ENDS

     Имя сегмента должно обязательно  присутствовать,  быть  уникальным  и
соответствовать  соглашениям  для  имен  в  ассемблере.  Директива    ENDS
обозначает конец сегмента.  Обе директивы  SEGMENT  и  ENDS  должны  иметь
одинаковые имена.  Директива SEGMENT может содержать три типа  параметров,
определяющих выравнивание, объединение и класс.

          1. В ы р а в н и в а н и е.  Данный параметр определяет  границу
     начала сегмента. Обычным значением является PARA, по которому сегмент
     устанавливается на границу параграфа.  В этом случае начальный  адрес
     делится  на 16 без остатка,  т.е.  имеет шест.  адрес nnn0.  В случае
     отсутствия этого операнда ассемблер принимает по умолчанию PARA.
          2. О б ъ е д и н е н и е.  Этот элемент определяет  объединяется
     ли данный сегмент с другими сегментами в  процессе  компановки  после
     ассемблирования  (пояснения  см.  в  следующем  разделе   "Компановка
     программы").  Возможны следующие  типы  объединений:  STACK,  COMMON,
     PUBLIC, AT выражение и MEMORY.  Сегмент стека определяется  следующим
     образом:
                       имя  SEGMENT   PARA   STACK

          Когда отдельно ассемблированные  программы  должны  объединяться
     компановщиком, то можно использовать типы: PUBLIC, COMMON и MEMORY. В
     случае, если программа не должна объединяться с другими  программами,
     то данная опция может быть опущена.
          3.  К л а с с.   Данный  элемент,   заключенный   в   апострофы,
     используется   для   группирования   относительных   сегментов    при
     компановке:

                  имя  SEGMENT   PARA   STACK   'Stack'

          Фрагмент программы на рис.3.1.  в следующем разделе иллюстрирует
     директиву SEGMENT и ее различные опции.


                              Директива PROC
                             ----------------
     Сегмент кода содержит выполняемые команды программы.  Кроме того этот
сегмент также включает в себя одну или  несколько  процедур,  определенных
директивой PROC. Сегмент, содержащий только одну процедуру имеет следующий
вид:
               имя-сегмента   SEGMENT   PARA
               имя-процедуры  PROC      FAR       Сегмент
                              .                   кода
                              .                   с
                              .                   одной
                              RET                 процедурой
               имя-процедуры  ENDP
               имя-сегмента   ENDS

     Имя процедуры должно обязательно присутствовать,  быть  уникальным  и
удовлетворять соглашениям по именам в ассемблере.  Операнд  FAR  указывает
загрузчику DOS, что начало данной  процедуры  является  точкой  входа  для
выполнения программы.
     Директива ENDP определяет конец процедуры и  имеет  имя,  аналогичное
имени в директиве PROC.  Команда RET завершает выполнение  программы  и  в
данном случае возвращает управление в DOS.
     Сегмент может содержать несколько процедур (см. гл.7).


                             Директива ASSUME
                            ------------------
     Процессор использует регистр SS для адресации стека, регистр  DS  для
адресации сегмента данных  и  регистр  CS  для  адресации  cегмента  кода.
Ассемблеру необходимо сообщить назначение каждого сегмента.  Для этой цели
служит директива ASSUME, кодируемая в сегменте кода следующим образом:

               Директива Операнд
               ASSUME     SS:имя_стека,DS:имя_с_данных,CS:имя_с_кода

     Например, SS:имя_стека указывает, что ассемблер должен  ассоциировать
имя сегмента стека с регистром SS.  Операнды могут  записываться  в  любой
последовательности.  Регистр  ES  также  может  присутствовать  в    числе
операндов.  Если программа не использует регистр ES, то его можно опустить
или указать ES:NOTHING.


                              Директива END
                             ---------------
     Как уже показано, директива ENDS завершает сегмент, а директива  ENDP
завершает процедуру.  Директива END в свою очередь полностью завершает всю
программу:

               Директива      Операнд
               END            [имя_процедуры]

     Операнд может  быть  опущен,  если  программа  не  предназначена  для
выполнения, например, если ассемблируются только определения  данных,  или
эта программа должна быть скомпанована с  другим  (главным)  модулем.  Для
обычной программы с  одним  модулем  oперанд  содержит  имя,  указанное  в
директиве PROC, которое было oбозначено как FAR.


     ПАМЯТЬ И РЕГИСТРЫ
     ________________________________________________________________

     Рассмотрим  особенности  использования  в  командах  имен,  имен    в
квадратных скобках и  чисел.  В  следующих  примерах  положим,  что  WORDA
определяет слово в памяти:

          MOV  AX,BX     ;Переслать содержимое BX в регистр AX
          MOV  AX,WORDA  ;Переслать содержимое WORDA в регистр AX
          MOV  AX,[BX]   ;Переслать содержимое памяти по адресу
                         ; в регистре BX в регистр AX
          MOV  AX,25     ;Переслать значение 25 в регистр AX
          MOV  AX,[25]   ;Переслать содержимое по смещению 25

     Новым здесь является использование квадратных скобок, что потребуется
в следующих главах.


     ИНИЦИАЛИЗАЦИЯ ПРОГРАММЫ
     ________________________________________________________________

     Существует  два  основных  типа  загрузочных  программ:  EXE  и  COM.
Рассмотрим требования к EXE-программам, а COM-программы будут представлены
в гл.6.  DOS  имеет  четыре  требования  для  инициализации   ассемблерной
EXE-программы:  1)  указать  ассемблеру,  какие cегментные регистры должны
соответствовать сегментам,  2) сохранить  в  стеке  адрес,  находящийся  в
регистре DS, когда программа начнет выполнение, 3) записать в стек нулевой
адрес и 4) загрузить в регистр DS адрес сегмента данных.
     Выход из программы и возврат в DOS сводится к  использованию  команды
RET.  Рис.3.1  иллюстрирует  требования  к  инициализации  и  выходу    из
программы:

          1. ASSUME - это ассемблерная  директива,  которая  устанавливает
     для  ассемблера  соответствие  между   конкретными    сегментами    и
     сегментными регистрами; в данном случае, CODESG - CS, DATASG -  DS  и
     STACKSG - SS.  DATASG и STACKSG не определены в этом примере, но  они
     будут представлены следующим образом:

          STACKSG  SEGMENT  PARA  STACK  Stack  'Stack'
          DATASG   SEGMENT  PARA  'Data'

          Ассоциируя сегменты с сегментными регистрами,  ассемблер  сможет
     определить смещения к отдельным областям в каждом сегменте. Например,
     каждая команда в сегменте  кодов  имеет  определенную  длину:  первая
     команда имеет смещение 0, и если это двухбайтовая команда, то  вторая
     команда будет иметь смещение 2 и т.д.
          2. Загрузочному модулю  в  памяти  непосредственно  предшествует
     256-байтовая (шест.100) область,  называемая  префиксом  программного
     сегмента  PSP.  Программа  загрузчика  использует  регистр  DS    для
     установки адреса  начальной  точки  PSP.  Пользовательская  программа
     должна сохранить этот адрес, поместив его в стек.  Позже, команда RET
     использует этот адрес для возврата в DOS.
          3.  В  системе  требуется,  чтобы  следующее  значение  в  стеке
     являлось нулевым адресом (точнее, смещением).  Для этого команда  SUB
     очищает регистр AX, вычитая его из этого же регистра  AX,  а  команда
     PUSH заносит это значение в стек.
          4.  Загрузчик  DOS  устанавливает  правильные  адреса  стека   в
     регистре SS и сегмента  кодов  в  регистре  CS.  Поскольку  программа
     загрузчика  использует  регистр  DS  для  других  целей,   необходимо
     инициализировать регистр DS двумя  командами  MOV,  как  показано  на
     рис.3.1. В следующем разделе этой главы "Исходная  программа.  Пример
     II" детально поясняется инициализация регистра DS.
          5. Команда RET обеспечивает выход из пользовательской  программы
     и возврат в DOS, используя для  этого  адрес,  записанный  в  стек  в
     начале программы командой PUSH DS. Другим обычно используемым выходом
     является команда INT 20H.

__________________________________________________________________________

     CODESG    SEGMENT PARA 'CODE'
     BEGIN     PROC FAR
1.             ASSUME    CS:CODESG,DS:DATASG,SS:STACKG
2.             PUSH      DS             ;Записать DS в стек
3.             SUB       AX,AX          ;Установить ноль в AX
               PUSH      AX             ;Записать ноль в стек
4.             MOV       AX,DATASG      ;Занести адрес
               MOV       DS,AX          ; DATASG в DS
               .
               .
               .
5.             RET                      ;Возврат в DOS
     BEGIN     ENDP
     CODESG    ENDS
               END       BEGIN
__________________________________________________________________________

     Рис.3.1. Инициализация EXE-программы.


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


     ПРИМЕР ИСХОДНОЙ ПРОГРАММЫ
     ________________________________________________________________

     Рис.3.2 обобщает предыдущие сведения в простой исходной программе  на
ассемблере.  Программа  содержит  сегмент стека - STACKSG и сегмент кода -
CODESG.

__________________________________________________________________________

        page    60,132
TITLE   EXASM1  (EXE) Пример регистровых операций
;------------------------------------------------
STACKSG SEGMENT PARA SACK 'Stack'
        DB      12 DUP('STACKSEG')
STACKSG ENDS
;------------------------------------------------
CODESG  SEGMENT PARA 'Code'
BEGIN   PROC    FAR
        ASSUME  SS:STACKSG,CS:CODESG,DS:NOTHING
        PUSH    DS              ;Записать DS в стек
        SUB     AX,AX           ;Записать ноль
        PUSH    AX              ; в стек

        MOV     AX,0123H        ;Записать шест.0123 в AX
        ADD     AX,0025H        ;Прибавить шест.25 к AX
        MOV     BX,AX           ;Переслать AX в BX
        ADD     BX,AX           ;Прибавить BX к AX
        MOV     CX,BX           ;Переслать BX в CX
        SUB     CX,AX           ;Вычесть AX из CX
        SUB     AX,AX           ;Очистить AX
        NOP
        RET                     ;Возврат в DOS
BEGIN   ENDP                    ;Конец процедуры

CODESG  ENDS                    ;Конец сегмента
        END     BEGIN           ;Конец программы
__________________________________________________________________________

     Рис.3.2. Пример исходной программы на ассемблере.


     STACKSG  содержит  один  элемент  DB  (определить   байт),    который
определяет 12 копий слова 'STACKSEG'.  В последующих  программах  стек  не
опpеделяется таким способом, но при использовании отладчика для  просмотра
ассемблированной  программы  на  экране,  данное   определение    помогает
локализовать стек.
     CODESG содержит выполняемые команды программы, хотя первая  директива
ASSUME не генерирует кода.  Директива  ASSUME  назначает  регистр  SS  для
STACKSG и  регистр  CS  для  CODESG.  В  действительности,  эта  директива
сообщает ассемблеру, что для адресации в STACKSG  необходимо  использовать
адрес в регистре SS и для адресации  в  CODESG  -  адрес  в  регистре  CS.
Системный загрузчик при загрузке программы с диска в память для выполнения
устанавливает действительные адреса в регистрах  SS  и  CS.  Программа  не
имеет  сегмента  данных,  так  как  в  ней  нет  определения  данных    и,
соответственно, в ASSUME нет необходимости ассигновать pегистр DS.
     Команды, следующие за ASSUME - PUSH, SUB и PUSH выполняют стандартные
действия для инициализации стека текущим адресом в регистре DS  и  нулевым
адресом.  Поскольку, обычно, программа выполняется из DOS, то эти  команды
обеспечивают возврат  в  DOS  после  завершения  программы.  (Можно  также
выполнить программу из отладчика, хотя это особый случай).
     Последующие команды выполняют те же действия, что показаны на pис.2.1
в предыдущей главе, когда рассматривался отладчик.


     ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
     ________________________________________________________________

     - Не забывайте ставить символ "точка с запятой" перед комментариями.

     - Завершайте каждый  сегмент  директивой  ENDS,  каждую  процедуру  -
директивой ENDP, а программу - директивой END.

     - В директиве ASSUME устанавливайте  соответствия  между  сегментными
регистрами и именами сегментов.

     - Для EXE-программ (но не для COM-программ, см.  гл.6)  обеспечивайте
не менее 32 слов для стека, соблюдайте соглашения по  инициализации  стека
командами PUSH, SUB и PUSH и заносите в регистр DS адрес сегмента данных.


     ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
     ________________________________________________________________

     3.1. Какие команды заставляют ассемблер печатать заголовок  в  начале
каждой страницы листинга и делать прогон листа?

     3.2. Какие из следующих имен неправильны: а) PC_AT, б) $50, в)  @$_Z,
г) 34B7, д) AX?

     3.3. Какое назначение каждого из трех  сегментов,  описанных  в  этой
главе?

     3.4. Что конкретно подразумевает директива END, если она завершает а)
программу, б) процедуру, в) сегмент?

     3.5. Укажите различия между директивой и командой.

     3.6. Укажите различия в назначении RET и END.

     3.7. Для сегментов кода, данных и стека даны имена  CDSEG,  DATSEG  и
STKSEG соответственно. Сформируйте директиву ASSUME.

     3.8. Напишите три команды для инициализации  стека  адресом  в  DS  и
нулевым адресом.



ГЛАВА 4                              Ассемблирование и выполнение программ
__________________________________________________________________________

     Ц е л ь: показать процессы ассемблирования, компановки  и  выполнения
программ.


     ВВЕДЕНИЕ
     ________________________________________________________________

     В  данной  главе  объясняется,  как  ввести  в  компьютер    исходный
ассемблерный текст программы, как осуществить ассемблирование,  компоновку
и  выполнение  программы.  Кроме  того,   показана    генерация    таблицы
перекрестных ссылок для целей отладки.



     ВВОД ПРОГРАММЫ
     ________________________________________________________________

     На рис.3.2    был    показан   только   исходный   текст   программы,
предназначенный для ввода с помощью  текстового  редактора.  Теперь  можно
использовать  DOS  EDLIN  или  другой  текстовый  редактор  для ввода этой
программы.  Если вы никогда не пользовались программой  EDLIN,  то  именно
сейчас  необходимо  выполнить  ряд  упражнений из руководства по DOS.  Для
запуска  программы  EDLIN  вставьте   дискету   DOS   в   дисковод   A   и
форматизованную дискету в дисковод B. Чтобы убедиться в наличии на дискете
свободного места для исходного текста,  введите CHKDSK B:.  Для винчестера
во  всех следующих примерах следует использовать C:  вместо B:.  Для ввода
исходной программы EXASM1, наберите команду

               EDLIN В:EXASM1.ASM [Return]

     В результате DOS загрузит EDLIN в памяти и  появится  сообщение  "New
file" и приглашение "*-".  Введите команду I  для  ввода  строк,  и  затем
наберите каждую ассемблерную команду так,  как они изобpажены на  рис.3.2.
Хотя  число  пробелов  в тексте для ассемблера не существенно,  старайтесь
записывать метки, команды, операнды и комментарии, выровненными в колонки,
программа  будет  более  yдобочитаемая.  Для  этого  в  EDLIN используется
табуляция через каждые восемь позиций.
     После ввода программы убедитесь в ее правильности.  Затем наберите  E
(и Return) для завершения  EDLIN.  Можно  проверить  наличие  программы  в
каталоге на диске, введите

               DIR B:              (для всех файлов)
          или  DIR B:EXASM1.ASM    (для одного файла)

     Если предполагается ввод исходного текста большего объема, то  лучшим
применением  будет  полноэкранный  редактор.  Для  получения    распечатки
программы включите принтер и установите в него бумагу.  Вызовите программу
PRINT  (для  DOS  2.0  и  старше).  DOS  загрузит  программу  в  память  и
распечатает текст на принтере:

               PRINT B:EXASM1.ASM [Return]

     Программа EXASM.ASM еще не может быть выполнена -  прежде  необходимо
провести ее ассемблирование и компоновку. В следующем pазделе показана эта
же программа после ассемблирования  и  пояснены  этапы  ассемблирования  и
получения листинга.


     ПОДГОТОВКА ПРОГРАММЫ ДЛЯ ВЫПОЛНЕНИЯ
     ________________________________________________________________

     После  ввода  на  диск  исходной  программы  под  именем   EXASM1.ASM
необходимо проделать два основных шага, прежде чем программу  можно  будет
выполнить.  Сначала необходимо ассемблиpовать программу, а затем выполнить
компоновку.  Программисты на языке бейсик могут выполнить программу  сразу
после ввода исходного текста,  в то время как для ассемблера и компилярных
языков нужны шаги трансляции и компоновки.
     Шаг ассемблирования включает  в  себя  трансляцию  исходного  кода  в
машинный объектный код и генерацию OBJ-модуля.  Вы уже  встречали  примеры
машинного кода в гл.2 и примеры исxодного текста в этой главе.
     OBJ-модуль уже более приближен к  исполнительной  форме,  но  еще  не
готов к выполнению.  Шаг компановки включает преобразование  OBJ-модуля  в
EXE  (исполнимый)  модуль,  содержащий  машинный  код.  Программа    LINK,
находящаяся на диске DOS, выполняет следующее:

          1. Завершает формирование в OBJ-модуле адресов, которые остались
     неопределенными после ассемблирования. Во многих следующих программах
     такие адреса ассемблер отмечает как ----R.
          2.  Компонует,  если  необходимо,    более    одного    отдельно
     ассемблированного модуля в одну загрузочную  (выполнимую)  программу;
     возможно  две  или  более  ассемблерных  программ  или   ассемблерную
     программу с программами, написанными на языках высокого уровня, таких
     как Паскаль или Бейсик.
          3. Инициализирует EXE-модуль командами загрузки для выполнения.

     После компановки OBJ-модуля (одного или более)  в  EXE-модуль,  можно
выполнить EXE-модуль любое число раз. Но, если необходимо внести некоторые
изменения  в  EXE-модуль,  следует  скорректировать  исходную   программу,
ассемблировать ее в другой OBJ-модуль и выполнить компоновку OBJ-модуля  в
новый EXE-модуль.  Даже, если  эти  шаги  пока  остаются  непонятными,  вы
обнаружите,  что,  получив  немного  навыка,  весь   процесс    подготовки
EXE-модуля будет  доведен  до  автоматизма.  Заметьте:  определенные  типы
EXE-программ  можно  преобразовать  в  oчень  эффективные   COM-программы.
Предыдущие примеры, однако, не  cовсем  подходят  для  этой  цели.  Данный
вопрос рассматривается в главе 6.


     АССЕМБЛИРОВАНИЕ ПРОГРАММЫ
     ________________________________________________________________

     Для того, чтобы выполнить исходную ассемблерную программу, необходимо
прежде провести ее  ассемблирование  и  затем  компоновку.  На  дискете  с
ассемблерным пакетом имеются две версии aссемблера.  ASM.EXE - сокращенная
версия с отсутствием некоторых незначительных возможностей  и  MASM.EXE  -
полная версия.  Если размеры памяти позволяют, то используйте версию  MASM
(подробности см. в соответствующем руководстве по ассемблеру).
     Для ассемблирования,  вставьте ассемблерную дискету в дисковод  A,  а
дискету  с  исходной  программой  в дисковод B.  Кто имеет винчестер могут
использовать в следующих примеpах C  вместо  A  и  B.  Простейший  вариант
вызова программы это ввод команды MASM (или ASM),  что приведет к загрузке
программы ассемблера с диска в память. На экране появится:

               source filename [.ASM]:
               object filename [filename.OBJ]:
               source listing [NUL.LST]:
               cross-reference [NUL.CRF]:

     Курсор при этом расположится в конце первой  строки,  где  необходимо
указать  имя  файла.  Введите  номер  дисковода  (если  он  не   определен
умолчанием) и имя файла в следующем виде: B:EXASM1.  Не  следует  набирать
тип файла ASM, так как ассемблер подразумевает это.
     Во-втором запросе предполагается аналогичное имя файла (но можно  его
заменить). Если необходимо, введите номер дисковода B:.
     Третий запрос предполагает, что листинг ассемблирования программы  не
требуется.  Для получения листинга на дисководе B наберите  B:  и  нажмите
Return.
     Последний запрос предполагает, что  листинг  перекрестных  cсылок  не
требуется.  Для получения листинга на дисководе B, наберите B:  и  нажмите
Return.
     Если вы хотите оставить значения по умолчанию, то  в  трех  последних
запросах просто нажмите Return. Ниже приведен пример запросов и ответов, в
результате которых ассемблер должен cоздать OBJ, LST и CRF-файлы.  Введите
ответы так, как показано, за исключением того, что номер  дисковода  может
быть иной.

               source filename [.ASM]:B:EXASM1 [Return]
               object filename [filename.OBJ]:B: [Return]
               source listing [NUL.LST]:B: [Return]
               cross-reference [NUL.CRF]:B: [Return]

     Всегда необходимо вводить имя исходного файла и, обычно,  запрашивать
OBJ-файл - это требуется для  компановки  программы  в  загрузочный  файл.
Возможно  потребуется  указание  LST-файла,  особенно,  если    необходимо
проверить сгенерированный машинный код. CRF-файл полезен для очень больших
программ, где необходимо видеть, какие команды  ссылаются  на  какие  поля
данных. Кроме того, ассемблер генерирует в LST-файле номера строк, которые
используются в CRF-файле.
     В прил.4 "Режимы ассемблирования и редактирования" перечислены режимы
(опции) для ассемблера версий 1.0 и 2.0.
     Ассемблер преобразует исходные команды в машинный  код  и  выдает  на
экран сообщения о возможных ошибках. Типичными ошибками являются нарушения
ассемблерных  соглашений  по  именам,  неправильное    написание    команд
(например, MOVE вместо MOV), а также наличие  в  опеpандах  неопределенных
имен.  Программа ASM  выдает  только  коды  ошибок,  которые  объяснены  в
руководстве по ассемблеру, в то время как программа  МASM  выдает  и  коды
ошибок, и пояснения к ним. Всего имеется около 100 сообщений об ошибках.
     Ассемблер делает попытки скорректировать некоторые ошибки, но в любом
случае  следует  перезагрузить  текстовый  редактор,  исправить   исходную
программу (EXASM1.ASM) и повторить ассемблирование.
     На рис.4.1 показан листинг,  полученный в результате  асcемблирования
программы и записанный на диск под именем EXASM1.LST.

__________________________________________________________________________

1                                page   60,132
2                        TITLE   EXASM1 (EXE) Пример регистровых операций
3                        ;-----------------------------------------------
4  0000                  STACKSG SEGMENT PARA SACK 'Stack'
5  0000  0C [                    DB      12 DUP('STACKSEG')
6            53 54 41 43
7            4B 53 45 47
8                       ]
9
10 0060                  STACKSG ENDS
11                       ;-----------------------------------------------
12 0000                  CODESG  SEGMENT PARA 'Code'
13 0000                  BEGIN   PROC    FAR
14                               ASSUME  SS:STACKSG,CS:CODESG,DS:NOTHING
15 0000  1E                      PUSH    DS       ;Записать DS в стек
16 0001  2B C0                   SUB     AX,AX    ;Записать ноль
17 0003  50                      PUSH    AX       ; в стек
18
19 0004  B8 0123                 MOV     AX,0123H ;Записать шест.0123 в AX
20 0007  05 0025                 ADD     AX,0025H ;Прибавить шест.25 к AX
21 000A  8B D8                   MOV     BX,AX    ;Переслать AX в BX
22 000C  03 D8                   ADD     BX,AX    ;Прибавить BX к AX
23 000E  8B CB                   MOV     CX,BX    ;Переслать BX в CX
24 0010  2B C8                   SUB     CX,AX    ;Вычесть AX из CX
25 0012  2B C0                   SUB     AX,AX    ;Очистить AX
26 0014  90                      NOP
27 0015  CB                      RET              ;Возврат в DOS
28 0016                  BEGIN   ENDP             ;Конец процедуры
29
30 0016                  CODESG  ENDS             ;Конец сегмента
31                               END     BEGIN    ;Конец программы
-------------------------------------------------------------------------
   Segments and Groups:
                N a m e           Size   Align    Combine   Class
   CODESG . . . . . . . . . . . . 0016   PARA     NONE      'CODE'
   STACKSG. . . . . . . . . . . . 0060   PARA     STACK     'STACK'

   Symbols:
                N a m e           Type   Value    Attr
   BEGIN. . . . . . . . . . . . . F PROC 0000     CODESG   Length=0016
__________________________________________________________________________

     Рис.4.1. Листинг ассемблирования программы.


     В  начале  листинга  обратите  внимание  на  реакцию  ассемблера   на
директивы PAGE и TITLE. Никакие директивы, включая SEGMENT, PROC, ASSUME и
END не генерируют машинных кодов.
     Листинг  содержит  не  только  исходный  текст,  но    также    слева
транслированный машинный код в шестнадцатиричном формате.  В  самой  левой
колонке находится шест.адреса команд и данных.
     Сегмент  стека  начинается  с  относительного    адреса    0000.    В
действительности он загружается  в  память  в  соответствии  с  адpесом  в
регистре SS и  нулевым  смещением  относительно  этого  адpеса.  Директива
SEGMENT устанавливает 16-кратный адрес и  указывает  ассемблеру,  что  это
есть начало стека.  Сама директива не генерирует машинный код. Команда DB,
также находится по  адресу  0000,  содержит  12  копий  слова  'STACKSEG';
машинный код представлен шест.0C (десятичное 12)  и  шест.  представлением
ASCII символов. (В дальнейшем можно использовать  отладчик  для  просмотра
результатов в  памяти).  Сегмент  стека заканчивается по адресу шест.0060,
который эквивалентен десятичному значению 96 (12х8).
     Сегмент кода  также  начинается  с  относительного  адреса  0000.  Он
загружается в память в соответствии с адресом  в  pегистре  CS  и  нулевым
смещением относительно этого адреса.  Поскольку ASSUME является директивой
ассемблеру, то первая команда, которая генерирует действительный  машинный
код есть PUSH DS -  однобайтовая  команда  (1E),  находящаяся  на  нулевом
смещении. Следующая команда SUB AX,AX генерирует двухбайтовый машинный код
(2B C0), начинающийся с относительного адреса 0001. Пробел  между  байтами
только для удобочитаемости.  В данном примере встречаются одно-,  двух-  и
трехбайтовые команды.
     Последняя команда END содержит операнд BEGIN, который имеeт отношение
к имени команды PROC по смещению 0000. Это есть адрес  сегмента  кодов,  с
которого начинается выполнение после загрузки программы.
     Листинг ассемблирования программы EXASM1.LST, имеет по директиве PAGE
шиpину 132 символа и может быть распечатан. Многие принтеры могут печатать
текст сжатым шрифтом. Включите ваш принтер и введите команду

               MODE LPT1:132,6


                         Таблица идентификаторов
                        -------------------------
     За    листингом    ассемблирования    программы    следует    таблица
идентификаторов.  Первая часть таблицы содержит определенные  в  программе
сегменты и группы вместе с их размером в байтах, выравниванием и  классом.
Вторая часть содержит идентификаторы  -  имена  полей  данных  в  сегменте
данных (в нашем примере их нет) и метки, назначенные командам  в  сегменте
кодов (одна в нашем примере).  Для того, чтобы ассемблер не  создавал  эту
таблицу, следует указать параметр /N вслед за командой MASM, т.е. MASM/N.


                         Двухпроходный ассемблер
                        -------------------------
     В  процессе  трансляции  исходной  программы  ассемблер  делает   два
просмотра исходного текста, или два  прохода.  Одной  из  основных  причин
этого являются ссылки  вперед,  что  происходит  в  том  случае,  когда  в
некоторой команде кодируется метка,  значение которой  еще  не  определено
ассемблером.
     В первом проходе ассемблер просматривает  всю  исходную  прогpамму  и
строит таблицу идентификаторов, используемых в программе, т.е. имен  полей
данных и меток программы и их относительных aдресов в программе.  В первом
проходе подчитывается объем объектного  кода,  но  сам  объектный  код  не
генерируется.
     Во  втором  проходе  ассемблер  использует  таблицу  идентификаторов,
построенную в  первом  проходе.  Так  как  теперь  уже  известны  длины  и
относительные адреса всех  полей  данных  и  команд,  то  ассемблер  может
сгенерировать объектный код для каждой команды.  Ассемблер  создает,  если
требуется, файлы: OBJ, LST и CRF.


     КОМПАНОВКА ПРОГРАММЫ
     ________________________________________________________________

     Если в результате ассемблирования не обнаружено ошибок, то  cледующий
шаг -  компановка  объектного  модуля.  Файл  EXASM1.OBJ  содержит  только
машинный  код  в  шестнадцатеричной  форме.  Так  как   программа    может
загружаться почти в любое место памяти для выполнения, то ассемблер  может
не определить все машинные адреса. Кроме того, могут использоваться другие
(под) программы для объединения с  основной.  Назначением  программы  LINK
является  завершение  определения  адресных  ссылок  и  объединение  (если
требуется) нескольких программ.
     Для компановки ассемблированной программы с дискеты, вставьте дискету
DOS в дисковод A,  а дискету  с  программой  в  дисковод  B.  Пользователи
винчестерского диска могут загрузить компоновщик LINK прямо с дисковода C.
Введите команду LINK и нажмите клавишу Return.  После загрузки  в  память,
компоновщик  выдает  несколько  запросов  (аналогично  MASM),  на  которые
необходимо ответить:

          Запрос компоновщика        Ответ          Действие

          Object Modules [.OBJ]:     B:EXASM1  Компонует EXASM1.OBJ
          Run file [EXASM1.EXE]:     B:        Создает EXASM1.EXE
          List file [NUL.MAP]:       CON       Создает EXASM1.MAP
          Libraries [.LIB]:          [Return]  По умолчанию

     Первый запрос - запрос имен объектных модулей для компановки, тип OBJ
можно опустить.
     Второй  запрос  -  запрос  имени  исполнимого  модуля  (файла),   (по
умолчанию A:EXASM1.EXE).  Ответ B: требует, чтобы компоновщик создал  файл
на дисководе В.  Практика сохранения одного имени (при разных типах) файла
упрощает работу с программами.
     Третий запрос предполагает, что LINK выбирает значение по yмолчанию -
NUL.MAP (т.е.  MAP отсутствует). MAP-файл содержит таблицу имен и размеров
сегментов и ошибки, которые  обнаружит  LINK.  Типичной  ошибкой  является
неправильное определение  сегмента  стека.  Ответ  CON  предполагает,  что
таблица будет выведена на экран, вместо записи ее на диск.  Это  позволяет
сэкономить  место  в  дисковой  памяти  и  сразу    просмотреть    таблицу
непосредственно на экране.  В нашем примере  MAP-файл  содержит  следующую
информацию:

               Start     Stop      Length    Name

               00000H    00015H    0016H     CODESG
               00020H    0007FH    0060H     STACKSG

     Для  ответа  на  четвертый  запрос  -  нажмите  Return,  что   укажет
компоновщику LINK  принять  остальные  параметры  по  yмолчанию.  Описание
библиотечных средств можно найти в руководстве по DOS.
     На данном этапе единственной возможной ошибкой  может  быть  yказание
неправильных  имен  файлов.  Исправить  это  можно  только    перезапуском
программы LINK. В прил.4 перечислен ряд pежимов компоновщика LINK.


     ВЫПОЛНЕНИЕ ПРОГРАММЫ
     ________________________________________________________________

     После ассемблирования  и  компановки  программы  можно  (наконец-то!)
выполнить ее.   На   рис.4.2   приведена   схема   команд   и   шагов  для
ассемблирования,  компановки и выполнения программы EXASM1.  Если EXE-файл
находится на дисководе B, то выполнить ее можно командой:

               B:EXASM1.EXE   или   B:EXASM1

     DOS предполагает, что файл имеет тип EXE (или COM), и загружает  файл
для  выполнения.  Но  так  как  наша  программа  не  вырабатывает  видимых
результатов, выполним ее трассировкой под отладчиком DEBUG. Введите

               DEBUG B:EXASM1.EXE

     В результате DOS загрузит программу DEBUG, который, в  свою  очередь,
загрузит требуемый EXE-модуль.  После этого отладчик выдаст  дефис  (-)  в
качестве приглашения. Для просмотра сегмента стека введите

               D SS:0

     Эту  область  легко  узнать  по  12-кратному  дублированию  константы
STACKSEG. Для просмотра сегмента кода введите

               D CS:0

     Сравните машинный код с листингом ассемблера:

               1E2BC050B823010525008BD803 ...

     Непосредственные операнды, приведенные в листинге ассемблирования как
0123 и 0025 в памяти представлены в виде 2301  и  2500  соответственно.  В
данном случае листинг ассемблирования не  вполне  соответствует  машинному
коду.  Все двухбайтовые  адреса  (слова)  и  непосредственные  операнды  в
машинном коде хранятся в обратном порядке.

__________________________________________________________________________
__________________________________________________________________________

     Рис.4.2. Схема ассемблирования, компановки и выполнения программы.


     Введите R для просмотра содержимого регистров и выполните прогpамму с
помощью команды T (трассировка).  Обратите внимание  на  воздействие  двух
команд PUSH на стек - в вершине стека теперь находится содержимое регистра
DS и нулевой адрес.
     В процессе  пошагового  выполнения  программы  обратите  внимание  на
содержимое регистров.  Когда вы дойдете до команды  RET,  можно  ввести  Q
(Quit - выход) для завершения работы отладчика.
     Используя команду dir, можно проверить наличие ваших файлов на диске:

               DIR B:EXASM1.*

     В результате на экране появится следующие  имена  файлов:  EXASM1.BAK
(если  для  корректировки  EXASM1.ASM  использовался   редактор    EDLIN),
EXASM1.ASM,   EXASM1.OBJ,    EXASM1.LST,    EXASM1.EXE    и    EXASM1.CRF.
Последовательность этих файлов может быть иной в зависимости от того,  что
уже находится на диске.
     Очевидно, что разработка ряда программ приведет к  занятию  дискового
пространства.  Для проверки оставшегося свободного места на диске  полезно
использовать  команду  DOS  CHKDSK.  Для  удаления  OBJ-,  CRF-,  BAK-   и
LST-файлов с диска следует использовать команду ERASE (или DEL):

               ERASE B:EXASM1.OBJ, ...

     Следует оставить (сохранить) ASM-файл  для  последующих  изменений  и
EXE-файл для выполнения.
     В  следующем  разделе  представлено  определение  данных  в  сегменте
данных. Позже будет описана таблица перекрестных cсылок.



     ПРИМЕР ИСХОДНОЙ ПРОГРАММЫ
     ________________________________________________________________

     Особенность программы, приведенной на рис.4.1, состоит в том, что она
не содержит определения данных.  Обычно все программы  имеют  определенные
константы,  рабочие  поля  для  арифметических  вычислений  и  области для
операций ввода-вывода.
     В главе 2 (рис.2.3) была рассмотрена программа в  машинных  кодах,  в
которой были  определены  два  поля  данных.  В  этой  главе  на   рис.4.3
приводится  аналогичная  программа,  но  на  этот  раз написанная на языке
ассемблера и для краткости уже ассемблированная.  Эта программа знакомит с
несколькими новыми особенностями.

__________________________________________________________________________

1                             page   60,132
2                     TITLE   EXASM2 (EXE) Операции пересылки и сложения
3                     ;-------------------------------------------------
4  0000               STACKSG SEGMENT PARA SACK 'Stack'
5  0000  20 [                 DB      32 DUP(?)
6           ????
7               ]
8
9  0040               STACKSG ENDS
10                    ;-------------------------------------------------
11 0000               DATASG  SEGMENT PARA 'Data'
12 0000  00FA         FLDA    DW      250
13 0002  007D         FLDB    DW      125
14 0004  ????         FLDC    DW      ?
15 0006               DATASG  ENDS
16                    ;-------------------------------------------------
17 0000               CODESG  SEGMENT PARA 'Code'
18 0000               BEGIN   PROC    FAR
19                            ASSUME  CS:CODESG,DS:DATASG,SS:STACKSG,ES:NO
THING
20 0000  1E                   PUSH    DS          ;Записать DS в стек
21 0001  2B C0                SUB     AX,AX       ;Записать в стек
22 0003  50                   PUSH    AX          ; нулевой адрес
23 0004  B8 ---- R            MOV     AX,DATASG   ;Поместить адрес DATASG
24 0007  8E D8                MOV     DS,AX       ; в регистр DS
25
26 0009  A1 0000 R            MOV     AX,FLDA     ;Переслать 0250 в AX
27 000C  03 06 0002 R         ADD     AX,FLDB     ;Прибавить 0125 к AX
28 0010  A3 0004 R            MOV     FLDC,AX     ;Записать сумму в FLDC
29 0013  CB                   RET                 ;Вернуться в DOS
30 0014               BEGIN   ENDP
31 0014               CODESG  ENDS
32                            END     BEGIN
------------------------------------------------------------------------
   Segments and Groups:
                N a m e           Size   Align    Combine   Class
   CODESG . . . . . . . . . . . . 0014   PARA     NONE      'CODE'
   DATASG . . . . . . . . . . . . 0006   PARA     NONE      'DATA'
   STACKSG. . . . . . . . . . . . 0040   PARA     STACK     'STACK'

   Symbols:
                N a m e           Type   Value    Attr
   BEGIN. . . . . . . . . . . . . F PROC 0000     CODESG   Length=0014
   FLDA . . . . . . . . . . . . . L WORD          0000     DATASG
   FLDB . . . . . . . . . . . . . L WORD          0002     DATASG
   FLDC . . . . . . . . . . . . . L WORD          0004     DATASG
__________________________________________________________________________

     Рис.4.3. Листинг ассемблирования программы с сегментом данных.


     Сегмент стека содержит директиву DW (Define Word - определить cлово),
описывающая 32  слова,  в  которых  генерируется  неопределенное  значение
обозначенное знаком вопроса (?).  Определение размера  стека  в  32  слова
является  наиболее  реальным,  так  как  в  больших    программах    может
потребоваться много "прерываний" для ввода-вывода и вызовов подпрограмм  -
все  они  используют  стек.  Определение  стека  дублированием   константы
'STACKSEG' в  примере на pис.3.2 необходимо лишь для удобства при работе с
отладчиком DEBUG.
     З а м е ч а н и е: Определяйте размер стека не  менее  32  слов.  При
малых размерах стека ни ассемблер, ни  компоновщик  не  смогут  определить
этого и  выполнение  программы  может  разрушиться  самым  непредсказуемым
образом.
     В примере на рис.4.3 определен сегмент данных DATASG, начинающийся по
относительному  адресу 0000.  Этот сегмент содержит три значения в формате
DW. Поле FLDA определяет слово (два байта), содержащее десятичное значение
250, которое ассемблер транслирует в шест.00FA (см. на рисунке слева).
     Поле FLDB  определяет  слово  с  десятичным  значением  125,  котоpое
транслируется  в  шест.007D.  Действительные значения этих двух констант в
памяти - FA00  и  7D00  соответственно,  что  можно  проверить  c  помощью
отладчика DEBUG.
     Поле FLDC определяет  слово  с  неизвестным  значением,  обозначенным
знаком вопроса (?).
     Сегмент кода в данном примере имеет имя CODESG  и  отличается  новыми
особенностями, связанными с сегментом данных.  Во-первых, директива ASSUME
указывает на определение DATASG через  регистр  DS.  Данной  программе  не
требуется   регистр  ES,  но  некоторые  программисты  описывают  его  для
стандартизации.  Во-вторых,  после  команд  PUSH,  SUB  и  PUSH,   которые
инициализируют   стек,   следуют  две  команды,  обеспечивающие  адресацию
сегмента данных:

               0004  B8 ---- R     MOV  AX,DATASG
               0007  8E D8         MOV  DS,AX

     Первая команда MOV загружает DATASG в регистр AX.  Конечно, на  самом
деле команда не может загрузить сегмент в регистр  -  она  загружает  лишь
адрес сегмента DATASG. Обратите внимание на машинный код слева:

               B8 ---- R

Четыре дефиса  говорят  о  том,  что  ассемблер  не может определить aдрес
DATASG;  он определяется лишь когда объектная программа будет скомпонована
и   загружена   для  выполнения.  Поскольку  загpузчик  может  расположить
программу в любом месте памяти,  асcемблер оставляет данный адрес открытым
и  показывает  это  символом R;  компоновщик должен будет подставить в это
место действительный адрес.
     Вторая команда MOV пересылает содержимое регистра AX  в  регистр  DS.
Таким образом, данная программа имеет директиву ASSUME, которая  соотносит
регистр DS с сегментом данных,  и  команды,  инициализирующие  регистр  DS
относительным адресом DATASG.
     Могут возникнуть два вопроса по  поводу  этой  программы.  Во-первых,
почему  не  использовать  одну  команду  для  инициализации  регистра  DS,
например

               MOV  DS,DATASG ?

     Дело в том, что не существует команд для  непосредственной  переcылки
данных из  памяти  в  регистр  DS.  Следовательно,  для  инициализации  DS
необходимо кодировать две команды.
     Во-вторых, почему программа инициализирует регистр DS, а регистры  SS
и CS нет? Оказывается, регистры SS и CS инициализируются автоматически при
загрузке программы для  выполнения,  а  ответственность  за  инициализацию
регистра DS и, если требуется ES, лежит полностью на самой программе.
     Пока все эти требования могут показаться весьма туманными,  но cейчас
нет необходимости   понимать  их.  Все  последующие  программы  используют
аналогичную стандартную инициализацию стека  и  сегмента  данных.  Поэтому
можно   просто   копировать   данные  коды  для  каждой  новой  программы.
Действительно,  вы можете сохранить на диске стандартную часть программы и
для  каждой  новой  программы  копировать  эту  часть  с новым именем,  и,
используя затем редактор, записать дополнительные команды.
     В качестве упражнения, создайте с помощью вашего редактора программу,
приведенную на рис.4.3, выполните ее ассемблирование и компоновку. Затем с
помощью   отладчика  DEBUG  просмотрите  сегмент  кодов,  сегмент  данных,
регистры и проделайте пошаговое выполнение программы.


     ФАЙЛ ПЕРЕКРЕСТНЫХ ССЫЛОК
     ________________________________________________________________

     В  процессе  трансляции  ассемблер  создает  таблицу  идентификаторов
(CRF), которая может быть представлена в виде листинга перекрестных ссылок
на метки, идентификаторы и переменные в программе.  Для получения  данного
фала, необходимо на четвертый запрос ассемблера, oтветить B:, полагая, что
файл должен быть создан на диске B:

               cross-reference [NUL.CRF]:B: [Return]

     Далее необходимо преобразовать полученный CRF-файл в  отсортиpованную
таблицу перекрестных ссылок.  Для  этого  на  ассемблерном  диске  имеется
соответствующая программа. После успешного ассемблирования введите команду
CREF. На экране появится два запроса:

               Cref filename [.CRF]:
               List filename [cross-ref.REF]:

     На первый запрос введите имя  CRF-файла,  т.е.  B:EXASM1.  На  второй
запрос можно ввести только номер дисковода и получить  имя  по  умолчанию.
Такой выбор приведет к записи CRF в  файл  перекрестных  ссылок  по  имени
EXASM1.REF на дисководе B.
     Для распечатки файла  перекрестных  ссылок  используйте  команду  DOS
PRINT. В приложении 4 приведен ряд режимов программы CREF.

__________________________________________________________________________

          EXASM2  (EXE) Операции пересылки и сложения

          Symbol Cross Reference    (# is definition)     Cref-1

          BEGIN. . . . . . . . . . . .    18#    30     32

          CODE . . . . . . . . . . . .    17
          CODESG . . . . . . . . . . .    17#    19     31

          DATA . . . . . . . . . . . .    11
          DATASG . . . . . . . . . . .    11#    15     19    23

          FLDA . . . . . . . . . . . .    12#    26
          FLDB . . . . . . . . . . . .    13#    27
          FLDC . . . . . . . . . . . .    14#    28

          STACK. . . . . . . . . . . .     4
          STACKSG. . . . . . . . . . .     4#     9     19

          10 Symbols
__________________________________________________________________________

     Рис.4.4. Таблица перекрестных ссылок.


     На рис.4.4  показана  таблица  перекрестных  ссылок  для   программы,
приведенной  на  рис.4.3.  Все  идентификаторы  в  таблице  предcтавлены в
алфавитном порядке и для каждого из них указаны номеpа  строк  в  исходной
программе,  где они определены и имеют ссылки. Имена сегментов и элементов
данных представлены в алфавитном поpядке. Первое число справа в формате n#
указывает  на  номер  строки  в  LST-файле,  где определен соответствующий
идентификатор.  Еще правее находятся числа,  указывающие на номера  строк,
где  имеются  cсылки на этот идентификатор.  Например,  CODESG определен в
строке 17 и имеет ссылки на строках 19 и 32.


     ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
     ________________________________________________________________

     - Ассемблер преобразует исходную программу в OBJ-файл, а  компоновщик
- OBJ-файл в загрузочный EXE-файл.

     - Внимательно проверяйте запросы и ответы на них для программ (M)ASM,
LINK и CREF прежде чем нажать клавишу Return.  Будьте особенно внимательны
при указании дисковода.

     - Программа CREF создает распечатку перекрестных ссылок.

     - Удаляйте ненужные  файлы  с  вашего  диска.  Регулярно  пользуйтесь
программой CHKDSK для проверки  свободного  места  на  диске.  Кроме  того
периодически создавайте резервные копии вашей программы, храните резервную
дискету и копируйте ее заново для последующего программирования.


     ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
     ________________________________________________________________

     4.1. Введите команду MASM и ответьте на запросы  для  ассемблирования
программы по имени TEMPY.ASM с получением файлов LST, OBJ и CRF,  полагая,
что дискета с программой находится на дисководе B.

     4.2. Введите команды для программы TEMPY  (из  вопроса  4.1)  а)  для
выполнения через отладчик DEBUG, б) для  непосредственного  выполнения  из
DOS.

     4.3. Объясните назначение каждого из следующих файлов:  а)  file.BAK,
б) file.ASM, в) file.LST,  г)  file.CRF,  д)  file.OBJ,  е)  file.EXE,  ж)
file.MAP.

     4.4. Напишите две команды для инициализации регистра DS, полагая, что
имя сегмента данных - DATSEG.

     4.5. Составьте ассемблерную программу для:

          - пересылки шест.30 (непосредственное значение) в регистр AL;
          - сдвига содержимого регистра AL на один бит влево (команда SHL)
          - пересылки шест.18 (непосредственное значение) в регистр BL;
          - умножения регистра AL на BL (команда MUL BL).

     Не забывайте команду RET.  В программе нет необходимости определять и
инициализировать сегмент данных. Не забывайте также копировать стандартную
часть  программы  (основу  программы)  и  использовать  редактор  для   ее
развития.  Выполните  ассемблирование  и  компоновку.  Используя  отладчик
DEBUG, проверьте сегмент кодов, регистры и проделайте пошаговое выполнение
(трассировку) программы.

     4.6. Модифицируйте программу из вопроса 4.5 для:

          - определения однобайтовых элементов  (директива  DB)  по  имени
     FLDA, содержащего шест.28, и по имени FLDB, содержащего шест.14;
          - определения двухбайтового элемента  (директива  DW)  по  имени
     FLDC, не имеющего значения;
          - пересылки содержимого поля FLDA в регистр AL и сдвига на  один
     бит;
          - умножения содержимого регистра AL на значение в поле FLDB (MUL
     FLDB);
          - пересылки результата из регистра AX в поле FLDC.

     Для  данной  программы   необходим    сегмент    данных.    Выполните
ассемблирование, компоновку программы и тестирование с  помощью  отладчика
DEBUG.



ГЛАВА 5                                                 Определение данных
__________________________________________________________________________

     Ц е л ь: Показать методам определения  констант  и  рабочих  полей  в
ассемблерной программе.


    ВВЕДЕНИЕ
     ________________________________________________________________

     Сегмент данных предназначен для определения констант, рабочих полей и
областей для ввода-вывода.  В  соответствии  с  имеющимися  директивами  в
ассемблере  разрешено  определение  данных  различной  длины:    например,
директива DB определяет байт, а директива  DW  oпределяет  слово.  Элемент
данных  может  содержать  непосредственное   значение    или    константу,
определенную как символьная строка или как числовое значение.
     Другим  способом  определения  константы  является   непосредственное
значение, т.е. указанное прямо в ассемблерной команде, например:

               MOV  AL,20H

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



     ДИРЕКТИВЫ ОПРЕДЕЛЕНИЯ ДАННЫХ
     ________________________________________________________________

     Ассемблер обеспечивает два  способа  определения  данных:  во-первых,
через указание длины данных и, во-вторых, по  их  cодержимому.  Рассмотрим
основной формат определения данных:

               [имя]     Dn   выражение

          Имя элемента данных не обязательно (это указывается  квадратными
     скобками), но если в программе имеются ссылки на  некоторый  элемент,
     то это делается посредством имени. Правила написания имен приведены в
     разделе "Формат кодирования" в главе 3.
          Для определения элементов данных имеются следующие директивы: DB
     (байт), DW (слово), DD (двойное слово), DQ (учетверенное слово) и  DT
     (десять байт).
          Выражение может содержать константу, например:

               FLD1      DB   25

     или знак вопроса для неопределенного значения, например

               FLDB      DB   ?

     Выражение может содержать несколько констант, разделенных запятыми  и
ограниченными только длиной строки:

               FLD3      DB   11, 12, 13, 14, 15, 16, ...

Ассемблер определяет эти константы в виде последовательности cмежных байт.
Ссылка по имени FLD3 указывает на первую константу, 11,  по  FLD3+1  -  на
вторую, 12. (FLD3 можно представить как FLD3+0). Например команда

               MOV  AL,FLD3+3

загружает в регистр AL значение 14 (шест. 0E).  Выражение допускает  также
повторение константы в следующем формате:

               [имя]     Dn  число-повторений DUP (выражение) ...

     Следующие три примера иллюстрируют повторение:

               DW   10 DUP(?)      ;Десять неопределенных слов
               DB   5 DUP(14)      ;Пять байт, содержащих шест.14
               DB   3 DUP(4 DUP(8));Двенадцать восьмерок

     В третьем примере сначала  генерируется  четыре  копии  десятичной  8
(8888), и затем это значение повторяется  три  раза,  давая  в  pезультате
двенадцать восьмерок.
     Выражение может содержать символьную строку или числовую константу.


                            Символьные строки
                           -------------------
     Символьная  строка  используются  для  описания  данных,  таких  как,
например, имена людей или заголовки страниц.  Содержимое строки oтмечается
одиночными кавычками,  например,  'PC'  или  двойными  кавычками  -  "PC".
Ассемблер переводит символьные строки в объектный код  в  обычном  формате
ASCII.
     Символьная  строка  определяется  только  директивой  DB,  в  котоpой
указывается более двух  символов  в  нормальной  последовательности  слева
направо.  Следовательно, директива DB представляет  единственно  возможный
формат для  определения  символьных  данных.  На  рис.5.1   приведен   ряд
примеров.

__________________________________________________________________________

                                 page    60,132
                         TITLE   EXDEF   (EXE) Определение данных
0000                     DATASG  SEGMENT PARA 'Data'

                         ;              Определение байта - DB:
                         ;              ----------------------
0000  ??                 FLD1DB  DB     ?         ;Не инициализировано
0001  50 65 72 73 6F 6E  FLD2DB  DB     'Personal Computer' ;Сим. строка
      61 6C 20 43 6F 6D
      70 75 74 65 72
0012  20                 FLD3DB  DB     32        ;Десятичная константа
0013  20                 FLD4DB  DB     20H       ;Шест. константа
0014  59                 FLD5DB  DB     01011001B ;Двоичная константа
0015  01 4A 41 4E 02 46  FLD6DB  DB     01,'JAN',02,'FEB',03,'MAR' ;Таб-ца
      45 42 03 4D 41 52
0021  33 32 36 35 34     FLD7DB  DB     '32654'   ;Символьные числа
0026     0A [ 00 ]       FLD8DB  DB     10 DUP(0) ;Десять нулей

                         ;              Определение слова - DW:
                         ;              ----------------------
0030  FFF0               FLD1DW  DW     0FFF0H    ;Шест. константа
0032  0059               FLD2DW  DW     01011001B ;Двоичная константа
0034  0021 R             FLD3DW  DW     FLD7DB    ;Адресная константа
0036  0003 0004 0007     FLD4DW  DW     3,4,7,8,9 ;Пять констант
      0008 0009
0040     05 [ 0000 ]     FLD5DW  DW     5 DUP(0)  ;Пять нулей

                         ;              Определение двойного слова - DD:
                         ;              -------------------------------
004A  ????????           FLD1DD  DD     ?         ;Не инициализировано
004E  43 50 00 00        FLD2DD  DD     'PC'      ;Символьная строка
0052  3C 7F 00 00        FLD3DD  DD     32572     ;Десятичное значение
0056  11 00 00 00        FLD4DD  DD     FLD3DB - FLD2DB ;Разность адресов
005A  0E 00 00 00 31 00  FLD5DD  DD     14,49     ;Две константы
      00 00
                         ;              Определ. учетверенного слова - DQ:
                         ;              ---------------------------------
0062  ????????????????   FLD1DQ  DQ     ?          ;Не инициализировано
006A  47 4D 00 00 00 00  FLD2DQ  DQ     04D47H     ;Шест. константа
      00 00
0072  3C 7F 00 00 00 00  FLD3DQ  DQ     32572      ;Десятич. константа
      00 00
                         ;              Определение десяти байт - DT:
                         ;              ----------------------------
007A  ?????????????????? FLD1DT  DT     ?          ;Не инициализировано
      ??
0084  43 50 00 00 00 00  FLD2DT  DT     'PC'       ;Символьная строка
      00 00 00 00

008E                     DATASG  ENDS
                                 END
------------------------------------------------------------------------
Segments and Groups:
             N a m e     Size   Align    Combine   Class
DATASG . . . . . . . . . 008E   PARA     NONE      'DATA'

Symbols:
             N a m e      Type           Value    Attr
FLD1DB . . . . . . . . . L BYTE          0000     DATASG
FLD1DD . . . . . . . . . L DWORD         004A     DATASG
FLD1DQ . . . . . . . . . L QWORD         0062     DATASG
FLD1DT . . . . . . . . . L TBYTE         007A     DATASG
FLD1DW . . . . . . . . . L WORD          0030     DATASG
FLD2DB . . . . . . . . . L BYTE          0001     DATASG
FLD2DD . . . . . . . . . L DWORD         004E     DATASG
FLD2DQ . . . . . . . . . L QWORD         006A     DATASG
FLD2DT . . . . . . . . . L TBYTE         0084     DATASG
FLD2DW . . . . . . . . . L WORD          0032     DATASG
FLD3DB . . . . . . . . . L BYTE          0012     DATASG
FLD3DD . . . . . . . . . L DWORD         0052     DATASG
FLD3DQ . . . . . . . . . L QWORD         0072     DATASG
FLD3DW . . . . . . . . . L WORD          0034     DATASG
FLD4DB . . . . . . . . . L BYTE          0013     DATASG
FLD4DD . . . . . . . . . L DWORD         0056     DATASG
FLD4DW . . . . . . . . . L WORD          0036     DATASG
FLD5DB . . . . . . . . . L BYTE          0014     DATASG
FLD5DD . . . . . . . . . L DWORD         005A     DATASG
FLD5DW . . . . . . . . . L WORD          0040     DATASG  Length =0005
FLD6DB . . . . . . . . . L BYTE          0015     DATASG
FLD7DB . . . . . . . . . L BYTE          0021     DATASG
FLD8DB . . . . . . . . . L BYTE          0026     DATASG  Length =000A
__________________________________________________________________________

     Рис.5.1. Определение символьных строк и числовых величин.


                            Числовые константы
                           --------------------
     Числовые константы используются  для  арифметических  величин  и  для
aдресов памяти.  Для описания константы  кавычки  не  ставятся.  Ассемблер
преобразует все числовые константы в шестнадцатеричные и записывает  байты
в объектном коде в  обратной  последовательности  -  справа  налево.  Ниже
показаны различные числовые форматы.

     Д е с я т и ч н ы й   ф о р м а т.    Десятичный   формат   допускает
десятичные цифры от 0 до 9 и  обозначается  последней  буквой  D,  которую
можно не указывать, например, 125 или 125D.  Несмотря на то, что ассемблер
позволяет кодирование в десятичном формате, он преобразует эти значения  в
шест. объектный код.  Например, десятичное число 125 преобразуется в шест.
7D.

     Ш е с т н а д ц а т и р и ч н ы й   ф о р м а т.    Шестнадцатиричный
формат допускает шест. цифры от 0 до F и обозначается последней буквой  H.
Так как ассемблер полагает, что  с  буквы  начинаются  идентификаторы,  то
первой цифрой шест. константы должна быть цифра от 0 до 9.  Например,  2EH
или 0FFFH, которые ассемблер преобразует соответственно в 2E и FF0F (байты
во  втором  примере  записываются   в    объектный    код    в    обратной
последовательности).

     Д в о и ч н ы й  ф о р м а т.   Двоичный  формат  допускает  двоичные
цифры 0 и 1 и обозначается последней  буквой  B.  Двоичный  формат  обычно
используется для более четкого представления битовых значений в логических
командах AND, OR, XOR и TEST.  Десятичное 12, шест. C и двоичное 1100B все
генерируют один  и  тот  же  код:  шест.  0C  или  двоичное  0000  1100  в
зависимости от того, как вы рассматриваете содержимое байта.

     В о с ь м е р и ч н ы й  ф о р м а т.   Восьмеричный формат допускает
восьмеричные цифры  от  0  до  7  и обозначается последней буквой Q или O,
например, 253Q. На сегодня восьмеричный формат используется весьма редко.

     Д е с я т и ч н ы й  ф о р м а т  с  п л а в а ю щ е й   т о ч к о й.
Этот формат поддерживается только ассемблером МASM.

     При записи символьных  и  числовых  констант  следует  помнить,  что,
например, символьная константа, определенная  как  DB  '12',  представляет
символы ASCII и генерирует шест.3132,  а числовая константа,  oпределенная
как DB 12, представляет двоичное число и генерирует шест.0C.
     Рис.5.1 иллюстрирует  директивы  для определения различных символьных
строк и числовых констант.  Сегмент данных  был  ассемблирован  для  того,
чтобы показать сгенерированный объектный код (слева).


     ДИРЕКТИВА ОПРЕДЕЛЕНИЯ БАЙТА (DB)
     ________________________________________________________________

     Из  различных  директив,  определяющих  элементы  данных,    наиболее
полезной является DB (определить байт).  Символьное выражение в  диpективе
DB может содержать строку символов любой длины,  вплоть  до  конца  строки
(см. FLD2DB и FLD7DB на рис.5.1).  Обратите внимание, что константа FLD2DB
содержит  символьную строку 'Personal Computer'.  Объектный код показывает
символы кода ASCII для каждого байта. Шест.20 представляет символ пробела.
     Числовое выражение в директиве DB  может  содержать  одну  или  более
однобайтовых  констант.  Один  байт  выражается  двумя   шест.    цифpами.
Наибольшее положительное шест. число в одном байте это 7F,  все  "большие"
числа от 80  до  FF  представляют  отрицательные  значения.  В  десятичном
исчислении эти пределы выражаются числами +127 и -128.
     В примере на рис.5.1 числовыми константами являются  FLD3DB,  FLD4DB,
FLD5DB  и  FLD8DB.  Поле FLD6DB представляет смесь из числовых и строковых


Страницы книги разрезаны по 70Кб, чтобы они не были гигантскими и недолго грузились, так что нажмите "Далее" чтобы читать дальше.
Назад  Далее

Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS