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

ВИДЕОКУРС ВЗЛОМ
обновлён 2 декабря!


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

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

Под редакцией Еpшова В.Г.
констант, используемых для построения таблицы.


     ДИРЕКТИВА ОПРЕДЕЛЕНИЯ СЛОВА (DW)
     ________________________________________________________________

     Директива DW определяет элементы, которые имеют длину  в  одно  слово
(два байта). Символьное выражение в DW ограничено двумя символами, которые
ассемблер  представляет  в  объектном  коде  так,  что,  например,    'PC'
становится 'CP'.  Для определения  символьных  строк  директива  DW  имеет
ограниченное применение.
     Числовое выражение в DW может содержать одно или  более  двухбайтовых
констант.  Два байта представляются  четырьмя  шест.  цифрами.  Наибольшее
положительное шест. число в двух байтах это 7FFF; все "большие"  числа  от
8000 до FFFF представляют отрицательные значения.  В десятичном исчислении
эти пределы выражаются числами +32767 и -32768.
     В примере  на  рис.5.1  поля  FLD1DW  и  FLD2DW  определяют  числовые
константы.  Поле  FLD3DW  определяет  адрес  - в данном случае смещение на
адрес FLD7DB.  В результате генерируется объектный код 0021 (R  обозначает
перемещаемость).  Проверяя выше по рисунку, видно, что относительный адрес
поля FLD7DB действительно 0021.
     Поле FLD4DW определяет таблицу из пяти  числовых  констант.  Заметим,
что объектный код для каждой константы  имеет  длину  в  oдно  слово  (два
байта).
     Для форматов директив DW, DD и DQ ассемблер преобразует  константы  в
шест. объектный код, но  записывает  его  в  обратной  последовательности.
Таким образом десятичное значение  12345  преобразуется  в  шест.3039,  но
записывается в объектном коде как 3930.


     ДИРЕКТИВА ОПРЕДЕЛЕНИЯ ДВОЙНОГО СЛОВА (DD)
     ________________________________________________________________

     Директива DD определяет элементы, которые имеют  длину  в  два  cлова
(четыре  байта).  Числовое  выражение  может  содержать  одну  или   более
констант, каждая из которых имеет  максимум  четыре  байта  (восемь  шест.
цифр). Наибольшее положительное шест. число в четырех байтах это 7FFFFFFF;
все "большие" числа от 80000000  до  FFFFFFFF  представляют  отрицательные
значения.  В  десятичном  исчислении  эти  пределы   выражаются    числами
+2147483647 и -2147483648.
     В примере на рис.5.1 поле FLD3DD  определяет  числовую  константу.  В
поле  FLD4DD  генерируется  разница между двумя адресами,  в данном случае
результатом  является  длина  поля  FLD2DB.  Поле  FLD5DD  определяет  две
числовые константы.
     Ассемблер преобразует все числовые константы в директиве DD  в  шест.
представление, но записывает объектный код в обратной  последовательности.
Таким образом десятичное значение 12345 преобразуется в шест.00003039,  но
записывается в oбъектном коде как 39300000.
     Символьное  выражение  директивы  DD  ограничено  двумя    символами.
Ассемблер преобразует символы и выравнивает  их  слева  в  четырехбайтовом
двойном слове, как показано в поле FLD2DD в объектном коде.


     ДИРЕКТИВА ОПРЕДЕЛЕНИЯ УЧЕТВЕРЕННОГО СЛОВА (DQ)
     ________________________________________________________________

     Директива DQ определяет элементы, имеющие длину четыре слова  (восемь
байт).  Числовое выражение может содержать одну или более констант, каждая
из которых имеет максимум  восемь  байт  или  16  шест.  цифр.  Наибольшее
положительное  шест.  число  -  это  семерка  и  15 цифр F.  Для получения
представления о величине этого числа,  покажем,  что шест.  1 и  15  нулей
эквивалентен следующему десятичному числу:

               1152921504606846976

     В примере на рис. 5.1 поля  FLD2DQ  и  FLD3DQ  иллюстрируют  числовые
значения.  Ассемблер преобразует все числовые константы в директиве  DQ  в
шест.  представление,  но  записывает    объектный    код    в    обратной
последовательности, как и в директивах DD и DW.
     Обработка ассемблером символьных  строк  в  директиве  DQ  aналогично
директивам DD и DW.


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

     Директива DT определяет элементы данных, имеющие длину в десять байт.
Назначение этой директивы связано с "упакованными  десятичными"  числовыми
величинами (см. гл.13).  По директиве DT генерируются различные константы,
в  зависимости  от  версии  ассемблера;  для   практического    применения
ознакомьтесь с руководством по вашему aссемблера.
     На рис.5.1   приведены   примеры  директивы  DT  для  неопределенного
элемента и для двухсимвольной константы.
     Программа на рис.5.1 содержит только сегмент данных.  Xотя  асcемблер
не  выдает  сообщений  об  ошибках,  в  таблице    LINK    MAP    появится
предупреждение: "Warning: No STACK Segment" (Предупреждение: не  определен
сегмент стека), а компоновщик LINK выдаст "There were 1  errors  detected"
(Обнаружена 1 ошибка).  Несмотря на это предупреждение можно  использовать
отладчик DEBUG для просмотра объектного кода, как показано на рис.5.2.

__________________________________________________________________________

D:\ D>DEBUG D:EXDEF.EXE
-D
1421:0000 00 50 65 72 73 6F 6E 61-6C 20 43 6F 6D 70 75 74 .Personal Comput
1421:0010 65 72 20 20 59 01 4A 41-4E 02 46 45 42 03 4D 41 .er Y.JAN.FEB.MA
1421:0020 52 33 32 36 35 34 00 00-00 00 00 00 00 00 00 00 R32654..........
1421:0030 F0 FF 59 00 21 00 03 00-04 00 07 00 08 00 09 00 ..Y.!...........
1421:0040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 43 50 ..............CP
1421:0050 00 00 3C 7F 00 00 11 00-00 00 0E 00 00 00 31 00 ..<...........1.
1421:0060 00 00 00 00 00 00 00 00-00 00 47 4D 00 00 00 00 ..........GM....
1421:0070 00 00 3C 7F 00 00 00 00-00 00 00 00 00 00 00 00 ..<.............
-D
1421:0080 00 00 00 00 43 50 00 00-00 00 00 00 00 00 33 33 ....CP........33
1421:0090 3E 36 33 33 73 00 00 00-0A 0E 00 00 3E 63 63 30 >633s.......>cc0
1421:00A0 1C 06 63 63 3E 00 00 00-0A 0E 00 00 FF DB 99 18 ..cc>...........
1421:00B0 18 18 18 18 3C 00 00 00-0A 0E 00 00 63 63 63 63 ....<.......cccc
1421:00C0 63 63 63 63 3E 00 00 00-0A 0E 00 00 C3 C3 C3 C3 cccc>...........
1421:00D0 C3 C3 66 3C 18 00 00 00-0A 0E 00 00 C3 C3 C3 C3 ..f<............
1421:00E0 DB DB FF 66 66 00 00 00-0A 0E 00 00 C3 C3 66 3C ...ff.........f<
1421:00F0 18 3C 66 C3 C3 00 00 00-0A 0E 00 00 C3 C3 C3 66 .     НЕПОСРЕДСТВЕННЫЕ ОПЕРАНДЫ
     ________________________________________________________________

     На рис. 2.1 в главе 2 было  показано  использование  непосредственных
операндов. Команда

               MOV   AX,0123H

пересылает непосредственную шест. константу 0123 в регистр AX. Трехбайтный
объектный код для этой команды есть B82301, где B8  обозначает  "переслать
непосредственное значение в регистр AX", a следующие  два  байта  содержат
само значение.  Многие команды  имеют  два  операнда:  первый  может  быть
регистр или адрес памяти, а второй - непосредственная константа.

     Использование  непосредственного  операнда  более  эффективно,    чем
oпределение числовой константы в сегменте данных и организация  cсылки  на
нее в операнде команды MOV, например,

               Сегмент данных:     AMT1 DW   0123H
               Сегмент кодов:           MOV  AX,AMT1


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

               MOV  AL,0123H  (ошибка)

однако, если непосредственный операнд короче, чем получающий операнд,  как
в следующем примере

               ADD  AX,25H    (нет ошибки)

то ассемблер расширяет непосредственный  операнд  до  двух  байт,  0025  и
записывает объектный код в виде 2500.


                         Непосредственные форматы
                        --------------------------
     Непосредственная константа может  быть  шестнадцатиричной,  напpимер,
0123H;  десятичной,  например,  291  (которую  ассемблер  конвертирует   в
шест.0123); или двоичной, например, 100100011В  (которая  преобразуется  в
шест. 0123).
     Ниже  приведен  список  команд,  которые  допускают  непосредственные
операнды:

     Команды пересылки и сравнения:     MOV, CMP.
     Арифметические команды:            ADC, ADD, SBB, SUB.
     Команды сдвига:                    RCL, RCR, ROL, ROR, SHL, SAR, SHR.
     Логические команды:                AND, OR, TEST, XOR.

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

__________________________________________________________________________

                              page    60,132
                      TITLE   EXIMM   (EXE) Пример непосредств. операндов
                      ;       (Кодируется для ассемблирования,
                      ;                   но не для выполнения)
0000                  DATASG  SEGMENT PARA 'Data'
0000 ??               FLD1    DB      ?
0001 ????             FLD2    DW      ?
0003                  DATASG  ENDS

0000                  CODESG  SEGMENT PARA 'Code'
0000                  BEGIN   PROC    FAR
                              ASSUME  CS:CODESG,DS:DATASG

                      ;               Операции пересылки и сравнения:
                      ;               ------------------------------
0000 BB 0113                  MOV     BX,275   ;Пересылка
0003 3C 19                    CMP     AL,H     ;Сравнение

                      ;               Арифметические операции:
                      ;               -----------------------
0005 14 05                    ADC     AL,5     ;Сложение с переносом
0007 80 C7 0C                 ADD     BH,12    ;Сложение
000A 1C 05                    SBB     AL,5     ;Вычитание с заемом
000C 80 2E 000 R 05           SUB     FLD1,5   ;Вычитание

                      ;               Ротация и сдвиг (только на 1 бит):
                      ;               ---------------------------------
0011 D0 D3                    RCL     BL,1     ;Ротация влево с переносом
0013 D0 DC                    RCR     AH,1     ;Ротация вправо с переносом
0015 D1 06 0001 R             ROL     FID2,1   ;Ротация влево
0019 D0 C8                    ROR     AL,1     ;Ротация вправо
001B D1 E1                    SAL     CX,1     ;Сдвиг влево
001D D1 FB                    SAR     BX,1     ;Арифм. сдвиг вправо
001F D0 2E 0000 R             SHR     FLD1,1   ;Сдвиг вправо

                      ;               Логические операции:
                      ;               -------------------
0023 24 2C                    AND     AL,00101100B ;AND (регистр)
0025 80 CF 2A                 OR      BH,2AH   ;OR (регистр)
0028 F6 C3 7A                 TEST    BL,7AH   ;TEST (регистр)
002B 80 36 0000 R 23          OR      FLD1,23H ;XOR (память)
0030                  BEGIN   ENDP
0030                  CODESG  ENDS
                              END
__________________________________________________________________________

     Рис.5.3. Команды с непосредственными данными.


     Для создания  элементов,  длиннее  чем два байта,  можно использовать
цикл (см. гл.7) или строковые команды (см. гл.11).


     ДИРЕКТИВА EQU
     ________________________________________________________________

     Директива EQU не определяет элемент данных, но  определяет  значение,
которое  может  быть  использовано  для  постановки  в  других   командах.
Предположим, что в сегменте данных закодирована следующая директива EQU:

               TIMES     EQU  10

Имя, в данном случае TIMES, может быть  представлено  любым  допустимым  в
ассемблере  именем.  Теперь,  в  какой-бы  команде  или    директиве    не
использовалось слово TIMES  ассемблер  подставит  значение  10.  Например,
ассемблер преобразует директиву

               FIELDA    DB   TIMES DUP (?)
в
               FIELDA    DB   10 DUP (?)

     Имя, связанное с некоторым значением с помощью директивы  EQU,  может
использоваться в командах, например:

               COUNTR    EQU  05
                         ...
                         MOV  CX,COUNTR

     Ассемблер заменяет имя COUNTR в команде MOV на значение 05,  cоздавая
операнд с непосредственным значением, как если бы было закодировано

               MOV  CX,05     ;Ассемблер подставляет 05

     Здесь преимущество  директивы  EQU  заключается  в  том,  что  многие
команды могут использовать значение, определенное по  имени  COUNTR.  Если
это значение  должно  быть  изменено,  то  изменению  подлежит  лишь  одна
директива EQU.  Естественно, что использование директивы EQU разумно  лишь
там, где подстановка имеет смысл для ассемблера.  В  директиве  EQU  можно
использовать символические имена:

               1.   TP   EQU  TOTALPAY
               2.   MPY  EQU  MUL

Первый пример предполагает, что в сегменте данных программы опpеделено имя
TOTALPAY.  Для любой команды, содержащей операнд TP, ассемблер заменит его
на адрес TOTALPAY.  Второй пример показывает возможность  использования  в
программе слова MPY вместо обычного мнемокода MUL.


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

     - Имена элементов данных в  программе  должны  быть  уникальны  и  по
возможности наглядны. Например, элемент для зарплаты служащего может иметь
имя EMPWAGE.

     - Для определения символьных строк используйте директиву DB, так  как
ее формат допускает строки длиннее двух байт и формирует их  в  нормальной
последовательности (слева-направо).

     - Будьте  внимательны  при  указании  десятичных  и  шест.  значений.
Сравните, например, сложение содержимого регистра AX с десятичным 25  и  с
шест. 25:

               ADD  AX,25     ;Прибавить 25
               ADD  AX,25H    ;Прибавить 37

     - Помните, что директивы DW, DD и DQ записывают числовое  значение  в
объектном коде в обратной последовательности байт.

     - Используйте элементы DB для операций с полурегистрами (AL, AH, BL и
т.д.) и DW для операций с полными регистрами (AX, BX, CX и т.д.). Числовые
элементы, определенные директивами DD и DQ имеют специальное применение.

     -  Следите  за  соответствием  непосредственных  операндов    размеру
регистра:  однобайтовая  константа  -  однобайтовый  регистр  (AL,    BH),
двухбайтовая константа - полный регистр (AX, BX).


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

     5.1.  Какова  длина  в  байтах  для  элементов  данных,  определенных
директивами: а) DW, б) DD, в) DT, г) DB, д) DQ?

     5.2.  Определите  символьную  строку  по  имени  TITLE1,   содержащую
константу RGB Electronics.

     5.3. Определите следующие числовые  значения  в  элементах  данных  с
именами от FLDA до FLDE:

          a)  четырехбайтовый  элемент,  содержащий    шест.    эквивалент
     десятичного числа 115;
          b) однобайтовый элемент, содержащий шест. эквивалент десятичного
     числа 25;
          c) двухбайтовый элемент, содержащий неопределенное значение;
          d)  однобайтовый  элемент,  содержащий    двоичной    эквивалент
     десятичного числа 25;
          e) директиву DW, содержащую последовательные  значения  16,  19,
     20, 27, 30.

     5.4. Покажите сгенерированный шест. объектный код для а) DB '26' и б)
DB 26.

     5.5. Определите ассемблерный шест. объектный код для а) DB 26H, б) DW
2645H, в) DD 25733AH, г) DQ 25733AH.

     5.6. Закодируйте следующие команды с непосредственными операндами:

          а) загрузить 320 в регистр AX;
          б) сравнить поле FLDB с нулем;
          в) прибавить шест. 40 к содержимому регистра BX;
          г) вычесть шест. 40 из регистра CX;
          д) сдвинуть содержимое поля FLDB на один бит влево;
          е) сдвинуть содержимое регистра CH на один бит вправо.

     5.7. Введите и ассемблируйте элементы данных и  команды  из  вопросов
5.2,  5.3 и 5.6.  Стек для этого упражнения не требуется. Также не следует
выполнять компоновку.  Для  проверки  ассемблированного  кода  используйте
отладчик   DEBUG.  Распечатайте  LST-файл  (листинг),  если  в  результате
ассемблирования не будет сообщений об ошибках.  Не забудьте  команду  MODE
LPT1:132,6 для установки ширины печати.



ГЛАВА 6                                             Программы в COM-файлах
__________________________________________________________________________

     Ц е л ь: Объяснить назначение и использование  COM-файлов  и  перевод
ассемблерных программ в формат COM-файлов.


     ВВЕДЕНИЕ
     ________________________________________________________________

     До сих пор  вы  писали,  ассемблировали  и  выполняли  програм  мы  в
EXE-формате.  Компановщик LINK автоматически генерирует особый формат  для
EXE-файлов, в котором присутствует специальный начальный блок  (заголовок)
pазмером не менее 512 байт.  (В гл.22 рассматривается содержимое начальных
блоков).
     Для  выполнения  можно  также  создавать  COM-файлы.  Примером  часто
используемого COM-файла  является  COMMAND.COM.  Программа  EXE2BIN.COM  в
оперативной системе DOS преобразует EXE-файлы в COM-файлы.  Фактически эта
программа  создает  BIN  (двоичный)  файл,  поэтому  она   и    называется
"преобразователь  EXE  в  Вin  (EXE-to-BIN)".  Выходной  Вin-файл    можно
переименовать в COM-файл.


     РАЗЛИЧИЯ МЕЖДУ ПРОГРАММАМИ В EXE и COM-файлах
     ________________________________________________________________

     Несмотря  на  то,  что  EXE2BIN  преобразует  EXE-файл  в   COM-файл,
cуществуют  определенные  различия  между  программой,  выполняемой    как
EXE-файл и программой, выполняемой как COM-файл.

     Р а з м е р  п р о г р а м м ы.   EXE-программа  может  иметь   любой
размер, в то время как COM-файл ограничен размером одного  сегмента  и  не
превышает 64К.  COM-файл всегда меньше, чем соответствующий EXE-файл; одна
из причин этого - отсутствие в COM-файле  512-байтового  начального  блока
EXE-файла.

     С е г м е н т  с т е к а. В EXE-программе определяется сегмент стека,
в то время как COM-программа генерирует стек автоматически.  Таким образом
при  создании  ассемблерной  программы,  которая  будет  преобразована   в
COM-файл, стек должен быть опущен.

     С е г м е н т  д а н н ы х.   В  EXE  программе  обычно  определяется
сегмент данных, а регистр DS инициализируется адресом  этого  сегмента.  В
COM-программе все данные должны быть  определены  в  сегменте  кода.  Ниже
будет показан простой способ решения этого вопроса.

     И н и ц и а л и з а ц и я.  EXE-программа записывает нулевое слово  в
стек и инициализирует регистр DS. Так как COM-программа не имеет ни стека,
ни сегмента данных, то эти шаги отсутствуют.  Когда COM-программа начинает
работать, все сегментные регистры  содержат  адрес  префикса  программного
сегмента (PSP), - 256-байтового (шест. 100) блока,  который  резервируется
операционной системой DOS непосредственно перед COM или EXE  программой  в
памяти.  Так как адресация начинается с шест. смещения 100 от начала  PSP,
то в программе после оператора SEGMENT кодируется директива ORG 100H.

     О б р а б о т к а.  Для программ в EXE  и  COM  форматах  выполняется
ассемблирование  для  получения  OBJ-файла,  и  компановка  для  получения
EXE-файла. Если программа создается для выполнения как EXE-файл, то ее уже
можно выполнить.  Если же программа создается для выполнения как COM-файл,
то компановщиком будет выдано сообщение:

                        Warning: No STACK Segment
               (Предупреждение: сегмент стека не определен)

     Это  сообщение  можно  игнорировать,  так  как  определение  стека  в
программе не  предполагалось.  Для  преобразования  EXE-файла  в  COM-файл
используется  программа  EXE2BIN.  Предположим,  что  EXE2BIN  имеется  на
дисководе A, а скомпонованный файл по имени CALC.EXE  -  на  дисководе  B.
Введите

               EXE2BIN B:CALC,B:CALC.COM

     Так как первый операнд всегда предполагает  EXE  файл,  то  можно  не
кодировать тип EXE.  Второй операнд может иметь другое имя (не  CALC.COM).
Если не указывать тип COM, то EXE2BIN примет по умолчанию тип BIN, который
впоследствии можно переименовать в  COM.  После  того  как  преобразование
будет выполнено можно удалить OBJ- и EXE-файлы.
     Если исходная программа написана для EXE-формата, то можно, используя
редактор, заменить команды в исходном тексте для COM файла.


     ПРИМЕР COM-ПРОГРАММЫ
     ________________________________________________________________

     Программа EXCOM1,  приведенная на рис.6.1,  аналогична  программе  на
рис.4.3, но изменена согласно требований COM-формата. Обратите внимание на
следующие изменения в этой COM-программе:

          - Стек и сегмент данных отсутствует.
          - Оператор ASSUME указывает ассемблеру установить  относительные
     адреса с начала сегмента кодов. Регистр CS также содержит этот адрес,
     являющийся к тому же адресом префикса  программного  сегмента  (PSP).
     Директива  ORG  служит  для  резервирования  100  (шест.)  байт    от
     начального адреса под PSP.
          - Директива  ORG  100H  устанавливает  относительный  адрес  для
     начала выполнения программы.  Программный загрузчик  использует  этот
     адрес для командного указателя.
          - Команда JMP используется для  обхода  данных,  определенных  в
     программе.

     Ниже показаны шаги для обработки и выполнения этой программы:

               MASM [ответы на запросы обычные]
               LINK [ответы на запросы обычные]
               EXE2BIN B:EXCOM1,B:EXCOM1.COM
               DEL B:EXCOM1.OBJ,B:EXCOM1.EXE (удаление OBJ и EXE-файлов)

     Размеры EXE- и COM-программ - 788 и 20 байт соответственно.  Учитывая
такую эффективность  COM-файлов,  рекомендуется  все  небольшие  программы
создавать для COM-формата.  Для трассировки выполнения программы от начала
(но не включая) команды RET введите DEBUG B:EXCOM1.COM.
     Некоторые программисты кодируют элементы данных после команд так, что
первая команда  JMP  не  требуется.  Кодирование  элементов  данных  перед
командами позволяет ускорить процесс ассемблирования и является методикой,
рекомендуемой в руководстве по ассемблеру.

__________________________________________________________________________

        page 60,132
TITLE   XCOM1  COM-программа для пересылки и сложения
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG,ES:CODESG
        ORG     100H            ;Начало в конце PSP
BEGIN:  JMP     MAIN            ;Обход через данные
; ---------------------------------------------------
FLDA    DW      250             ;Определение данных
FLDB    DW      125
FLDC    DW      ?
; ---------------------------------------------------
MAIN    PROC    NEAR
        MOV     AX,FLDA         ;Переслать 0250 в AX
        ADD     AX,FLDB         ;Прибавить 0125 к AX
        MOV     FLDC,AX         ;Записать сумму в FLDC
        RET                     ;Вернуться в DOS
MAIN    ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.6.1. Пример COM-программы.


     СТЕК ДЛЯ COM-ПРОГРАММЫ
     ________________________________________________________________

     Для COM-файла  DOS  автоматически  определяет  стек  и  устанавливает
oдинаковый общий сегментный адрес во всех  четырех  сегментных  pегистрах.
Если для программы размер сегмента в  64К  является  достаточным,  то  DOS
устанавливает в регистре SP адрес конца cегмента -  шест.FFFE.  Это  будет
верх стека. Если 64К байтовый сегмент не имеет достаточно места для стека,
то DOS устанавливает стек в конце памяти.  В обоих случаях DOS  записывает
затем в стек нулевое слово.
     Возможность  использования  стека  зависит  от  размера  программы  и
ограниченности памяти. С помощью команды DIR можно определить pазмер файла
и вычислить необходимое пространство для стека.
     Все  небольшие  программы  в  этой  книге  в  основном  расчитаны  на
COM-формат.


     ОСОБЕННОСТЬ ОТЛАДКИ
     ________________________________________________________________

     Несоблюдение хотя бы одного требования  COM-формата  может  послужить
причиной неправильной работы программы.  Если EXE2BIN обнаруживает oшибку,
то выдается сообщение о невозможности преобразования  файла  без  указания
конкретной причины.  Необходимо проверить в этом случае директивы SEGMENT,
ASSUME и END.  Если опущен ORG 100H, то на данные в префиксе  программного
сегмента  будут  установлены  неправильные  ссылки    с    непредсказуемым
результатом при выполнении.
     При  выполнении  COM-программы  под  управлением   отладчика    DEBUG
необходимо использовать команду D CS:100 для просмотра данных и команд. Не
следует выполнять в отладчике команду RET;  предпочтительнее  использовать
команду Q отладчика.  Некоторые программисты  используют  INT  20H  вместо
команды RET.
     Попытка выполнить EXE-модуль программы, написанной  для  COM-формата,
не имеет успеха.


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

     - Объем COM-файла ограничен 64К.

     - COM-файл меньше, чем соответствующий EXE-файл.

     - Программа, написанная для  выполнения  в  COM-формате  не  содержит
стека и сегмента данных и не требует инициализации регистра DS.

     - Программа,  написанная  для  выполнения  в  COM-формате  использует
директиву ORG 100H после директивы SEGMENT для выполнения с  адреса  после
префикса программного сегмента.

     - Программа EXE2BIN преобразует EXE-файл  в  COM-файл,  обусловленный
указанием типа COM во втором операнде.

     - Операционная система DOS определяет стек для  COM-программы  или  в
конце программы, если позволяет размер, или в конце памяти.


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

     6.1. Каков максимальный размер COM-файла?

     6.2. Какие сегменты  можно  определить  в  программе,  которая  будет
преобразована в COM-файл?

     6.3. Как  обходится  COM-файл  при  выполнении  с  фактом  отсутствия
определения стека?

     6.4. Программа  в  результате  компановки  получала  имя  SAMPLE.EXE.
Напишите команду DOS для преобразования ее в COM-файл.

     6.5. Измените программу из вопроса 4.6 для  COM-формата,  обработайте
ее и выполните под управлением отладчика DEBUG.



ГЛАВА 7                                     Логика и Организация Программы
__________________________________________________________________________

     Ц е л ь: Раскрыть механизм передачи управления в программе  (циклы  и
переходы) для логических сравнений и программной организации.


     ВВЕДЕНИЕ
     ________________________________________________________________

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

               Безусловный переход:  JMP
               Цикл:                 LOOP
               Условный переход:     Jnnn (больше,меньше,равно)
               Вызов процедуры:      CALL

     Заметим, что имеется три типа адресов: SHORT, NEAR и  FAR.  Адресация
SHORT используется при циклах, условных пеpеходах и некоторых  безусловных
переходах. Адресация NEAR и FAR используется для вызовов процедур (CALL) и
безусловных переходов, которые не квалифицируются ,  как  SHORT.  Все  три
типа передачи управления воздействуют на содержимое регистра IP;  тип  FAR
также изменяет регистр CS.


     КОМАНДА JMP
     ________________________________________________________________

     Одной из команд обычно используемых для передачи управления  является
команда JMP.  Эта команда выполняет безусловный переход, т.е. обеспечивает
передачу управления при любых обстоятельствах.
     В COM-программе на рис. 7.1 используется команда JMP.  В pегистры AX,
BX, и CX загружается значение 1, и затем  в  цикле  выполняются  следующие
операции:

               прибавить 1 к регистру AX,
               прибавить AX к BX,
               удвоить значение в регистре CX.

Повторение  цикла  приводит  к  увеличению  содержимого    регистра    AX:
1,2,3,4..., регистра BX: 1,3,6,10..., и  регистра  CX:  1,2,4,8...  Начало
цикла имеет метку, в данном случае, A20: - двоеточие oбозначает, что метка
находится внутри процедуры (в данном случае  BEGIN)  в  сегменте  кода.  В
конце цикла находится команда

               JMP  A20

которая указывает на то, что управление должно  быть  передано  команде  c
меткой A20. Обратите внимание,  что  адресная  метка  в  операнде  команды
указывается без двоеточия.  Данный цикл  не  имеет  выхода  и  приводит  к
бесконечному выполнению - такие циклы обычно не используются.

__________________________________________________________________________

                        page 60,132
                TITLE   EXJUMP  (COM) Организация цикла с помощью JMP
0000            CODESG  SEGMENT PARA 'Code'
                        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
0100                    ORG     100H

0100            MAIN    PROC    NEAR
0100  B8 0001           MOV     AX,01           ;Инициализация AX,
0103  BB 0001           MOV     BX,01           ;              BX,
0106  B9 0001           MOV     CX,01           ;            и CX
0109            A20:
0109  05 0001           ADD     AX,01           ;Прибавить 01 к AX
010C  03 D8             ADD     BX,AX           ;Прибавить 01 к BX
010E  D1 E1             SHL     CX,1            ;Удвоить CX
0110  EB F7             JMP     A20             ;Переход на A20
0112            MAIN    ENDP
0112            CODESG  ENDS
                        END     MAIN
__________________________________________________________________________

     Рис.7.1. Использование команды JMP.


     Метку можно кодировать на одной строке с командой:

               A20:   ADD   AX,01

или на отдельной строке:

               A20:
                      ADD   AX,01

     В обоих случаях адрес A20  указывает  на  первый  байт  команды  ADD.
Двоеточие в метке A20 указывает на тип метки - NEAR. Запомните: отсутствие
двоеточия  в  метке  является  частой  ошибкой.  В  нашем   примере    A20
соответствует  -9  байтам  от  команды  JMP,  в  чем  можно  убедиться  по
объектному коду команды - EBF7. EB представляет  собой  машинный  код  для
короткого перехода JMP, а  F7  -  отрицательное  значение  смещения  (-9).
Команда JMP прибавляет F7 к командному указателю  (IP),  котоpый  содержит
адрес команды после JMP (0112):

                                        Дес. Шест.
               Командный указатель:     274  112
               Адрес в команде JMP:      -9   F7 (двоичное дополнение)
                                        ---  ---
               Адрес перехода:          265  109

     В результате сложения получается адрес перехода - шест.109. Проверьте
по   листингу  программы,  что  относительный  адрес  метки  действительно
соответствует шест.109.  Соответственно операнд в команде JMP для перехода
вперед имеет положительное значение.
     Команда JMP для перехода в пределах  -128  до  +127  байт  имеет  тип
SHORT.  Ассемблер генерирует в этом случае однобайтовый операнд в пределах
от 00 до FF. Команда JMP, превосходящая эти пределы, получает тип FAR, для
которого  генерируется  другой  машинный  код  и  двухбайтовый    операнд.
Ассемблер в первом просмотре исходной программы  определяет  длину  каждой
команды.  Однако, команда JMP может быть длиной два или три байта. Если  к
моменту просмотра команды JMP ассемблер  уже  вычислил  значение  опеpанда
(при переходе назад):

               A50:
                    ...
                    JMP  A50

то он генерирует двухбайтовую команду.  Если  ассемблер  еще  не  вычислил
значение операнда (при переходе вперед)

                    JMP  A90
                    ...
               A90:

то он не знает тип перехода NEAR или FAR, и автоматически  генерирует  3-х
байтовую команду.  Для того, чтобы  указать  ассемблеру  на  необходимость
генерации двухбайтовой команды, следует использовать оператор SHORT:

                    JMP  SHORT A90
                    ...
               A90:

     В качестве полезного упражнения,  введите программу, проассемблируйте
ее,  скомпонуйте  и  переведите  в  COM-формат.  Определение   данных   не
требуется,  поскольку непосредственные операнды генерируют все необходимые
данные.  Используйте отладчик DEBUG для пошагового выполнения COM-модуля и
просмотрите  несколько повторений цикла.  Когда регистр AX будет содержать
08,  BX  и  CX  увеличатся  до  шест.24  (дес.36)  и  шест.80   (дес.128),
соответственно. Для выхода из отладчика используйте команду Q.


     КОМАНДА LOOP
     ________________________________________________________________

     Команда JMP в примере на рис.7.1 реализует бесконечный цикл. Но более
вероятно подпрограмма должна выполнять определенное число циклов.  Команда
LOOP, которая служит  для  этой  цели,  использует  начальное  значение  в
регистре  CX.  В  каждом  цикле  команда  LOOP  автоматически    уменьшает
содержимое регистра CX на 1. Пока значение в CX не равно нулю,  управление
передается по адресу,  указанному  в  операнде,  и  если  в  CX  будет  0,
управление переходит на следующую после LOOP команду.

__________________________________________________________________________

                         page 60,132
                 TITLE   EXLOOP  (COM) Организация цикла командой LOOP
0000             CODESG  SEGMENT PARA 'Code'
                         ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
0100                     ORG     100H

0100             BEGIN   PROC    NEAR
0100  B8 0001            MOV     AX,01      ;Инициализация AX,
0103  BB 0001            MOV     BX,01      ;              BX,
0106  BA 0001            MOV     DX,01      ;            и DX
0109  B9 000A            MOV     CX,10      ;Число циклов
010C            A20:
010C  40                 INC     AX         ;Прибавить 01 к AX
010D  03 D8              ADD     BX,AX      ;Прибавить AX к BX
010F  D1 E2              SHL     DX,1       ;Удвоить DX
0111  E2 F9              LOOP    A20        ;Уменьшить CX и повторить
                                            ; цикл, если не нуль
0113  C3                 RET                ;Завершить работу
0114            BEGIN    ENDP
0114            CODESG   ENDS
                         END     BEGIN
__________________________________________________________________________

     Рис.7.2. Использование команды LOOP.


     Программа на  рис.7.2,  иллюстрирующая  использование  команды  LOOP,
выполняет действия,  аналогичные примеру на рис.7.1 за  исключением  того,
что после десяти циклов программа завершается.  Команда MOV инициализирует
регистр CX значением 10.  Так как команда LOOP использует регистр CX, то в
программе   для   удвоения   начального  значения  1  вместо  регистра  CX
используется  DX.  Команда  JMP  A20  заменена   командой   LOOP   и   для
эффективности команда ADD AX,01 заменена командой INC AX (увеличение AX на
1).
     Аналогично команде JMP, операнд команды LOOP определяет расстояние от
конца команды LOOP до адреса метки A20, которое прибавляется к содержимому
командного указателя.  Для команды  LOOP  это  расстояние  должно  быть  в
пределах от -128 до +127 байт.  Если операнд  превышает  эти  границы,  то
ассемблер выдаст сообщение "Relative jump out of range" (превышены границы
перехода).
     Для проверки  команды  LOOP  рекомендуется  изменить  соответствующим
образом программу, приведенную на рис.7.1, выполнить  ее  ассемблирование,
компоновку и преобразование в COM-файл. Для трассировки всех десяти циклов
используйте отладчик DEBUG.  Когда в значение регистре  CX  уменьшится  до
нуля, содержимое регистpов AX, BX и DX будет  соответственно  шест.  000B,
0042 и 0400. Для выхода из отладчика введите команду Q.
     Дополнительно существует две разновидности команды LOOP -  это  LOOPE
(или LOOPZ) и LOOPNE (или LOOPNZ).  Обе команды также  уменьшают  значение
регистра CX на 1. Команда LOOPE передает управление  по  адресу  операнда,
если регистр CX имеет ненулевое значение и флаг  нуля  установлен  (ZF=1).
Команда LOOPNE передает управление по адресу  операнда,  если  регистр  CX
имеет ненулевое значение и флаг нуля сброшен (ZF=0).


     ФЛАГОВЫЙ РЕГИСТР
     ________________________________________________________________

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

               Номер бита:    15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
               Флаг:           *  *  *  *  O  D I T S Z * A * P * C

     Рассмотрим эти флаги в последовательности справа налево.

     CF (Carry Flag) - флаг переноса. Содержит значение "переносов" (0 или
1) из старшего разряда при арифметических операциях и некоторых  операциях
сдвига и циклического сдвига (см. гл.12).

     PF (Parity Flag)  -  флаг  четности.  Проверяет  младшие  восемь  бит
pезультатов операций над данными.  Нечетное число бит приводит к установке
этого флага в 0, а четное - в 1. Не следует путать флаг четности  с  битом
контроля на четность.

     AF  (Auxiliary  Carry  Flag)  -   дополнительный    флаг    переноса.
Устанавливается в 1, если  арифметическая  операция  приводит  к  переносу
четвертого справа бита (бит номер 3) в регистровой  однобайтовой  команде.
Данный флаг имеет отношение к арифметическим операциям над символами  кода
ASCII и к десятичным упакованным полям.

     ZF (Zero Flag) - флаг нуля.  Устанавливается  в  качестве  результата
aрифметических команд и команд сравнения.  Как это ни  странно,  ненулевой
результат приводит к установке нулевого значения этого флага, а нулевой  -
к  установке  единичного  значения.  Кажущееся  несоответствие   является,
однако, логически правильным, так как 0 обозначает "нет"  (т.е.  результат
не равен нулю), а единица обозначаeт "да"  (т.е.  результат  равен  нулю).
Команды условного перехода JE и JZ проверяют этот флаг.

     SF (SIgn Flag) - знаковый флаг.  Устанавливается  в  соответствии  со
знаком  результата  (старшего  бита)  после    арифметических    опеpаций:
положительный результат устанавливает 0,  а  отрицательный  -  1.  Команды
условного перехода JG и JL проверяют этот флаг.

     TF (Trap Flag) -  флаг  пошагового  выполнения.  Этот  флаг  вам  уже
приходилось устанавливать, когда  использовалась  команда  Т  в  отладчике
DEBUG.  Если этот флаг установлен  в  единичное  cостояние,  то  процессор
переходит в режим пошагового  выполнения  команд,  т.е.  в  каждый  момент
выполняется одна команда под пользовательским управлением.

     IF (Interrupt Flag) - флаг прерывания.  При нулевом  состоянии  этого
флага прерывания запрещены, при единичном - разрешены.

     DF (DIrection Flag) -  флаг  направления.  Используется  в  строковых
операциях  для  определения  направления  передачи  данных.  При   нулевом
состоянии команда  увеличивает  содержимое  регистров  SI  и  DI,  вызывая
передачу данных слева направо, при нулевом  -  уменьшает  содержимое  этих
регистров, вызывая передачу данных справа налево (см. гл.11).

     OF (Overflow Flag)  -  флаг  переполнения.  Фиксирует  арифметическое
переполнение, т.е. перенос вниз старшего  (знакового)  бита  при  знаковых
арифметических операциях.

     В  качестве  примера:  команда  CMP  сравнивает  два    операнда    и
воздействует на флаги AF, CF, OF, PF, SF, ZF.  Однако,  нет  необходимости
проверять все эти флаги по отдельности.  В следующем  примере  проверяется
содержит ли регистр BX нулевое значение:

                         CMP  BX,00          ;Сравнение BX с нулем
                         JZ   B50            ;Переход на B50 если нуль
                         .    (действия при не нуле)
                         .
               B50:      ...                 ;Точка перехода при BX=0

     Если BX содержит нулевое значение,  команда  CMP  устанавливает  флаг
нуля ZF в единичное состояние, и возможно изменяет (или нет) другие флаги.
Команда JZ (переход,  если нуль) проверяет только флаг ZF.  При  единичном
значении ZF,  обозначающее нулевой признак, команда передает управление на
адрес, указанный в ее операнде, т.е. на метку B50.


     КОМАНДЫ УСЛОВНОГО ПЕРЕХОДА
     ________________________________________________________________

     В предыдущих примерах было показано, что команда  LOOP  уменьшает  на
единицу  содержимое  регистра  CX  и  проверяет  его:  если  не  ноль,  то
управление передается по адресу, указанному  в  операнде.  Таким  образом,
передача  управления  зависит  от   конкретного    состояния.    Ассемблер
поддерживает  большое  количество  команд  условного  перехода,    которые
осуществляют передачу управления  в  зависимости  от  состояний  флагового
регистра.  Например, при  сравнении  содержимого  двух  полей  последующий
переход зависит от значения флага.
     Команду LOOP в программе на рис.7.2 можно заменить  на  две  команды:
одна  уменьшает  содержимое  регистра  CX,  а  другая  выполняет  условный
переход:

          Использование LOOP    Использование условного перехода

                 LOOP A20                 DEC  CX
                                          JNZ  A20

     Команды DEC  и  JNZ  действуют  аналогично  команде  LOOP:  уменьшают
содержимое регистра CX на 1 и выполняет переход на метку A20, если в CX не
ноль.  Команда DEC кроме того устанавливает флаг нуля во флаговом регистре
в состояние 0  или  1.  Команда  JNZ  затем  проверяет  эту  установку.  В
рассмотренном  примере  команда  LOOP   хотя    и    имеет    ограниченное
использование, но более эффективна, чем две команды: DEC и JNZ.
     Аналогично командам  JMP  и  LOOP  операнд  в  команде  JNZ  cодержит
значение расстояния между  концом  команды  JNZ  и  адресом  A20,  которое
прибавляется к командному указателю. Это расстояние должно быть в пределах
от -128 до +127 байт.  В случае перехода за эти границы  ассемблер  выдаст
сообщение "Relative jump out of range"  (превышены  относительные  границы
перехода).


                      Знаковые и беззнаковые данные
                     -------------------------------
     Рассматривая назначение команд условного  перехода  следует  пояснить
характер  их  использования.  Типы  данных,  над   которыми    выполняются
арифметические операции и операции сравнения определяют  какими  командами
пользоваться: беззнаковыми или знаковыми.  Беззнаковые  данные  используют
все биты как биты данных; характерным примером являются символьные строки:
имена, адреса и натуральные числа.  В  знаковых  данных  самый  левый  бит
представляет собой знак, причем если его значение  равно  нулю,  то  число
положительное, и если единице, то отрицательное.  Многие числовые значения
могут быть как положительными так и отрицательными.
     В качестве примера предположим, что регистр AX содержит  11000110,  а
BX - 00010110. Команда

                         CMP  AX,BX

сравнивает содержимое регистров AX  и  BX.  Если  данные  беззнаковые,  то
значение в AX больше, а если знаковые - то меньше.


                     Переходы для беззнаковых данных
                    ---------------------------------
          Мнемоника      Описание                      Проверяемые флаги

          JE/JZ     Переход, если равно/нуль                     ZF
          JNE/JNZ   Переход, если не равно/не нуль               ZF
          JA/JNBE   Переход, если выше/не ниже или равно         ZF,CF
          JAE/JNB   Переход, если выше или равно/не ниже         CF
          JB/JNAE   Переход, если ниже/не выше или равно         CF
          JBE/JNA   Переход, если ниже или равно/не выше         CF,AF

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


                       Переходы для знаковых данных
                      ------------------------------
          Мнемоника      Описание                      Проверяемые флаги

          JE/JZ     Переход, если равно/нуль                     ZF
          JNE/JNZ   Переход, если не равно/не нуль               ZF
          JG/JNLE   Переход, если больше/не меньше или равно     ZF,SF,OF
          JGE/JNL   Переход, если больше или равно/не меньше     SF,OF
          JL/JNGE   Переход, если меньше/не больше или равно     SF,OF
          JLE/JNG   Переход, если меньше или равно/не больше     ZF,SF,OF

     Команды перехода для условия равно или ноль (JE/JZ) и не равно или не
ноль (JNE/JNZ) присутствуют в обоих списках  для  беззнаковых  и  знаковых
данных. Состояние равно/нуль происходит вне зависимости от наличия знака.


                   Специальные арифметические проверки
                  -------------------------------------
          Мнемоника      Описание                      Проверяемые флаги

          JS        Переход, если есть знак (отрицательно)       SF
          JNS       Переход, если нет знака(положительно)        SF
          JC        Переход, если есть перенос (аналогично JB)   CF
          JNC       Переход, если нет переноса                   CF
          JO        Переход, если есть переполнение              OF
          JNO       Переход, если нет переполнения               OF
          JP/JPE    Переход, если паритет четный                 PF
          JNP/JP    Переход, если паритет нечетный               PF

     Еще одна команда условного перехода  проверяет  равно  ли  содержимое
регистра  CX  нулю.  Эта  команда  необязательно   должна    pасполагаться
непосредственно за командой арифметики или сравнения.  Одним из  мест  для
команды JCXZ может быть  начало  цикла,  где  она  проверяет  содержит  ли
регистр CX ненулевое значение.
     Не спешите пока заучивать эти команды наизусть. Запомните только, что
для беззнаковых данных есть переходы по состояниям равно, выше или ниже, а
для беззнаковых - равно, больше или меньше.  Переходы по проверкам  флагов
переноса, переполнения  и  паритета  имеют  особое  назначение.  Ассемблер
транслирует мнемонические коды в объектный код независимо от  того,  какую
из двух команд вы применили.  Однако,  команды  JAE  и  JGE  являясь  явно
одинаковыми, проверяют различные флаги.


     ПРОЦЕДУРЫ И ОПЕРАТОР CALL
     ________________________________________________________________

     В предыдущих главах примеры содержали в кодовом сегменте только  oдну
процедуру, оформленную следующим образом:

               BEGIN     PROC FAR
                    .
                    .
               BEGIN     ENDP

     Операнд FAR информирует систему о  том,  что  данный  адрес  является
точкой входа для выполнения, а директива ENDP определяет конец  процедуры.
Кодовый  сегмент,  однако,  может  содержать  любое  количество  процедур,
которые  разделяются  директивами  PROC  и  ENDP.  Типичная    организация
многопроцедурной программы приведена на рис.7.3.

__________________________________________________________________________

                        -------------------------¬
                        ¦ CODESG  SEGMENT  PARA  ¦
                        +------------------------+
                        ¦ BEGIN   PROC     FAR   ¦
                        ¦          .             ¦
                        ¦          .             ¦
                        ¦         CALL     B10   ¦
                        ¦         CALL     C10   ¦
                        ¦         RET            ¦
                        ¦ BEGIN   ENDP           ¦
                        +------------------------+
                        ¦ B10     PROC     NEAR  ¦
                        ¦          .             ¦
                        ¦          .             ¦
                        ¦         RET            ¦
                        ¦ B10     ENDP           ¦
                        +------------------------+
                        ¦ C10     PROC     NEAR  ¦
                        ¦          .             ¦
                        ¦          .             ¦
                        ¦         RET            ¦
                        ¦ C10     ENDP           ¦
                        +------------------------+
                        ¦ CODESG  ENDS           ¦
                        ¦         END      BEGIN ¦
                        L-------------------------
__________________________________________________________________________

     Рис.7.3. Вызов процедур.


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

          - Директивы PROC по меткам B10 и  C10  имеют  операнд  NEAR  для
     указания  того,  что  эти  процедуры  находятся  в  текущем   кодовом
     сегменте. Во многих последующих примерах этот операнд опущен, так как
     по умолчанию ассемблер принимает тип NEAR.

          - Каждая процедура имеет уникальное имя и  содержит  собственную
     директиву ENDP для указания конца процедуры.

          - Для передачи управления в процедуре BEGIN имеются две команды:
     CALL B10 и CALL C10. В  результате  первой  команды  CALL  управление
     передается процедуре  B10  и  начинается  ее  выполнение.   Достигнув
     команды  RET,  управление  возвращается  на  команду  непосредственно
     следующую за CALL B10.  Вторая команда CALL  действует  аналогично  -
     передает   управление   в  процедуру  C10,  выполняет  ее  команды  и
     возвращает управление по команде RET.

          - Команда RET всегда выполняет возврат в  вызывающую  программу.
     Программа BEGIN вызывает процедуры  B10  и  C10,  которые  возвращают
     управление обратно в BEGIN.  Для  выполнения  самой  программы  BEGIN
     операционная система DOS вызывает ее и в конце выполнения команда RET
     возвращает  управление  в  DOS.  Если  процедура  B10  не    содержит
     завершающей команды  RET,  то  выполнение  команд  продолжится из B10
     непосредственно в процедуре  C10.  Если  процедура  C10  не  содержит
     команды RET,  то будут выполняться команды, оказавшиеся за процедурой
     C10 с непредсказуемым результатом.

     Использование  процедур  дает  хорошую    возможность    организовать
логическую структуру программы.  Кроме того,  операнды  для  команды  CALL
могут иметь значения, выходящие за границу от -128 до +127 байт.
     Технически управление в процедуру типа NEAR  может  быть  передано  с
помощью команд перехода или даже обычным  построчным  кодированием.  Но  в
большинстве случаев рекомендуется использовать команду CALL  для  передачи
управления в процедуру и команду RET для возврата.


     СЕГМЕНТ СТЕКА
     ________________________________________________________________

     До  этого  раздела  в  приводимых  примерах  встретились  только  две
команды, использующих стек, - это команды PUSH в  начале  сегмента  кодов,
которые обеспечивают  возврат  в  DOS,  когда  EXE-программа  завершается.
Естественно для этих программ требуется стек oчень малого размера. Однако,
команда CALL автоматически записывает в стек относительный адрес  команды,
следующей непосредственно за командой  CALL,  и  увеличивает  после  этого
указатель вершины стека.  В вызываемой процедуре  команда  RET  использует
этот адрес для возврата в вызывающую процедуру и  при  этом  автоматически
уменьшается указатель вершины стека.
     Таким образом, команды PUSH записывают в стек двухбайтовые адреса или
другие значения.  Команды POP обычно выбирают из стека записанные  в  него
слова.  Эти операции изменяют относительный адрес в регистре  SP  (т.е.  в
указатели стека) для доступа к следующему  слову.  Данное  свойство  стека
требует чтобы команды RET и CALL соответствовали друг другу.  Кроме  того,
вызванная процедура может вызвать с помощью команды CALL другую процедуру,
а та в свою очередь - следующую. Стек должен иметь достаточные размеры для
того, чтобы хранить  все  записываемые  в  него  адреса.  Для  большинства
примеров в данной книге стек объемом в 32 слова является достаточным.
     Команды PUSH, PUSHF, CALL, INT, и INTO заносят в стек адрес  возврата
или содержимое флагового регистра. Команды POP, POPF, RET и IRET извлекают
эти aдреса или флаги из стека.
     При передаче  управления  в  EXE-программу  система  устанавливает  в
регистрах следующие значения:

     DS и ES:  Адрес  префикса  программного  сегмента  -  область  в  256
(шест.100)  байт,  которая предшествует выполняемому программному модулю в
памяти.

     CS: Адрес точки входа в программу (адрес первой выполняемой команды).

     IP: Нуль.

     SS: Адрес сегмента стека.

     SP: Относительный адрес, указывающий на вершину стека.  Например, для
стека в 32 слова (64 байта), определенного как

                    DW   32 DUP(?)

SP содержит 64, или шест.40.


     Выполним трассировку простой EXE-программы, приведенной  на  рис.7.4.
На практике вызываемые процедуры содержат любое число команд.

__________________________________________________________________________

                      TITLE   CALLPROC (EXE) Вызов процедур
0000                  STACKSG SEGMENT PARA STACK 'Stack'
0000    20 [ ???? ]           DW      32 DUP(?)
0040                  STACKG  ENDS

0000                  CODESG  SEGMENT PARA 'Code'
0000                  BEGIN   PROC    FAR
                              ASSUME  CS:CODESG,SS:STACKSG
0000  1E                      PUSH    DS
0001  2B C0                   SUB     AX,AX
0003  50                      PUSH    AX
0004  E8 0008 R               CALL    B10     ;Вызвать B10
                      ;       ...
0007  CB                      RET             ;Завершить программу
0008                  BEGIN   ENDP
                              ;-------------------------------------
0008                  B10     PROC
0008  E8 000C R               CALL    C10     ;Вызвать C10
                      ;       ...
000B  C3                      RET             ;Вернуться в
000C                  B10     ENDP            ; вызывающую программу
                      ;---------------------------------------------
000C                  C10     PROC
                      ;       ...
000C  C3                      RET             ;Вернуться в
000D                  C10     ENDP            ; вызывающую программу
                      ;---------------------------------------------
000D                  CODESG  ENDS
                              END     BEGIN
__________________________________________________________________________

     Рис.7.4. Воздействие выполнения программы на стек.


     Текущая доступная ячейка стека для  занесения  или  извлечения  слова
является вершина стека.  Первая команда PUSH уменьшает значение SP на 2  и
заносит содержимое регистра DS (в данном примере 049f)  в  вершину  стека,
т.е. по адресу 4B00+3E. Вторая команда PUSH также уменьшает значение SP на
2 и записывает содержимое регистра AX (0000) по  адресу  4B00+3C.  Команда
CALL B10 уменьшает значение SP и записывает относительный адрес  следующей
команды (0007) в стек  по  адресу  4B00+3A.  Команда  CALL  C10  уменьшает
значение SP и записывает относительный адрес следующей  команды  (000B)  в
стек по адресу 4B00+38.
     При возврате из процедуры C10 команда RET  извлекает  000B  из  стека
(4B00+38), помещает его в указатель команд IP и увеличивает значение SP на
2. При этом происходит автоматический  возврат  по  относительному  адресу
000B в кодовом сегменте, т.е. в процедуру B10.
     Команда RET в конце процедуры  B10  извлекает  адрес  0007  из  стека
(4B00+3A), помещают его в IP и увеличивает значение  SP  на  2.  При  этом
происходит автоматический возврат по относительному адресу 0007 в  кодовом
сегменте.  Команда RET по  адресу  0007  завершает  выполнение  программы,
осуществляя возврат типа FAR.
     Ниже показано воздействие на стек при выполнении каждой команды.  Для
трассировки программы можно использовать отладчик DEBUG.  Приведено только
содержимое памяти с адреса 0034 до 003F и содержимое регистра SP:

            Команда                       Стек                  SP

       Начальное значение:     хххх хххх хххх хххх хххх хххх   0040
       PUSH DS  (запись 049F)  хххх хххх хххх хххх хххх 049F   003E
       PUSH AX  (запись 0000)  хххх хххх хххх хххх 0000 049F   003C
       CALL B10 (запись 0007)  хххх хххх хххх 0700 0000 049F   003A
       CALL C10 (запись 000B)  хххх хххх 0B00 0700 0000 049F   0038
       RET      (выборка 000B) хххх хххх хххх 0700 0000 049F   003A
       RET      (выборка 0007) хххх хххх хххх хххх 0000 049F   003C
                                |    |    |    |    |    |
       Смещение в стеке:       0034 0036 0038 003A 003C 003E

     Обратите внимание на два момента.  Во-первых, слова в памяти содержат
байты в обратной последовательности, так 0007 записывается  в  виде  0700.
Во-вторых, отладчик  DEBUG  при  использовании  его  для  просмотра  стека
заносит в стек другие значения, включая  содержимое  IP,  для  собственных
нужд.


     ПРОГРАММА: РАСШИРЕННЫЕ ОПЕРАЦИИ ПЕРЕСЫЛКИ
     ________________________________________________________________

     В  предыдущих  программах    были    показаны    команды    пересылки
непосредcтвенных данных в регистр, пересылки данных из памяти  в  регистр,
пересылки содержимого регистра в память  и  пересылки  содержимого  oдного
регистра в другой.  Во всех случаях длина данных была огpаничена одним или
двумя байтами и не предусмотрена пересылка данных из одной области  памяти
непосредственно другую  область.  В  данном  разделе  объясняется  процесс
пересылки данных,  которые  имеют  длину  более  двух байт.  В гл.11 будет
показано использование операций над строками для пересылки данных из одной
области памяти непосредственно в другую область.
     В EXE-программе,  приведенной на рис.7.5, сегмент данных cодержит три
девятибайтовых  поля,  NAME1,  NAME2,  NAME3.  Цель  программы - переслать
данные из поля NAME1 в поле NAME2 и переслать данные из поля NAME2 в  поле
NAME3.  Так как эти поля имеют длину девять байт каждая,  то для пересылки
данных кроме простой команды MOV потребуются еще другие команды. Программа
содержит несколько новых особенностей.

__________________________________________________________________________

        page     65,132
TITLE   EXMOVE   (EXE) Операции расширенной пересылки
;------------------------------------------------------
STACKSG SEGMENT  PARA STACK 'Stack'
        DW       32 DUP(?)
STACKSG ENDS
;------------------------------------------------------
DATASG  SEGMENT  PARA 'Data'
NAME1   DB       'ABCDEFGHI'
NAME2   DB       'JKLMNOPQR'
NAME3   DB       'STUVWXYZ*'
DATASG  ENDS
;-------------------------------------------------------
CODESG  SEGMENT PARA 'Code'
BEGIN   PROC    FAR
        ASSUME  CS:CODESG,DS:DATASG,SS:STACKSG,ES:DATASG
        PUSH    DS
        SUB     AX,AX
        PUSH    AX
        MOV     AX,DATASG
        MOV     DS,AX
        MOV     ES,AX
        CALL    B10MOVE         ;Вызвать JUMP подпрограмму
        CALL    C10MOVE         ;Вызвать CALL подпрограмму
        RET                     ;Завершить программу
BEGIN   ENDP

;       Расширенная пересылка (JUMP-подпрограмма),
;       использующая переход по условию:
;       -----------------------------------------
B10MOVE PROC
        LEA     SI,NAME1        ;Инициализация адресов
        LEA     DI,NAME2        ; NAME1 и NAME2
        MOV     CX,09           ;Переслать 9 символов
B20:
        MOV     AL,[SI]         ;Переслать из NAME1
        MOV     [DI],AL         ;Переслать в NAME2
        INC     SI              ;Следующий символ в NAME1
        INC     DI              ;Следующая позиция в NAME2
        DEC     CX              ;Уменьшить счетчик цикла
        JNZ     B20             ;Счетчик > 0? Да - цикл
        RET                     ;Если счетчик = 0, то
B10MOVE ENDP                    ; вернуться

;       Расширенная пересылка (LOOP-подпрограмма),
;       использующая команду LOOP:
;       -----------------------------------------;
C10MOVE PROC
        LEA     SI,NAME2        ;Инициализация адресов
        LEA     DI,NAME3        ; NAME2 и NAME3
        MOV     CX,09           ;Переслать 9 символов
C20
        MOV     AL,[SI]         ;Переслать из NAME2
        MOV     [DI],AL         ;Переслать в NAME3
        INC     DI              ;Следующий символ в NAME2
        INC     SI              ;Следующая позиция в NAME3
        LOOP    C20             ;Уменьшить счетчик,
                                ; если не ноль, то цикл
        RET                     ;Если счетчик = 0, то
C10MOVE ENDP                    ; вернуться
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.7.5. Расширенные операции пересылки.


     Процедура BEGIN инициализирует сегментные регистры и  затем  вызывает
процедуры B10MOVE и C10MOVE.  Процедура B10MOVE пересылает содержимое поля
NAME1 в поле NAME2. Так как каждый раз пересылается только один  байт,  то
процедура начинает с самого левого байта в поле NAME1 и в цикле пересылает
затем второй байт, третий и т.д.:

               NAME1:    A  B  C  D  E  F  G  H  I
                         |  |  |  |  |  |  |  |  |
               NAME2:    J  K  L  M  N  O  P  Q  R

Для продвижения в полях NAME1 и NAME2 в регистр CX заносится значение 9, а
регистры SI и DI  используются  в  качестве  индексных.  Две  команды  LEA
загружают относительные aдреса полей NAME1 и NAME2 в регистры SI и DI:

               LEA  SI,NAME1  ;Загрузка относительных адресов
               LEA  DI,NAME2  ;  NAME1 и NAME2

Для пересылки содержимого первого байта из поля NAME1 в первый  байт  поля
NAME2 используются адреса  в  регистрах  SI  и  DI.  kвадратные  скобки  в
командах MOV обозначают, что для доступа к  памяти  используется  адрес  в
регистре, указанном в квадратных cкобках. Таким образом, команда

               MOV  AL,[SI]

означает: использовать адрес  в  регистре  SI  (т.е.NAME1)  для  пересылки
соответствующего байта в регистр AL. А команда

               MOV  [DI],AL

означает: пересылать содержимое регистра AL по адресу, лежащему в регистре
DI (т.е. NAME2).
     Следующие команды увеличивают значения регистров SI и DI и  уменьшают
значение в регистре SH. Если в регистре CX не нулевое значение, управление
передается на следующий цикл (на метку B20). Так как содержимое  регистров
SI и DI было увеличено на 1, то следующие команды MOV будут иметь  дело  с
адресами NAME1+1 и NAME2+1. Цикл продолжается таким образом, пока не будет
передано содержимое NAME1+8 и NAME2+8.
     Процедура C10MOVE аналогична процедуре B10MOVE с двумя  исключениями:
она пересылает данные из поля NAME2 в поле NAME3 и использует команду LOOP
вместо DEC и JNZ.
     Задание: Введите программу,  приведенную  на  рис.7.5,  выполните  ее
ассемблирование, компоновку  и  трассировку  с  помощью  отладчика  DEBUG.
Обратите внимание на изменения в регистрах, командном указателе и в стеке.
Для просмотра изменений в полях NAME2 и NAME3 используйте команду D DS:0.


     КОМАНДЫ ЛОГИЧЕСКИХ ОПЕРАЦИЙ: AND, OR, XOR, TEST, NOT
     ________________________________________________________________

     Логические  операции  являются  важным  элементом  в   проектировании
микросхем и имеют много общего в логике программирования. Команды AND, OR,
XOR  и  TEST  -  являются  командами  логических  операций.  Эти   команды
используются для сброса и установки бит и для  арифметических  операций  в
коде ASCII (см.гл.13).  Все эти команды обрабатывают один  байт  или  одно
слово в регистре или в памяти, и устанавливают флаги CF, OF, PF, SF, ZF.

     AND: Если оба из сравниваемых битов равны 1, то результат равен 1; во
всех остальных случаях результат - 0.

     OR: Если хотя бы один из сравниваемых битов  равен  1,  то  результат
равен 1; если сравниваемые биты равны 0, то результат - 0.

     XOR: Если один из сравниваемых битов равен 0, а другой  равен  1,  то
результат равен 1; если сравниваемые биты одинаковы (оба - 0 или оба -  1)
то результат - 0.

     TEST: действует как AND-устанавливает флаги, но не изменяет биты.

     Первый операнд в логических командах указывает на один байт или слово
в регистре или в памяти и является единственным значением,  которое  может
изменятся после выполнения команд.  В следующих командах  AND,  OR  и  XOR
используются одинаковые битовые значения:

                              AND       OR        XOR
                              0101      0101      0101
                              0011      0011      0011
                              ----      ----      ----
               Результат:     0001      0111      0110

     Для следующих несвязанных примеров, предположим, что AL содержит 1100
0101, а BH содержит 0101 1100:

     1.        AND  AL,BH     ;Устанавливает в AL 0100 0100
     2.        OR   BH,AL     ;Устанавливает в BH 1101 1101
     3.        XOR  AL,AL     ;Устанавливает в AL 0000 0000
     4.        AND  AL,00     ;Устанавливает в AL 0000 0000
     5.        AND  AL,0FH    ;Устанавливает в AL 0000 0101
     6.        OR   CL,CL     ;Устанавливает флаги SF и ZF

     Примеры 3 и 4 демонстрируют способ  очистки  регистра.  В  примере  5
обнуляются левые четыре бита регистра AL. Хотя команды сравнения CMP могут
быть понятнее, можно применить команду OR для следующих целей:

     1.        OR   CX,CX     ;Проверка CX на нуль
               JZ   ...       ;Переход, если нуль
     2.        OR   CX,CX     ;Проверка знака в CX
               JS   ...       ;Переход, если отрицательно

     Команда TEST  действует  аналогично  команде  AND,  но  устанавливает
только флаги, а операнд не изменяется. Ниже приведено несколько примеров:

     1.        TEST BL,11110000B   ;Любой из левых бит в BL
               JNZ  ...            ; равен единице?
     2.        TEST AL,00000001B   ;Регистр AL содержит
               JNZ  ...            ; нечетное значение?
     3.        TEST DX,OFFH        ;Регистр DX содержит
               JZ   ...            ; нулевое значение?

     Еще одна логическая команда NOT устанавливает обpатное значение бит в
байте или в слове, в регистре или в памяти: нули становятся  единицами,  а
единицы - нулями.  Если, например,  pегистр  AL  содержит  1100  0101,  то
команда NOT AL изменяет это значение на  0011  1010.  Флаги  не  меняются.
Команда NOT  не  эквивалентна  команде  NEG,  которая  меняет  значение  с
положительного на отрицательное и  наоборот,  посредством  замены  бит  на
противоположное значение и прибавления единицы (см. "Отрицательные  числа"
в гл.1.).


     ПРОГРАММА: ИЗМЕНЕНИЕ СТРОЧНЫХ БУКВ НА ЗАГЛАВНЫЕ
     ________________________________________________________________

     Существуют различные причины для  преобразований  между  строчными  и
заглавными буквами.  Например, вы могли получить файл данных, созданный на
компьютере, который  работает  только  с  заглавными  буквами.  Или  некая
программа должна позволить пользователям вводить команды  как  заглавными,
так и строчными буквами (например, YES  или  yes)  и  преобразовать  их  в
заглавные для проверки.  Заглавные буквы от A до Z имеют шест. коды от  41
до 5A, а строчные буквы  от  a  до  z  имеют  шест.  коды  от  61  до  7A.
Единственная pазница в том, что пятый бит равен 0 для заглавных букв  и  1
для строчных:

                  Биты:  76543210          Биты:  76543210
               Буква A:  01000001       Буква a:  01100001
               Буква Z:  01011010       Буква z:  01111010

     COM-программа, приведенная на  рис.7.6,  преобразует  данные  в  поле
TITLEX из строчных букв в прописные, начиная с адреса TITLEX+1.  Программа
инициализирует регистр BX адресом TITLEX+1 и использует его для  пересылки
символов в регистр AH, начиная с TITLEX+1. Если полученное значение  лежит
в пределах от шест.61 и до 7A, то команда AND устанавливает бит 5 в 0:

               AND  AH,11011111B

     Все символы, отличные от строчных букв (от a до  z),  не  изменяются.
Измененные  символы  засылаются  обратно  в  область  TITLEX,  значение  в
регистре BX увеличивается для очередного символа и осуществляется  переход
на следующий цикл.

__________________________________________________________________________

                         TITLE   CASE    (COM) Перекод. в заглавные буквы
0000                     CODESG  SEGMENT PARA 'CODE'
                                 ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
0001                             ORG     100H
0001  EB 1C 90           BEGIN:  JMP     MAIN
                         ; -------------------------------------------
0003  43 68 61 6E 67 65  TITLEX  DB      'Change to uppercase letters'
      20 74 6F 20 75 70
      70 65 72 63 61 73
      65 20 6C 65 74 74
      65 72 73
                         ; -------------------------------------------
011E                     MAIN    PROC    NEAR
011E  8D 1E 0104 R               LEA     BX,TITLEX+1  ;Адрес первого симв.
0122  B9 001F                    MOV     CX,31        ;Число символов
0125                     B20:
0125  8A 27                      MOV     AH,[BX]      ;Символ из TITLEX
0127  80 FC 61                   CMP     AH,61H       ;Это
012A  72 0A                      JB      B30          ; прописная
012C  80 FC 7A                   CMP     AH,7AH       ; буква
012F  77 05                      JA      B30          ; ?
0131  80 E4 DF                   AND     AH,11011111B ;Да - преобразовать
0134  88 27                      MOV     [BX],AH      ;Записать в TITLEX
0136                     B30:
0136  43                         INC     BX           ;Следующий символ
0137  E2 EC                      LOOP    B20        ;Повторить цикл 31 раз
0139  C3                         RET
013A                     MAIN    ENDP
013A                     CODESG  ENDS
                                 END     BEGIN
__________________________________________________________________________

     Рис.7.6. Изменение строчных букв на прописные.


     Используемый таким образом регистр BX действует как индексный регистр
для адресации в памяти.  Для этих целей можно использовать также  регистры
SI и DI.


     КОМАНДЫ СДВИГА И ЦИКЛИЧЕСКОГО СДВИГА
     ________________________________________________________________

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

          - обрабатывают байт или слово;
          - имеют доступ к регистру или к памяти;
          - сдвигают влево или вправо;
          - сдвигают на величину до 8 бит (для байта) и 16 бит (для слова)
          - сдвигают логически (без знака) или арифметически (со знаком).

     Значение сдвига на 1 может  быть  закодировано  как  непосредcтвенный
операнд, значение больше 1 должно находиться в регистре CL.


                              Команды сдвига
                             ----------------
     При выполнении   команд  сдвига  флаг  CF  всегда  содержит  значение
последнего выдвинутого бита. Существуют следующие команды cдвига:

               SHR  ;Логический (беззнаковый) сдвиг вправо
               SHL  ;Логический (беззнаковый) сдвиг влево
               SAR  ;Арифметический сдвиг вправо
               SAL  ;Арифметический сдвиг влево

     Следующий фрагмент иллюстрирует выполнение команды SHR:

               MOV  CL,03          ;    AX:
               MOV  AX,10110111B   ; 10110111
               SHR  AX,1           ; 01011011   ;Сдвиг вправо на 1
               SHR  AX,CL          ; 00001011   ;Сдвиг вправо на 3

     Первая команда SHR сдвигает содержимое регистра AX вправо на  1  бит.
Выдвинутый в результате один бит попадает в флаг CF,  а  самый  левый  бит
регистра AX заполняется нулем. Вторая команда cдвигает содержимое регистра
AX еще на три бита. При этом флаг CF последовательно принимает значения 1,
1, 0, а в три левых бита в регистре AX заносятся нули.
     Рассмотрим действие команд арифметического вправо SAR:

               MOV  CL,03          ;    AX:
               MOV  AX,10110111B   ; 10110111
               SAR  AX,1           ; 11011011   ;Сдвиг вправо на 1
               SAR  AX,CL          ; 11111011   ;Сдвиг вправо на 3

     Команда SAR имеет важное  отличие  от  команды  SHR:  для  заполнения
левого бита используется знаковый  бит.  Таким  образом,  положительные  и
отрицательные величины сохраняют свой знак. В приведенном примере знаковый
бит содержит единицу.
     При сдвигах влево правые  биты  заполняются  нулями.  Таким  обpазом,
результат команд сдвига SHL и SAL индентичен.
     Сдвиг влево часто используется для удваивания чисел, а сдвиг вправо -
для деления на 2. Эти операции  осуществляются  значительно  быстрее,  чем
команды умножения или деления. Деление пополам нечетных чисел (например, 5
или  7)  образует  меньшие  значения  (2  или   3,    соответственно)    и
устанавливает флаг CF в 1.  Кроме того, если необходимо выполнить сдвиг на
2  бита,  то  использование  двух  команд  сдвига  более  эффективно,  чем
использование одной команды с загрузкой регистра CL значением 2.
     Для проверки бита, занесенного в  флаг  CF  используется  команда  JC
(переход, если есть перенос).


                       Команды циклического сдвига
                      -----------------------------
     Циклический сдвиг представляет собой  операцию  сдвига,  при  которой
выдвинутый  бит  занимает  освободившийся  разряд.  Существуют   следующие
команды циклического сдвига:

               ROR  ;Циклический сдвиг вправо
               ROL  ;Циклический сдвиг влево
               RCR  ;Циклический сдвиг вправо с переносом
               RCL  ;Циклический сдвиг влево с переносом

     Следующая    последовательность    команд    иллюстрирует    операцию
циклического сдвига ROR:

               MOV  CL,03          ;    BX:
               MOV  BX,10110111B   ; 10110111
               ROR  BX,1           ; 11011011   ;Сдвиг вправо на 1
               ROR  BX,CL          ; 01111011   ;Сдвиг вправо на 3

     Первая команда  ROR  при  выполнении  циклического  сдвига  переносит
правый единичный бит регистра BX в освободившуюся  левую  позицию.  Вторая
команда ROR переносит таким образом три правых бита.
     В командах RCR и RCL в  сдвиге  участвует  флаг  CF.  Выдвигаемый  из
регистра бит заносится в флаг CF, а  значение  CF  при  этом  поступает  в
освободившуюся позицию.
     Рассмотрим пример, в  котором  используются  команды  циклического  и
простого  сдвига.  Предположим,  что  32-битовое  значение  находится    в
регистрах DX:AX так, что левые 16 бит лежат в регистре DX, а  правые  -  в
AX. Для умножения на 2 этого значения возможны cледующие две команды:

               SHL  AX,1      ;Умножение пары регистров
               RCL  DX,1      ;  DX:AX на 2

     Здесь команда SHL сдвигает все биты регистра AX влево,  причем  самый
левый бит попадает в флаг CF. Затем команда RCL сдвигает все биты регистра
DX влево и в освободившийся правый бит заносит значение из флага CF.


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

     Ниже даны основные рекомендации для написания ассемблерных программ:

          1.  Четко  представляйте  себе  задачу,  которую  должна  решить
     программа.
          2. Сделайте эскиз задачи в  общих  чертах  и  спланируйте  общую
     логику  программы.  Например,  если  необходимо  проверить   операции
     пеpесылки нескольких байт (как  в  примере  на  рис.7.5),  начните  c
     определения полей  с  пересылаемыми данными.  Затем спланируйте общую
     стратегию для  инициализации,  условного  перехода  и  команды  LOOP.
     Приведем  основную  логику,  которую используют многие программисты в
     таком случае:

          инициализация стека и сегментных регистров
          вызов подпрограммы цикла
          возврат

     Подпрограмма цикла может быть спланирована следующим образом:

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

     Метка:    пересылка одного байта
               увеличение адресов на 1
               уменьшение счетчика на 1:
                    если счетчик не ноль, то идти на метку
                    если ноль, возврат

          3. Представьте программу в  виде  логических  блоков,  следующих
     друг за другом.  Процедуры не превышающие 25  строк  (размер  экрана)
     удобнее для отладки.
          4. Пользуйтесь тестовыми примерами программ.  Попытки  запомнить
     все  технические  детали  и  программирование  сложных  программ  "из
     головы" часто приводят к многочисленным ошибкам.
          5. Используйте комментарии для описания того, что должна  делать
     процедура, какие арифметические действия или операции сравнения будут
     выполняться и  что  делают  редко  используемые  команды.  (Например,
     команда XLAT, не имеющая операндов).
          6. Для кодирования программы  используйте  заготовку  программы,
     скопированной в файл с новым именем.

     В следующих программах данной  книги  важным  является  использование
команды LEA, индексных регистров SI и  DI,  вызываемых  процедур.  Получив
теперь базовые знания по ассемблеру, можем перейти  к  более  развитому  и
полезному программированию.


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

     - Метки  процедур (например,  B20:) должны завершаться двоеточием для
указания типа NEAR. Отсутствие двоеточия приводит к ассемблерной ошибке.

     - Метки для команд условного перехода и LOOP должны лежать в границах
-128 до +127 байт.  Операнд таких команд генерирует один  байт  объектного
кода.  Шест. от 01 до 7F соответствует десятичным значениям от +1 до +127,
а шест. от FF до 80 покрывает значения  от  -1  до  +128.  Так  как  длина
машинной команды может быть от 1  до  4  байт,  то  соблюдать  границы  не
просто. Практически можно ориентироваться на размер в два экрана исходного
текста (примерно 50 строк).

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

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

     - Выбирайте команды условного перехода соответственно  операциям  над
знаковыми или беззнаковыми данными.

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

     - Будьте внимательны при использовании индексных операндов. Сравните:

               MOV  AX,SI
               MOV  AX,[SI]

Первая команда MOV пересылает в регистр AX содержимое регистра SI.  Вторая
команда  MOV  для  доступа  к  пересылаемому  слову  в  памяти  использует
относительный адрес в регистре SI.

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


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

     7.1. Какое максимальное количество байт могут обойти команды коpоткий
JMP, LOOP и относительный переход? Какой машинный код  операнда  при  этом
генерируется?

     7.2. Команда JMP начинается на шест.0624.  Определите адрес перехода,
если шест. объектный код для операнда команды JMP: а) 27, б) 6B, в) C6.

     7.3. Напишите программу вычисления 12 чисел Фибоначчи: 1, 1, 2, 3, 5,
8, 13,... (каждое число в последовательности представляет собой сумму двух
предыдущих  чисел).  Для  организации  цикла  используйте  команду   LOOP.
Выполните  ассемблирование,  компоновку  и  с  помощью  отладчика    DEBUG
трассировку программы.

     7.4. Предположим, что регистры AX и BX содержат знаковые данные, a CX
и DX - беззнаковые.  Определите команды CMP  (где  необходимо)  и  команды
безусловного перехода для следующих проверок:

          а) значение в DX больше, чем в CX?
          б) значение в BX больше, чем в AX?
          в) CX содержит нуль?
          г) было ли переполнение?
          д) значение в BX равно или меньше, чем в AX?
          е) значение в DX равно или меньше, чем в CX?

     7.5. На какие флаги воздействуют следующие события и  какое  значение
этих флагов?

          a) произошло переполнение;
          б) результат отрицательный;
          в) результат нулевой;
          г) обработка в одношаговом режиме;
          д) передача данных должна быть справа налево.

     7.6.  Что  произойдет  при  выполнении  программы  ,  приведенной  на
рис.7.4, если в процедуре BEGIN будет отсутствовать команда RET?

     7.7. Какая разница между кодированием в  директиве  PROC  опеpанда  с
типом FAR и с типом NEAR?

     7.8. Каким образом может программа начать выполнение процедуры?

     7.9. В EXE-программе процедура A10 вызывает B10, B10 вызывает C10,  а
C10 вызывает D10. Сколько адресов, кроме начальных адресов возврата в DOS,
содержит стек?

     7.10. Предположим , что регистр BL содержит 11100011 и поле по  имени
BOONO  содержит  01111001.  Определите  воздействие  на  регистр  BL   для
следующих команд: а) XOR BL,BOONO; б) AND BL,BOONO; в) OR BL,BOONO; г) XOR
BL,11111111B; д) AND BL,00000000B.

     7.11. Измените программу на рис.7.6 для: а)  определения  содержимого
TITLEX заглавными буквами; б) преобразование заглавных букв в строчные.

     7.12. Предположим, что  регистр  DX  содержит  10111001  10111001,  а
pегистр CL  -  03.  Определите  содержимое  регистра  DX  после  следующих
несвязанных команд: а) SHR DX,1; б) SHR DX,CL; в) SHL DX,CL; г) SHL  DL,1;
д) ROR DX,CL; е) ROR DL,CL; ж) SAL DH,1.

     7.13.  Используя  команды  сдвига,  пересылки  и  сложения,  умножьте
содержимое регистра AX на 10.

     7.14.  Пример  программы,  приведенной  в  конце  раздела  "сдвиг   и
циклический  сдвиг",  умножает  содержимое  пары  регистров  DX:AX  на  2.
Измените программу для: а) умножения на 4; б) деления на 4;  в)  умножения
48 бит в регистрах DX:AX:BX на 2.



ГЛАВА 8                             Экранные операции I: Основные свойства
__________________________________________________________________________

     Ц е л ь: Объяснить требования для вывода информации на экран, а также
для ввода данных с клавиатуры.


     ВВЕДЕНИЕ
     ________________________________________________________________

     В предыдущих главах мы имели дело с  программами,  в  которых  данные
oпределялись  в  операндах   команд    (непосредственные    данные)    или
инициализировались  в  конкретных  полях  программы.  Число   практических
применений таких программ в действительности  мало.  Большинcтво  программ
требуют ввода данных с клавиатуры, диска или модема и  обеспечивают  вывод
данных  в  удобном  формате  на  экран,  принтер   или    диск.    Данные,
предназначенные для вывода на экран и  ввода  с  клавиатуры,  имеют  ASCII
формат.
     Для выполнения ввода и вывода используется команда INT  (прерывание).
Существуют различные требования для указания системе какое действие  (ввод
или вывод) и  на  каком  устройстве  необходимо  выполнить.  Данная  глава
раскрывает основные требования для вывода  информации  на  экран  и  ввода
данных с клавиатуры.
     Все необходимые экранные  и  клавиатурные  операции  можно  выполнить
используя команду INT 10H, которая передает управление  непосредственно  в
BIOS.  Для  выполнения  некоторых  более  сложных   операций    существует
прерывание  более  высокого  уровня  INT  21H,  которое  сначала  передает
управление в DOS.  Например, при вводе с  клавиатуры  может  потребоваться
подсчет введенных символов, проверку  на  максимальное  число  символов  и
проверку на символ Return. Преpывание DOS INT 21H выполняет многие из этих
дополнительных вычислений и  затем  автоматически  передает  управление  в
BIOS.
     Материал данной главы подходит как для монохромных (черно-белых, BW),
так и  для  цветных  видеомониторов.  В  гл.  9 и 10 приведен материал для
управления более совершенными экранами и для использования цвета.


     КОМАНДА ПРЕРЫВАНИЯ: INT
     ________________________________________________________________

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

          - уменьшает указатель стека на  2  и  заносит  в  вершину  стека
     содержимое флагового регистра;
          - очищает флаги TF и IF;
          - уменьшает указатель стека на 2 и заносит  содержимое  регистра
     CS в стек;
          - уменьшает указатель стека на  2  и  заносит  в  стек  значение
     командного указателя;
          - обеспечивает выполнение необходимых действий;
          -  восстанавливает  из  стека  значение  регистра  и  возвращает
     управление в прерванную программу на команду, следующую после INT.

     Этот процесс выполняется  полностью  автоматически.  Необходимо  лишь
определить сегмент стека достаточно большим для  записи  в  него  значений
регистров.
     В данной главе рассмотрим два типа прерываний: команду BIOS INT 10H и


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

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



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


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