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

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


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

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

Под редакцией Еpшова В.Г.

     AH=0BH: Установка цветовой палитры.  Число в регистре  BH  определяет
назначение регистра BL:

          BH=00 выбирает цвета фона и бордюра в соответствии с  содержимым
     pегистра BL.  Цвет фона от 1 до 16 соответствует шест. значениям oт 0
     до F;
          BH=01 выбирает палитру соответственно содержимому регистра BL (0
     или 1):

               MOV  AH,0BH    ;Функция установки цвета
               MOV  BH,01     ;Выбор палитры
               MOV  BL,00     ; 0 (зеленый, красный, корич.)
               INT  10H       ;Вызвать BIOS

     Палитра, установленная один раз, сохраняется, пока не будет  отменена
другой командой.  При смене палитры весь экран меняет цветовую комбинацию.
При  использовании  функции  AH=0BH  в   текстовом    режиме,    значение,
установленное для цвета 0 в палитре, определяет цвет бордюра.

     AH=0CH: Вывод точки на экран.  Использование кода 0C  в  регистре  AH
позволяет вывести на экран  точку  в  выбранном  цвете  (фон  и  палитра).
Например, для  разрешения  320х200  загрузим  в  регистр  DX  вертикальную
координату (от 0 до 199), а в регистр CX - горизонтальную координату (от 0
до 319). В регистр AL поместим цвет точки (от 0 до 3):

               MOV  AH,0CH         ;Функция вывода точки
               MOV  AL,цвет        ;Цвет точки
               MOV  CX,столбец     ;Горизонтальная координата
               MOV  DX,строка      ;Вертикальная координата
               INT  10H            ;Вызвать BIOS

     AH=0DH: Чтение точки с экрана.  Данная  функция  позволяет  прочитать
точку для определения  ее  цвета.  В  регистр  DX  должна  быть  загружена
вертикальная координата (от 0 до 199), а в регистр CX - горизонтальная (от
0 до 319).  В регистре AH должно быть значение 0D. Функция возвращает цвет
точки в регистре AL.


     ПРОГРАММА: УСТАНОВКА ГРАФИЧЕСКОГО РЕЖИМА И ОТОБРАЖЕНИЕ ЦВЕТА
     ________________________________________________________________

     Программа, приведенная на рис.10.1, использует команду  INT  10H  для
установки графического режима, выбора зеленого  фона  и  вывода  на  экран
точек (40 строк  по  320  столбцов).  В  программе  происходит  увеличение
значения цвета на 1  для  каждой  строки.  Так  как  в  определении  цвета
участвуют только три правых бита,  цвета  повторяются  через  каждые  семь
строк.

__________________________________________________________________________

TITLE   GRAPHIX (COM) Пример цвета и графики
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H

MAIN    PROC    NEAR
        MOV     AN,00           ;Установка режима графики
        MOV     AL,0DH          ; для EGA (CGA=04)
        MOV     AH,0BH          ;Установить палитру
        MOV     BH,00           ;Фон
        MOV     BL,02           ;Зеленый
        INT     10H
        MOV     BX,00           ;Начальные цвет,
        MOV     CX,00           ; столбец
        MOV     DX,00           ; и строка
A50:
        MOV     AH,0CH          ;Функция вывода точки
        MOV     AL,BL           ;Установить цвет
        INT     10H             ;BX, CX, и DX сохраняются
        INC     CX              ;Увеличить столбец
        CMP     CX,320          ;Столбец 320?
        JNE     A50             ; нет - цикл,
        MOV     CX,00           ; да - сбросить столбец
        INS     BL              ;Изменить цвет
        INS     DX              ;Увеличить строку
        CMP     DX,40           ;Строка 40?
        JNE     A50             ; нет - цикл,
        RET                     ; да - завершить
MAIN    ENDP
CODESG  ENDS
        END     MAIN
__________________________________________________________________________

     Рис.10.1 Вывод на экран в цветном графическом режиме.


     После выполнения программы дисплей  остается  в  графическом  режиме.
Восстановление текстового режима возможно с помощью команды DOS MODE (MODE
CO80) или  пользовательской  COM  программой,  в  которой  для  этой  цели
используется команда INT 10H.


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

     -  Память  объемом  16К  для  цветного  дисплея  позволяет    хранить
дополнительные страницы (экраны).  Возможны четыре страницы для экранов на
80 столбцов или восемь страниц для экранов на 40 столбцов.

     - Графический режим обеспечивает низкое разрешение (не поддерживается
в ROM), среднее разрешение (для цветной графики) и высокое разрешение (для
черно-белой графики).

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

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


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

    10.1. Сколько цветов возможно для фона и  для  текста  на  стандартном
цветном адаптере (CGA) в текстовом режиме?

    10.2. Напишите байты атрибуты в двоичном формате для а) сиреневого  на
ярко-голубом, б) коричневого на желтом, в)красного на сером с миганием.

    10.3. Объясните разницу в количестве  цветов,  возможных  при  низком,
среднем и высоком разрешении.

    10.4. Напишите  команды  для  вывода  пяти  символов  карточной  масти
"бубны" в текстовом режиме ярко-зеленым цветом на сиреневом фоне.

    10.5. Напишите команды для установки графического режима с разрешением
а) 320х200 в адаптере CGA и б) 640х200 в адаптере EGA.

    10.6. Напишите команды для установки синего фона в графическом режиме.

    10.7. Напишите команды для чтения точки на 12 строке и  13  столбце  в
графическом режиме.

    10.8. Модифицируйте программу на рис.10.1 для:  а) графического режима
на вашем мониторе; б) красного фона; в) строк с 10 по 30; г) столбцов с 20
по 300.



ГЛАВА 11                                           Команды обработки строк
__________________________________________________________________________

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


     ВВЕДЕНИЕ
     ________________________________________________________________

     Команды, показанные в предыдущих главах,  оперировали  одним  байтом,
или одним словом за одно  выполнение.  Часто,  однако,  бывает  необходимо
переслать или сравнить поля данных, которые превышают по длине одно слово.
Например,  необходимо  сравнить  описания  или  имена  для  того,    чтобы
отсортировать их в восходящей последовательности.  Элементы такого формата
известны как  строковые  данные  и  могут являться как символьными,  так и
числовыми.  Для обработки строковых данных  ассемблер  имеет  пять  команд
обработки строк:

MOVS -    переслать один байт или одно слово из  одной  области  памяти  в
          другую;
LODS -    загрузить из памяти один байт в регистр  AL  или  одно  слово  в
          регистр AX;
STOS -    записать содержимое регистра AL или AX в память;
CMPS -    сравнить содержимое двух областей памяти, размером  в  один байт
          или в одно слово;
SCAS -    сравнить содержимое регистра AL или AX с содержимым памяти.

     Префикс REP позволяет этим командам обрабатывать строки любой длины.


     СВОЙСТВА ОПЕРАЦИЙ НАД СТРОКАМИ
     ________________________________________________________________

     Цепочечная команда  может   быть   закодирована   для   повторяющейся
обpаботки  одного  байта  или  одного слова за одно выполнение.  Например,
можно выбрать "байтовую" команду для обработки строки  с  нечетным  числом
байт  или  "двухбайтовую"  команду для обработки четного числа байт.  Ниже
перечислены регистры,  участвующие в цепочечных командах (для однобайтовых
и  двухбайтовых  вариантов).  Предположим,  что  регистры DI и SI содержат
необходимые адреса:

               Команда   Операнды            Байт      Слово

               MOVS      DI,SI               MOVSB     MOVSW
               LODS      AL,SI или AX,SI     LODSB     LODSW
               STOS      DI,AL или DI,AX     STOSB     STOSW
               CMPS      SI,DI               CMPSB     CMPSW
               SCAS      DI,AL или DI,AX     SCASB     SCASW

     Например, можно кодировать операнды для команды MOVS, но опустить  их
для MOVSB и MOVSW. Эти команды предполагают, что pегистры DI и SI содержат
относительные адреса,  указывающие  на  необходимые  области  памяти  (для
загрузки можно использовать команду  LEA).  Регистр  SI  обычно  связан  с
регистром сегмента данных - DS:SI.  Регистр DI всегда связан  с  регистром
дополнительного сегмента - ES:DI.  Следовательно, команды MOVS, STOS, CMPS
и SCAS требуют инициализации регистра ES (обычно адресом в регистре DS).


     REP: ПРЕФИКС ПОВТОРЕНИЯ ЦЕПОЧЕЧНОЙ КОМАНДЫ
     ________________________________________________________________

     Несмотря на то, что цепочечные команды имеют отношение к одному байту
или одному слову, префикс REP обеспечивает  повторение  команды  несколько
раз.  Префикс  кодируется  непосредственно  перед  цепочечной    командой,
например, REP MOVSB.  Для использования префикса REP необходимо установить
начальное значение в регистре CX.  При  выполнении  цепочечной  команды  с
префиксом REP происходит уменьшение на 1 значения в регистре CX  до  нуля.
Таким образом, можно обрабатывать строки любой длины.
     Флаг направления определяет направление повторяющейся операции:

          - для направления слева направо необходимо с помощью команды CLD
     установить флаг DF в 0;
          - для направления справа налево необходимо с помощью команды STD
     установить флаг DF в 1.

     В следующем примере  выполняется  пересылка  20  байт  из  STRING1  в
STRING2. Предположим, что оба регистра DS и  ES  инициализированы  адресом
сегмента данных:

               STRING1   DB   20 DUP('*')
               STRING2   DB   20 DUP(' ')
                         ...
                         CLD                 ;Сброс флага DF
                         MOV  CX,20          ;Счетчик на 20 байт
                         LEA  DI,STRING2     ;Адрес области "куда"
                         LEA  SI,STRING1     ;Адрес области "откуда"
                         REP MOVSB           ;Переслать данные

     При  выполнении  команд  CMPS  и  SCAS  возможна  установка    флагов
состояния, так чтобы операция могла прекратиться сразу  после  обнаружения
необходимого условия.  Ниже приведены модификации префикса  REP  для  этих
целей:

REP -               повторять операцию, пока CX не равно 0;
REPZ или REPE -     повторять операцию, пока флаг ZF показывает "равно или
                    ноль". Прекратить операцию при флаге ZF,  указывающему
                    на не равно или не ноль или при CX равном 0;
REPNE или REPNZ -   повторять операцию,  пока флаг ZF показывает "не равно
                    или  не  ноль".  Прекратить  операцию  при  флаге  ZF,
                    указывающему на "равно или нуль" или при CX равным 0.

Для  процессоров  8086,  80286  и  80386,  обрабатывающих  слово  за  oдно
выполнение, использование цепочечных команд, где это возможно, приводит  к
повышению эффективности работы программы.


     MOVS: ПЕРЕСЫЛКА СТРОК
     ________________________________________________________________

     На рис.7.5 была  показана  программа  для  пересылки  девятибайтового
поля.  Программа включала три команды для инициализации и пять команд  для
цикла. Команда MOVS с префиксом REP и длиной в регистре CX может выполнять
пересылку любого числа символов более эффективно.
     Для области,  принимающей  строку,  сегментным  регистром,   является
pегистр ES,  а регистр DI содержит относительный адрес области, передающей
строку.  Сегментным регистром является регистр DS,  а регистр SI  содержит
относительный адрес.  Таким образом,  в начале программы перед выполнением
команды MOVS необходимо инициализировать регистр ES вместе с регистром DS,
а также загрузить требуемые относительные адреса полей в регистры DI и SI.
В зависимости от состояния флага DF команда MOV  S  производит  увеличение
или уменьшение на 1 (для байта) или на 2 (для слова) содержимого регистров
DI и SI.
     Приведем команды, эквивалентные цепочечной команде REP MOVSB:

                 JCXZ      LABEL2
     LABEL1:     MOV       AL,[SI]
                 MOV       [DI],AL
                 INC/DEC   DI        ;Инкремент или декремент
                 UNC/DEC   SI        ;Инкремент или декремент
                 LOOP      LABEL1
     LABEL2:     ...

     В программе на рис.11.1 процедура C10MVSB  использует  команду  MOVSB
для пересылки содержимого десятибайтового поля NAME1 в поле NAME2.  Первая
команда CLD сбрасывает флаг  направления  в  0  для  обеспечения  процесса
пересылки слева направо.  В нормальном  состоянии  флаг  DF  обычно  имеет
нулевое значение и команда CLD используется из предосторожности.

__________________________________________________________________________

        page    60,132
TITLE   STRING  (EXE)  Проверка строковых операций
; ---------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
        DW      32 DUP(?)
STACKG  ENDS
; ---------------------------------------------------
DATASG  SEGMENT PARA 'Data'
NAME1   DB      'Assemblers'    ;Элементы данных
NAME2   DB      10 DUP(' ')
NAME3   DB      10 DUP(' ')
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    C10MVSB         ;Подпрограмма MVSB
        CALL    D10MVSW         ;Подпрограмма LODS
        CALL    E10LODS         ;Подпрограмма LODS
        CALL    F10STOS         ;Подпрограмма CMPS
        CALL    H10SCAS         ;Подпрограмма SCAS
        RET
BEGIN   ENDP
;               Использование MOVSB:
;               -------------------
C10MVSB PROC    NEAR
        CLD
        LEA     SI,NAME1
        LEA     D1,NAME2
        MOV     CX,10           ;Переслать 10 байтов
        REP MOVSB               ; из NAME1 в NAME2
        RET
C10MVSB ENDP
;               Использование MOVSW:
;               -------------------
D10MVSW PROC    NEAR
        CLD
        LEA     SI,NAME2
        LEA     DI,NAME3
        MOV     CX,05           ;Переслать 5 слов
        REP MOVSW               ; из NAME2 в NAME3
        RET
D10MVSW ENDP
;               Использование LODSW:
;               -------------------
E10LODS PROC    NEAR
        CLD
        LEA     SI,NAME1        ;Загрузить первое слово
        LODSW                   ; из NAME1 в AX
        RET
E10LODS ENDP
;               Использование STOSW:
;               -------------------
F10STOS PROC    NEAR
        CLD
        LEA     D1,NAME3
        MOV     CX,05
        MOV     AX,2020H        ;Переслать пробелы
        REP STOSW               ; в NAME3
        RET
F10STOS ENDP
;               Использование CMPSB:
;               -------------------
G10CMPS PROC    NEAR
        CLD
        MOV     CX,10
        LEA     SI,NAME1
        LEA     DI,NAME2
        REPE CMPSB              ;Сравнить NAME1 и NAME2
        JNE     G20             ;Не равны?
        MOV     BH,01

G20:    MOV     CX,10
        LEA     SI,NAME2
        LEA     DI,NAME3
        REPE CMPSB              ;Сравнить NAME2 и NAME3
        JE      G30             ;Если равны, то выйти
        MOV     BL,02
G30:    RET
G10CMPS ENDP

;               Использование SCASB:
;               -------------------
H10SCAS PROC    NEAR
        CLD
        MOV     CX,10
        LEA     DI,NAME1
        MOV     AL,'m'          ;Поиск символа 'm'
        REPNE SCASB             ; в NAME1
        JNE     H20             ;Если не найден - выйти
        MOV     AH,03
H20:    RET
H10SCAS ENDP

CODES   ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.11.1. Использование цепочечных команд.


     Две команды LEA загружают регистры SI и  DI  относительными  адресами
NAME1 и NAME2  соответственно.  Так  как  регистры  DS  и  ES  были  ранее
инициализированы адресом DATASG, то полные  адреса  полей  NAME1  и  NAME2
будут  в  регистрах  ES:DI  и  DS:SI.   (COM    программа    автоматически
инициализирует регистры ES  и  DS).  Команда  MOV  заносит  в  регистр  CX
значение 10 - длину полей NAME1  и  NAME2.  Команда  REP  MOVSB  выполняет
следующее:

          - Пересылает самый  левый  байт  из  поля  NAME1  (адресованного
     pегистрами  ES:DI)  в  самый  левый  байт  поля  NAME2 (адресованного
     регистрами DS:SI).
          - Увеличивает на 1 адреса в регистрах DI  и  SI  для  следующего
     байта.
          - Уменьшает CX на 1.
          - Повторяет перечисленные действия (в  данном  случае  10  раз),
     пока содержимое регистра CX не станет равным нулю.

     Поскольку флаг DF имеет нулевое значение, команда  MOVSB  увеличивает
адреса в регистрах DI и SI, и в каждой итерации процесс переходит на  байт
вправо, т.е. пересылает байт из NAME1+1 в NAME2+1 и т.д.  Если бы флаг  DF
был равен 1, тогда команда MOVSB уменьшала бы адреса в регистрах DI и  SI,
выполняя процесс справа  налево.  Но  в  этом  случае  регистры  SI  и  DI
необходимо инициализировать адресами последних байтов полей, т.е.  NAME1+9
и NAME2+9 соответственно.
     В  процедуре  D10MVSW  (рис.11.1)   используется    команда    MOVSW,
пересылающая  одно  слово  за  одно  выполнение.  Так  как  команда  MOVSW
увеличивает адреса в регистрах DS и SI на 2, операция требует только  пять
циклов.  Для процесса пересылки  справа  налево  регистр  SI  должен  быть
инициализирован адресом NAME1+8, а регистр DI - NAME2+8.


     LODS: ЗАГРУЗКА СТРОКИ
     ________________________________________________________________

     Команда LODS загружает из памяти в регистр AL один байт или в регистр
AX одно слово.  Адрес памяти определяется регистрами DS:SI. В  зависимости
от значения флага DF происходит увеличение или уменьшение регистра SI.
     Поскольку одна команда LODS загружает регистр, то практической пользы
от префикса REP в данном случае нет.  Часто простая команда MOV  полностью
адекватна команде LODS, хотя MOV генерирует три байта  машинного  кода,  а
LODS  -  только  один,  но  требует  инициализацию  регистра  SI.    Можно
использовать команду LODS в том случае, когда требуется продвигаться вдоль
строки (по байту или по слову), проверяя загружаемый регистр на конкретное
значение.
     Команды, эквивалентные команде LODSB:

               MOV  AL,[SI]
               INC  SI

     На рис.11.1 процедура  E10LODS  демонстрирует  использование  команды
LODSW.  В примере обрабатывается только одно слово: первый байт из области
NAME1 (содержащий As) заносится в регистр AL, а второй байт  -  в  регистр
AH. В результате в регистре AX получится значение sA.


     STOS: ЗАПИСЬ СТРОКИ
     ________________________________________________________________

     Команда STOS записывает (сохраняет) содержимое регистра AL или  AX  в
байте или в слове памяти.  Адрес памяти всегда  представляется  регистрами
ES:DI.  В зависимости от флага  DF  команда  STOS  также  увеличивает  или
уменьшает адрес в регистре DI на 1 для байта или на 2 для слова.
     Практическая польза команды STOS  с  префиксом  REP  -  инициализация
области данных конкретным значением, например, очистка дисплейного  буфера
пробелами. Длина области (в байтах или в cловах) загружается в регистр AX.
Команды, эквивалентные команде REP STOSB:

                    JCXZ      LABEL2
          LABEL1:   MOV       [DI],AL
                    INC/DEC   DI        ;Инкремент или декремент
                    LOOP      LABEL1
          LABEL2:   ...

     На рис.11.1 процедура  F10STOS  демонстрирует  использование  команды
STOSW.  Операция осуществляет запись  шест.  2020  (пробелы)  пять  раз  в
область NAME3, причем значение из регистра AL заносится в первый  байт,  а
из регистра AH - во второй.  По завершении  команды  регистр  DI  содержит
адрес NAME3+10.


     CMPS: СРАВНЕНИЕ СТРОК
     ________________________________________________________________

     Команда CMPS сравнивает содержимое одной области  памяти  (адресуемой
регистрами DS:SI) с содержимыми другой области (адресуемой как  ES:DI).  В
зависимости от флага DF  команда  CMPS  также  увеличивает  или  уменьшает
адреса в регистрах SI и DI на 1 для байта или на 2 для слова. Команда CMPS
устанавливает флаги AF, CF, OF, PF, SF и ZF.  При  использовании  префикса
REP в регистре CX должна находиться длина сравниваемых полей. Команда CMPS
может сравнивать любое число байт или слов.
     Рассмотрим процесс сравнения двух  строк,  содержащих  имена  JEAN  и
JOAN. Сравнение побайтно слева направо приводит к следующему:

               J  :  J        Равно
               E  :  O        Не равно (E меньше O)
               A  :  A        Равно
               N  :  N        Равно

     Сравнение  всех  четырех  байт  заканчивается  сравнением    N:N    -
pавно/нуль.  Так как имена "не равны", операция должна  прекратиться,  как
только будет обнаружено условие "не равно".  Для этих  целей  команда  REP
имеет модификацию REPE, которая  повторяет  сравнение  до  тех  пор,  пока
сравниваемые элементы равны, или регистр  CX  не  pавен  нулю.  Кодируется
повторяющееся однобайтовое сравнение следующим образом:

               REPE CMPSB

     На рис.11.1 в процедуре G10CMPS  имеются  два  примера  использования
команды CMPSB.  В первом примере происходит  сравнение  содержимого  полей
NAME1 и NAME2. Так как ранее команда MOVSB переслала содержимое поля NAME1
в поле NAME2, то команда  CMPSB  продолжается  на  всех  десяти  байтах  и
завершается  состоянием  pавно/нуль:  флаг  SF   получает    значение    0
(положительно) и флаг ZF - 1(нуль).
     Во втором примере сравнивается поля  NAME2  и  NAME3.  Ранее  команда
STOSW заполнила поле NAME3 пробелами,  поэтому  команда  CMPB  завершается
после сравнения первых же байт с  результатом  "больше/неравно":  флаг  SF
получает значение 0 (положительно) и флаг ZF - 0 (не нуль).
     Первый пример заканчивается с результатом "равно/нуль" и заносит 01 в
регистр BH.  Второй пример заканчивается с результатом "неравно" и заносит
02 в регистр BL.  При трассировке команд с помощью отладчика  DEBUG  можно
увидеть, что в конце процедуры G10CMPS регистр BX будет содержать значение
0102.
     Предупреждение!  Показанные  примеры  используют  команду  CMPSB  для
сравнения одного байта за одно выполнение. При использовании команды CMPSW
для  сравнения  одного  слова,  необходимо  инициализиpовать  регистр   CX
значением 5. Кроме того следует помнить, что команда CMPSW  при  сравнении
слов переставляет байты. Например, сравнивая имена SAMUEL и ARNOLD команда
CMPSW выбирает вместо SA и AR переставленные значения, т.е.  AS  и  RA.  В
результате  вместо  "больше"  получится  "меньше",    т.е.    неправильный
результат.  Таким образом команда  CMPSW  работает  правильно  только  при
сравнении строк, которые содержат числовые данные, определенные как DW, DD
или DQ.


     SCAS: СКАНИРОВАНИЕ СТРОК
     ________________________________________________________________

     Команда  SCAS  отличается  от  команды  CMPS  тем,   что    сканирует
(просматривает) строку на определенное значение байта или  слова.  Команда
SCAS сравнивает содержимое области памяти (адресуемой pегистрами ES:DI)  с
содержимым регистра AL или AX.  В зависимости от значения флага DF команда
SCAS также увеличивает или уменьшает адрес в регистре DI на  1  для  байта
или на 2 для слова.  Команда SCAS устанавливает флаги AF, CF, OF, PF, SF и
ZF.  При использовании префикса REP и значения длины в регистре CX команда
SCAS может сканировать строки любой длины.
     Команда SCAS особенно полезна, например, в текстовых редакторах,  где
программа должна сканировать строки,  выполняя  поиск  знаков  пунктуации:
точек, запятых и пробелов.
     На рис.11.1 процедура H10SCAS сканирует  область  NAME1  на  строчную
букву "m".  Так как команда SCASB  должна  продолжать  сканирование,  пока
результат сравнения -  "не  равно"  или  регистр  CX  не  равен  нулю,  то
используется префикс REPNE:

               REPNE SCASB

     Так как область NAME1 содержит слово "Assemblers", то  команда  SCASB
находит символ "m" в пятом сравнении.  При использовании  отладчика  DEBUG
для трассировки команд в конце процедуры H10SCAS можно увидеть в  регистре
AH значение 03 для индикации того, что  символ  "m"  найден.  Команда  REP
SCASB кроме того уменьшит значение регистра CX от 10 до 06.
     Команда SCASW сканирует в памяти слово  на  соответствие  значению  в
регистре AX.  При использовании команд LODSW или MOV для пересылки слова в
регистр AX, следует помнить, что первый байт будет в регистре AL, а второй
байт - в регистре AH.  Так как команда SCAS сравнивает  байты  в  обратной
последовательности, то oперация корректна.


     СКАНИРОВАНИЕ И ЗАМЕНА
     ________________________________________________________________

     В  процессе  обработки  текстовой   информации    может    возникнуть
необходимость замены определенных символов в тексте на  другие,  например,
подстановка  пробелов  вместо  различных   редактирующих    символов.    В
приведенном ниже фрагменте программы  осуществляется  сканирование  cтроки
STRING и замена символа амперсанд (&) на  символ  пробела.  Когда  команда
SCASB обнаружит символ &  (в  примере  это  будет  позиция  STRING+8),  то
операция сканирования прекратится  и  регистр  DI  будет  содержать  aдрес
STRING+9. Для получения адреса символа & необходимо  уменьшить  содержимое
DI на единицу и записать по полученному адресу символ пробела.

               STRLEN    EQU  15             ;Длина поля STRING
               STRING    DB   'The time&is now'
                         ...
                         CLD
                         MOV  AL,'&'         ;Искомый символ
                         MOV  CX,STRLEN      ;Длина поля STRING
                         LEA  DI,STRING      ;Адрес поля STRING
                         REPNE SCASB         ;Сканировать
                         JNZ  K20            ;Символ найден?
                         DEC  DI             ;Да - уменьшить адрес
                         MOV  BYTE PTR[DI],20H  ;Подставить пробел
               K20:      RET


     АЛЬТЕРНАТИВНОЕ КОДИРОВАНИЕ
     ________________________________________________________________

     При использовании  команд  MOVSB  или  MOVSW  ассемблер  предполагает
наличие  корректной  длины  строковых  данных  и  не  требует  кодирования
операндов в команде.  Для команды MOVS длина должна  быть  закодирована  в
операндах.  Например, если поля FLDA и FLDB определены как байтовые  (DB),
то команда
               REP  MOVS FLDA,FLDB

предполагает повторяющуюся пересылку байтов из поля FLDB в поле FLDA.  Эту
команду можно записать также в следующем виде:

               REP  MOVS ES:BYTE PTR[DI],DS:[SI]

Однако загрузка регистров DI и SI адресами FLDA и FLDB oбязательна в любом
случае.


     ДУБЛИРОВАНИЕ ОБРАЗЦА
     ________________________________________________________________

     Команда  STOS  бывает  полезна  для  установки  в  некоторой  области
oпределенных значений байтов  и  слов.  Для  дублирования  образца,  длина
которого  превышает  размер  слова,  можно  использовать  команду  MOVS  с
небольшой модификацией.  Предположим, что необходимо  сформировать  строку
следующего вида:

               ***---***---***---***---***--- . . .

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

               PATTERN   DB   '***---'
               DISAREA   DB   42 DUP(?)
                         .
                         .
                         CLD
                         MOV  CX,21
                         LEA  DI,DISAREA
                         LEA  SI,PATTERN
                         REP MOVSW

В процессе выполнения команда MOVSW сначала пересылает первое  слово  (**)
из образца PATTERN в первое слово области DISAREA, затем  -  второе  слово
(*-), потом третье (--):

                    ***---***---
                    |     |
               PATTERN   DISAREA

К этому моменту регистр DI будет содержать адрес DISAREA+6, а pегистр SI -
PATTERN+6, который также является адресом  DISAREA.  Затем  команда  MOVSW
автоматически дублирует образец,  пересылая  первое  слово  из  DISAREA  в
DISAREA+6, из DISAREA+2, в DISAREA+8, из DISAREA+4 в DISAREA+10 и  т.д.  В
результате образец будет полностью продублирован по всей области DISAREA:

                    ***---***---***---***---***--- . . . ***---
                    |           |           |            |
               PATTERN      DISAREA+6   DISAREA+12   DISAREA+42

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


     ПРОГРАММА: ВЫРАВНИВАНИЕ ВПРАВО ПРИ ВЫВОДЕ НА ЭКРАН
     ________________________________________________________________

     COM-программа, изображенная  на  рис.11.2,  иллюстрирует  почти  весь
материал,  приведенный  в  этой  главе.  Процедуры  программы    выполняют
следующие действия:

B10INPT - Принимает имена длиной до 30 символов, вводимых вверху экрана.
D10SCAS - Использует команду SCASB для сканирования имен и об хода  любого
          ввода, содержащего символ "звездочка".
E10RGHT - Использует команду MOVSB для выравнивания имен по правой границе
          выводит имена  в  колонку  в  правой части экрана.  Длина в поле
          ACTNLEN из списка параметров ввода используется  для  вычисления
          самого правого символа в имени, например:

                               JEROME KERN
                         OSCAR HAMMERSTEIN
                            RICHARD ROGERS

F10CLNM - Использует команду STOSW для очистки области имени в памяти.

__________________________________________________________________________

        page    60,132
TITLE   EXRING  (COM) Вывод имен, выровненных справа
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG,ES:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
;--------------------------------------------------------
NAMEPAR LABEL   BYTE            ;Имя списка параметров
MAXNLEN DB      31              ;Макс. длина
ACTNLEN DB      ?               ;Число введенных символов
NAMEFLD DB      31 DUP(' ')     ;Имя

PROMPT  DB      'Name?', '$'
NAMEDSP DB      31 DUP(' '), 13, 10, '$'
ROW     DB      00
;--------------------------------------------------------
MAIN    PROC    NEAR            ;Основная процедура
        MOV     AX,0600H
        CALL    Q10SCR          ;Очистить экран
        SUB     DX,DX           ;Установить курсор в 00,00
        CALL    Q20CURS
A10LOOP:
        CALL    B10INPT         ;Ввести имя с клавиатуры
        TEST    ACTNLEN,0FFH    ;Нет имени? (т.е. конец)
        JZ      A90             ; да - выйти
        CALL    D10SCAS         ;Найти звездочку
        CMP     AL,'*'          ;Найдена?
        JE      A10LOOP         ; да - обойти
        CALL    E10RGHT         ;Выровнять имя справа
        CALL    A10LOOP
A90:    RET
MAIN    ENDP
;               Вывод запроса для ввода имени:
;               -----------------------------
B10INPT PROC
        MOV     AH,09
        LEA     DX,PROMPT       ;Выдать текст запроса
        INT     21H
        RET
B10INPT ENDP
;               Поиск звездочки в имени:
;               -----------------------
D10SCAS PROC
        CLD
        MOV     AL,'*'
        MOV     CX,30           ;Длина сканирования - 30
        LEA     DI,NAMEFLD
        REPNE SCASB             ;Звездочка найдена?
        JE      D20             ; да - выйти,
        MOV     AL,20H          ; нет стереть * в AL
D20:    RET
D10SCAS ENDP
;               Выравнивание справа и вывод на экран:
;               ------------------------------------
E10RGHT PROC
        STD
        SUB     CH,CH
        MOV     CL,ACTNLEN      ;Длина в CX для REP
        LEA     SI,NAMEFLD      ;Вычислить самую правую
        ADD     SI,CX           ; позицию
        DEC     SI              ; введенного имени
        LEA     DI,NAMEDSP+30   ;Правая поз. поля имени
        REP MOVSB               ;Переслать справа налево
        MOV     DH,ROW
        MOV     DL,48
        CALL    Q20CURS         ;Установить курсор
        MOV     AH,09
        LEA     DX,NAMEDSP      ;Выдать имя на экран
        INT     21H
        CMP     ROW,20          ;Последняя строка экрана?
        JAE     E20             ; нет -
        INC     ROW             ; увеличить строку,
        JMP     E90
E20:
        MOV     AX,0601H        ; да -
        CALL    Q10SCR          ; прокрутить и
        MOV     DH,ROW          ; установить курсор
        MOV     DL,00
        CALL    Q20CURS
E90:    RET
E10RGHT ENDP
;               Очистить область имени:
;               ----------------------
F10CLNM PROC
        CLD
        MOV     AX,2020H
        MOV     CX,15           ;Очистить 15 слов
        LEA     DI,NAMEDSP
        REP STOSW
        RET
F10CLNM ENDP
;               Прокрутка экрана:
;               ----------------
Q10SCR  PROC                    ;AX установлен при вызове
        MOV     BH,30           ;Цвет ( 07 для ч/б)
        MOV     CX,00
        MOV     DX,184FH
        INT     10H
        RET
Q10SCR  ENDP
;               Установить курсор (строка/столбец):
;               ----------------------------------
Q20CURS PROC                    ;DX установлен при вызове
        MOV     AH,02
        SUB     BH,BH
        INT     10H
        RET
Q20CURS ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.11.2. Выравнивание вправо при выводе на экран.


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

     - Для  цепочечных  команд  MOVS,  STOS,  CMPS  и  SCAS  не  забывайте
инициализировать регистр ES.

     - Сбрасывайте  (CLD)  или  устанавливайте  (STD)  флаг  направления в
соответствии с направлением обработки.

     - Не  забывайте  устанавливать  в  регистрах  DI  и  SI   необходимые
значения.  Например,  команда MOVS предполагает операнды DI,SI,  а команда
CMPS - SI,DI.

     - Инициализируйте регистр CX в соответствии с количеством байтов  или
слов, участвующих в процессе обработки.

     - Для  обычной  обработки  используйте  префикс REP для команд MOVS и
STOS и модифицированный префикс (REPE или REPNE) для команд CMPS и SCAS.

     - Помните об обратной последовательности байтов в сравниваемых cловах
при выполнении команд CMPSW и SCASW.

     - При  обработке  справа  налево  устанавливайте  начальные адреса на
последний байт обрабатываемой области.  Если,  например,  поле NAME1 имеет
длину 10 байтов,  то для побайтовой обработки данных в этой области справа
налево начальный адрес, загружаемый командой LEA, должен быть NAME1+9. Для
обработки слов начальный адрес в этом случае - NAME1+8.


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

     11.1. В данной главе приведены эквивалентные команды для а) MOVSB, б)
LODSB и  в)  STOSB  с  префиксом  REP.  Напишите эквивалентные команды для
обработки по словам а) MOVSW, б) LODSW и в) STOSW с префиксом REP.

     11.2.  Введите,  ассемблируйте  и  выполните  компоновку   программы,
приведенной на рис.11.1. Не забудьте о инициализации регистра ES. Замените
команды MOVSB и MOVSW для  пересылки  справа  налево.  Измените  процедуру
H10SCAS для сканирования поля NAME1  на  слово  "mb".  Используя  отладчик
DEBUG для трассировки процедур,  обратите внимание на содержимое  сегмента
данных и регистров.

     11.3. Имеются следующие определения:

               DATASG    SEGMENT   PARA
                         CONAME    DB   'SPACE EXPLORERS INC.'
                         PRLINE    DB   20 DUP(' ')

Используя цепочечные команды, выполните:

     а) пересылку данных из CONAME в PRLINE слева направо;
     б) пересылку данных из CONAME в PRLINE справа налево;
     в) загрузку третьего и четвертого байтов области CONAME в регистр AX;
     г) сохранение содержимого регистра AX в область по адресу PRLINE+5;
     д) сравнение данных в областях CONAME и PRLINE (они должны быть не
        равны);
     е) сканирование областей CONAME и  PRLINE,  и  поиск  в  ней  символа
        пробел. Если символ будет найден, то переслать его в регистр BH.

     11.4. Переделайте процедуру H10SCAS (рис.11.1) так, чтобы выполнялось
сканирование поля NAME1 на символ "er".  Обратите  внимание,  что  символы
"er" не встречаются в поле NAME1 как  одно  слово:  /As/se/mb/le/rs/.  Для
решения этой проблемы возможны два варианта:

     а) использовать команду SCASW дважды, причем первая должна начинаться
        по адресу NAME1, а вторая - по адресу NAME1+1;
     б) использовать команду SCASB для поиска символа "е" и сравнить затем
        следующий байт на символ "r".

     11.5. Определите поле, содержащее шест. значения 03,  04,  05  и  B4.
Продублируйте это поле 20 раз и выдайте результат на экран.



ГЛАВА 12              Арифметические операции I: Обработка двоичных данных
__________________________________________________________________________

     Ц е л ь: Дать сведения об операциях сложения, вычитания, умножения  и
деления двоичных данных.


     ВВЕДЕНИЕ
     ________________________________________________________________

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


     СЛОЖЕНИЕ И ВЫЧИТАНИЕ
     ________________________________________________________________

     Команды ADD и SUB выполняют сложение и  вычитание  байтов  или  слов,
содержащих двоичные данные.  Вычитание выполняется в компьютере по  методу
сложения с двоичным  дополнением:  для  второго  операнда  устанавливаются
обратные значения бит и прибавляется 1,  а  затем  происходит  сложение  с
первым операндом.  Во  всем,  кроме  первого  шага,  операции  сложения  и
вычитания идентичны.
     На рис.12.1 представлены примеры команд  ADD  и  SUB,  обрабатывающие
байты или слова.  В процедуре B10ADD используется команда ADD для сложения
байтов,  а  в  процедуре  C10SUB  команда  SUB  вычитает  слова.   Примеры
показывают все пять возможных ситуаций:

               сложение/вычитание регистр-регистр;
               сложение/вычитание память-регистр;
               сложение/вычитание регистр-память;
               сложение/вычитание регистр-непоср.значение;
               сложение/вычитание память-непоср.значение.

__________________________________________________________________________

        page    60,132
TITLE   EXADD   (СОМ) Сложение и вычитание
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; --------------------------------------------
BYTEA   DB      64H                ;Элементы данных
BYTEB   DB      40H
BYTEC   DB      16H
WORDA   DW      4000H
WORDB   DW      2000H
WORDC   DW      1000H
; --------------------------------------------
MAIN    PROC    NEAR               ;Основная процедура:
        CALL    B10ADD             ;Вызвать сложение ADD
        CALL    C10SUB             ;Вызвать вычитание SUB
        RET
MAIN    ENDP
;               Пример сложения байт:
;               --------------------
B10ADD  PROC
        MOV     AL,BYTEA
        MOV     BL,BYTEB
        ADD     AL,BL              ;Регистр и регистр
        ADD     AL,BYTEC           ;Память и регистр
        ADD     BYTEA,BL           ;Регистр и память
        ADD     BL,10H             ;Непосредств. и регистр
        ADD     BYTEA,25H          ;Непосредств. и память
        RET
B10ADD  ENDP
;               Пример вычитания слов:
;               ---------------------
C10SUB  PROC
        MOV     AX,WORDA
        MOV     BX,WORDB
        SUB     AX,BX              ;Регистр из регистра
        SUB     AX,WORDC           ;Память из регистра
        SUB     WORDA,BX           ;Регистр из памяти
        SUB     BX,1000H           ;Непосредств. из peг.
        SUB     WORDA,256H         ;Непосредств. из пам.
        RET
C10SUB  ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.12.1. Примеры команд ADD и SUB.


     Поскольку  прямой  операции  память-память  не  существует,    данная
oперация выполняется через регистр.  В  следующем  примере  к  содержимому
слова WORDB прибавляется содержимое слова WORDA, описанных как DW:

               MOV  AX,WORDA
               ADD  AX,WORDB
               MOV  WORDB,AX


                               Переполнения
                              --------------
     Опасайтесь  переполнений  в  арифметических  операциях.  Один    байт
содержит знаковый бит и семь бит данных, т.е. значения от  -128  до  +127.
Результат  арифметической  операции  может   легко    превзойти    емкость
однобайтового  регистра.  Например,  результат  сложения  в  регистре  AL,
превышающий  его  емкость,  автоматически  не  переходит  в  регистр   AH.
Предположим, что регистр AL содержит шест.60, тогда результат команды

               ADD  AL,20H

генерирует в  AL  сумму  -  шест.80.  Но операция также устанавливает флаг
переполнения  и  знаковый  флаг  в   состояние   "отрицательно".   Причина
заключается   в   том,   что  шест.80  или  двоичное  1000  0000  является
отрицательным числом. Т.е. в результате, вместо +128, мы получим -128. Так
как  регистр  AL  слишком мал для такой операции и следует воспользоваться
регистром AX.  В следующем примере команда CBW (Convert  Byte  to  Word  -
преобразовать  байт в слово) преобразует шест.60 в регистре AL в шест.0060
в регистре AX,  передавая при этом знаковый  бит  (0)  через  регистр  AH.
Команда   ADD  генерирует  теперь  в  регистре  AX  правильный  результат:
шест.0080, или +128:

               CBW            ;Расширение AL до AX
               ADD  AX,20H    ;Прибавить к AX

     Но полное слово имеет также ограничение: один знаковый бит и  15  бит
данных, что соответствует значениям от -32768 до +32767. Рассмотрим  далее
как можно обрабатывать числа, превышающие эти пределы.


                          Многословное сложение
                         -----------------------
     Максимальное  возможное  значение  в  регистре  +32767   ограничивает
возможность компьютера для выполнения арифметических операций.  Рассмотрим
два способа выполнения арифметических  операций.  Первый  способ  -  более
прост, но специфичен, второй - сложнее, но имеет общий характер.

__________________________________________________________________________

        page    60,132
TITLE   EXDBADD (COM) Пример сложения двойных слов
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; -------------------------------------------
WORD1A  DW      0123H              ;Элементы данных
WORD1B  DW      0BC62H
WORD2A  DW      0012H
WORD2B  DW      553AH
WORD3A  DW      ?
WORD3B  DW      ?
; -------------------------------------------
MAIN    PROC    NEAR               ;0сновная процедура:
        CALL    D10DWD             ;Вызвать сложение 1
        CALL    E10DWD             ;Вызвать сложение 2
        RET
MAIN    ENDP
;               Пример сложения двойных слов:
;               ----------------------------
D10DWD  PROC
        MOV     AX,WORD1B          ;Сложить правые слова
        ADD     AX,WORD2B
        MOV     WORD3B,AX
        MOV     AX,WORD1A          ;Сложить левые слова
        ADC     AX,WORD2A          ; с переносом
        MOV     WORD3A,AX
        RET
D10DWD  ENDP
;              Сложение чисел любой длины:
;              --------------------------
E10DWD  PROC
        CLC                        ;Очистить флаг переноса
        MOV     CX,2               ;Установить счетчик
        LEA     SI,WORD1B          ;Левое слово DWORD1
        LEA     DI,WORD2B          ;Левое слово DWORD2
        LEA     BX,WORD3B          ;Левое слово суммы
Е20:
        MOV     AX,[SI]            ;Поместить слово в AX
        ADC     AX,[DI]            ;Сложить с переносом
        MOV     [BX],AX            ;Сохранить слово
        DEC     SI
        DEC     SI
        DEC     DI
        DEC     DI
        DEC     BX
        DEC     BX
        LOOP    Е20                ;Повторить цикл
        RET
E10DWD  ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.12.2. Сложение двойных слов.


     На рис.12.2 процедура D10DWD демонстрирует  простой  способ  сложения
содержимого одной пары слов (WORD1A и WORD1B)  с  содержимым  второй  пары
слов (WORD2A и WORD2B) и сохранения суммы в третьей паре  слов  (WORD3A  и
WORD3B). Сначала выполняется сложение правых слов:

               WORD1B          BC62
               WORD2B          553A
                              -----
               Сумма:         1119C

     Сумма  -  шест.1119C  превышает  емкость  регистра  AX.  Переполнение
вызывает установку флага переноса в 1. Затем  выполняется  сложение  левых
слов, но в данном случае, вместо команды ADD используется команда сложения
с переносом ADC (ADd with Carry).  Эта команда складывает два значения,  и
если флаг CF уже установлен, то к сумме прибавляется 1:

               WORD1A         0123
               WORD2A         0012
               Плюс перенос      1
                              ----
               Сумма:         0136

     При использовании  отладчика  DEBUG  для  трассировки  арифметических
команд можно увидеть эту сумму 0136 в регистре  AX,  и  обpатные  значения
3601 в поле WORD3A и 9C11 в поле WORD3B.
     На рис.12.2 процедура E10DWD демонстрирует подход к сложению значений
любой  длины.  Действие  начинается  со  сложения  самых    правых    слов
складываемых полей.  В первом цикле складываются правые cлова, во втором -
слова, расположенные левее.  При этом адреса  в  регистрах  SI,  DI  и  BX
уменьшаются на 2. По две команда DEC выполняют эту  операцию  для  каждого
регистра. Применять команду

               SUB  reg,02

в данном случае нельзя, т.к. при этом  будет  очищен  флаг  переноса,  что
приведет к искажению результата сложения.
     Ввиду наличия цикла, используется только одна команда  сложения  ADC.
Перед  циклом  команда  CLC  (CLear  Carry  -  очистить  флаг    переноса)
устанавливает нулевое значение флага переноса.  Для работы данного  метода
необходимо: 1) обеспечить смежность слов, 2)  выполнять  обработку  справа
налево и 3) загрузить в регистр CX число складываемых слов.
     Для многословного вычитания используется команда SBB  (SuBtract  with
Borrow  -  вычитание  с  заемом)  эквивалентная  команде  ADC.  Заменив  в
процедуре E10DWD (рис.12.2) команду ADC  на  SBB,  получим  процедуру  для
вычитания.


     БЕЗЗНАКОВЫЕ И ЗНАКОВЫЕ ДАННЫЕ
     ________________________________________________________________

     Многие числовые поля не имеют знака, например, номер абонента,  aдрес
памяти.  Некоторые  числовые  поля  предлагаются  всегда    положительные,
например, норма выплаты, день недели, значение числа ПИ.  Другие  числовые
поля являются знаковые, так как их содержимое может быть положительным или
отрицательным.  Например, долговой баланс покупателя, который  может  быть
отрицательным при переплатах, или алгебраическое число.
     Для беззнаковых величин все биты  являются  битами  данных  и  вместо
ограничения +32767 регистр может содержать числа до +65535.  Для  знаковых
величин левый байт является знаковым битом.  Команды ADD и SUB  не  делают
разницы между знаковыми и беззнаковыми величинами, они просто складывают и
вычитают биты.  В следующем примере сложения двух двоичных  чисел,  первое
число  содержит  единичный  левый  бит.  Для  беззнакового   числа    биты
представляют положительное число 249, для знакового - отрицательное  число
-7:

                              Беззнаковое    Знаковое
               11111001            249            -7
               00000010              2            +2
                                   ---            --
               11111011            251            -5

Двоичное представление результата сложения одинаково  для  беззнакового  и
знакового числа.  Однако, биты представляют +251 для беззнакового числа  и
-5  для  знакового.  Таким  образом,  числовое  содержимое   поля    может
интерпретироваться по разному.
     Состояние "перенос" возникает в том случае, когда имеется  пеpенос  в
знаковый разряд.  Состояние "переполнение" возникает в том  случае,  когда
перенос в знаковый разряд не  создает  переноса  из  разрядной  сетки  или
перенос из разрядной сетки происходит без переноса в знаковый разряд.  При
возникновении переноса   при   сложении   беззнаковых   чисел,   результат
получается неправильный:

                              Беззнаковое    Знаковое  CF   OF
               11111100            252            -4
               00000101              5            +5
                                   ---            --
               00000001              1             1    1    0
                              (неправильно)

     При возникновении переполнения при сложении знаковых чисел, результат
получается неправильный:

                              Беззнаковое    Знаковое  CF   OF
               01111001            121          +121
               00001011             11           +11
                                   ---          ----
               10000100            132          -124    0    1
                                          (неправильно)

     При операциях сложения и вычитания может  одновременно  возникнуть  и
переполнение, и перенос:

                              Беззнаковое    Знаковое  CF   OF
               11110110            246           -10
               10001001            137          -119
                                   ---          ----
               01111111            127          +127    1    1
                              (неправильно)  (неправильно)


     УМНОЖЕНИЕ
     ________________________________________________________________

     Операция умножения для беззнаковых данных выполняется командой MUL, а
для знаковых - IMUL (Integer  MULtiplication  -  умножение  целых  чисел).
Ответственность за контроль над форматом обрабатываемых чисел и  за  выбор
подходящей команды умножения лежит на самом программисте.  Существуют  две
основные операции умножения:

     "Б а й т   н а   б а й т".   Множимое  находится  в  регистре  AL,  а
множитель в байте памяти или  в  однобайтовом  регистре.  После  умножения
произведение находится в регистре AX.  Операция игнорирует и стиpает любые
данные, которые находились в регистре AH.

                       | AH |   AL   |            |    AX      |
         До умножения: |    |Множимое|     После: |Произведение|

     "С л о в о  н а  с л о в о".  Множимое находится  в  регистре  AX,  а
множитель - в слове памяти или в регистре.  После  умножения  произведение
находится в двойном слове, для которого требуется  два  регистра:  старшая
(левая) часть произведения находится в регистре  DX,  а  младшая  (правая)
часть в регистре AX.  Операция игнорирует и стирает любые данные,  которые
находились в регистре DX.

                       |   AX   |          |   DX   ||   AX   |
          До умножения:|Множимое|   После: |Ст.часть||Мл.часть|
                                           |   Произведение   |

     В единственном операнде команд  MUL  и  IMUL  указывается  множитель.
Рассмотрим следующую команду:

               MUL  MULTR

     Если поле MULTR определено как байт (DB),  то  операция  предполагает
умножение содержимого AL на значение байта из поля MULTR.  Если поле MULTR
определено как слово (DW), то операция предполагает умножение  содержимого
AX на значение слова из поля MULTR.  Если множитель находится в  регистре,
то длина регистра определяет тип операции, как это показано ниже:

          MUL  CL  ;Байт-множитель: множимое в AL, произвед. в AX
          MUL  BX  ;Слово-множитель:множимое в AX, произвед. в DX:AX


                    Беззнаковое умножение: Команда MUL
                   ------------------------------------
     Команда MUL (MULtiplication - умножение) умножает беззнаковые  числа.
На рис.12.3  в процедуре C10MUL дано три примера умножения:  байт на байт,
слово на слово и слово на байт. Первый пример команды MUL умножает шест.80
(128) на шест.47 (64). Произведение шест.2000 (8192) получается в регистре
AX.

__________________________________________________________________________

        page 60,132
TITLE   EXMULT  (COM) Пример команд умножения
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        OR6     100H
BEGIN:  JMP     SHORT MAIN
; -------------------------------------------
BYTE1   DB      80H
BYTE2   DB      40H
WORD1   DW      8000H
WORD2   DW      4000H
; -------------------------------------------
MAIN    PROC    NEAR               ;Основная процедура:
        CALL    C10MUL             ;Вызвать умнож. MUL
        CALL    D10IMUL            ;Вызвать умнож. IMUL
        RET
MAIN    ENDP
;               Пример умножения MUL:
;               --------------------
C10MUL  PROC
        MOV     AL,BYTE1           ;Байт * байт
        MUL     BYTE2              ; произведение в AХ
        MOV     AX,WORD1           ;Слово * слово
        MUL     WORD2              ; произведение в DX:AX
        MOV     AL,BYTE1           ;Байт * слово
        SUB     AН,AН              ; расшир. множ. в AН
        MUL     WORD1              ; произведение в DX:AX
        RET
C10MUL  ENDP
;               Пример умножения IMUL:
;               ---------------------
D10IMUL PROC
        MOV     AL,BYTE1           ;Байт * байт
        IMUL    BYTE2              ; произведение в AХ
        MOV     AX,WORD1           ;Слово * слово
        IMUL    WORD2              ; произвед. в DX:AX
        MOV     AL,BYTE1           ;Байт * слово
        CBW                        ; расшир. множ. в AН
        IMUL    WORD1              ; произвед. в DX:AX
        RET
D10IMUL ENDP

CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.12.3. Беззнаковое и знаковое умножение.


     Второй пример команды MUL генерирует шест.10000000 в регистpах DX:AX.
     Третий пример команды MUL выполняет умножение слова на байт и требует
расширение  байта  BYTE1  до  размеров  слова.  Так  как    предполагаются
беззнаковые величины, то в примере левый бит регистра AH равен нулю.  (При
использовании команды CBW значение левого бита регистpа AL  может  быть  0
или 1). Произведение - шест.00400000 получается в регистрах DX:AX.


                     Знаковое умножение: Команда IMUL
                    ----------------------------------
     Команда  IMUL  (Integer  MULtiplication  -  умножение  целых   чисел)
умножает знаковые числа.  На рис.12.3 в процедуре D10IMUL используются  те
же три примера умножения,  что и в процедуре C10MUL,  но вместо команд MUL
записаны команды IMUL.
     Первый пример команды IMUL умножает шест.80 (отрицательное число)  на
шест.40 (положительное  число).  Произведение  -  шест.E000  получается  в
регистре AX.  Используя те  же  данные,  команда  MUL  дает  в  результате
шест.2000, так что можно видеть разницу в использовании команд MUL и IMUL.
Команда MUL рассматривает шест.80 как +128, а команда IMUL - как  -128.  В
результате  умножения  -128  на  +64  получается  -8192  или    шест.E000.
(Попробуйте преобразовать шест.Е000 в десятичный формат).
     Второй  пример  команды  IMUL  умножает   шест.8000    (отрицательное
значение)  на  шест.2000  (положительное   значение).    Произведение    -
шест.F0000000  получается  в  регистрах  DX:AX  и    представляет    собой
oтрицательное значение.
     Третий пример команды  IMUL  перед  умножением  выполняет  расширение
байта  BYTE1  до  размеров  слова  в  регистре  AX.  Так   как    значения
предполагаются  знаковые,  то  в  примере  используется  команда  CBW  для
перевода левого знакового  бита  в  регистр  AH:  шест.80  в  pегистре  AL
превращается в шест.FF80 в регистре AX.  Поскольку множитель в слове WORD1
имеет также  отрицательное  значение,  то  произведение  должно  получится
положительное.  В самом деле: шест.00400000 в регистрах DX:AX -  такой  же
результат, как и в случае умножения  командой  MUL,  которая  предполагала
положительные сомножители.
     Таким образом, если множимое и множитель  имеет  одинаковый  знаковый
бит, то команды MUL и  IMUL  генерируют  одинаковый  результат.  Но,  если
сомножители имеют  разные  знаковые  биты,  то  команда  MUL  вырабатывает
положительный результат умножения, а команда IMUL - отрицательный.
     Можно  обнаружить  это,  используя  отладчик  DEBUG  для  трассировки
примеров.

     П о в ы ш е н и е  э ф ф е к т и в н о с т и  у м н о ж е н и я:  При
умножении на степень числа 2 (2,4,8 и  т.д.)  более  эффективным  является
сдвиг влево на требуемое  число  битов.  Сдвиг  более  чем  на  1  требует
загрузки величины сдвига в регистр CL.  В следующих примерах  предположим,
что множимое находится в регистре AL или AX:

               Умножение на 2:     SHL  AL,1
               Умножение на 8:     MOV  CL,3
                                   SHL  AX,CL


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

                                   1365
                                    х12
                                  -----
                                   2730
                                  1365
                                  -----
                                  16380

Представим, что десятичная арифметика  может  умножать  только  двузначные
числа. Тогда можно умножить 13 и 65 на 12 раздельно, cледующим образом:

                            13             65
                           х12            х12
                           ---            ---
                            26            130
                           13             65
                           ---            ---
                           156            780

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

                                  15600
                                   +780
                                  -----
                                  16380

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

     У м н о ж е н и е   д в о й н о г о    с л о в а    н а    с л о в о.
Процедура E10XMUL на рис.12.4 умножает двойное слово на  слово.  Множимое,
MULTCND, состоит из  двух  слов,  содержащих  соответственно  шест.3206  и
шест.2521.  Определение данных в виде двух слов (DW) вместо двойного слова
(DD) обусловлено  необходимостью  правильной  адресации  для  команд  MOV,
пересылающих слова  в  регистр  AX.  Множитель MULTPLR содержит шест.6400.
Область для записи произведения,  PRODUCT,  состоит из трех  слов.  Первая
команда MUL перемножает MULTPLR и правое cлово поля MULTCND;  произведение
- шест.0E80 E400 записывается в PRODUCT+2 и PRODUCT+4.  Вторая команда MUL
перемножает MULTPLR и левое слово поля MULTCND, получая в результате шест.
138A 5800. Далее выполняется сложение двух произведений следующим образом:

               Произведение 1:     0000 0E80 E400
               Произведение 2:     138A 5800
                                   --------------
               Результат:          138A 6680 E400

     Так как первая  команда  ADD  может  выработать  перенос,  то  второе
cложение выполняется командой сложения с переносом ADC (ADd with Carry). В
силу обратного представления байтов  в  словах  в  процессоpах  8086/8088,
область PRODUCT в действительности  будет  содержать  значение  8A13  8066
00E4. Программа предполагает, что первое слово  в  области  PRODUCT  имеет
начальное значение 0000.

__________________________________________________________________________

TITLE   EXDWMUL - Умножение двойных слов
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; ---------------------------------------------
MULTCND DW      3206H           ;Элементы данных
        DW      2521H
MULTPLR DW      6400H
        DW      0A26H
PRODUCT DW      0
        DW      0
        DW      0
        DW      0
; ---------------------------------------------
MAIN    PROC    NEAR            ;Основная процедура
        CALL    E10XMUL         ;Вызвать 1-е умножение
        CALL    Z10ZERO         ;Очистить произведение
        CALL    F10XMUL         ;Вызвать 2-е умножение
        RET
MAIN    ENDP
;               Умножение двойного слова на слово:
; -----------------------------------------------
E10XMUL PROC
        MOV     AX,MULTCND+2    ;Умножить правое слова
        MUL     MULTPLR         ; множимого
        MOV     PRODUCT+4,AX    ;Записать произведение
        MOV     PRODUCT+2,DX

        MOV     AX,MULTCND      ;Умножить левое слово
        MUL     MULTPLR         ; множимого
        ADD     PRODUCT+2,AX    ;Сложить с полученным ранее
        ADC     PRODUCT,DX
        RET
E10XMUL ENDP
;               Перемножение двух двойных слов:
; --------------------------------------------
F10XMUL PROC
        MOV     AX,MULTCND+2    ;Слово-2 множимого
        MUL     MULTPLR+2       ; * слово-2 множителя
        MOV     PRODUCT+6,AX    ;Сохранить результат
        MOV     PRODUCT+4,DX

        MOV     AX,MULTCND+2    ;Слово-2 множимого
        MUL     MULTPLR         ; * слово-1 множителя
        ADD     PRODUCT+4,AX    ;Сложить с предыдущим
        ADC     PRODUCT+6,DX
        ADC     PRODUCT,00      ;Прибавить перенос

        MOV     AX,MULTCND      ;Слово-1 множимого
        MUL     MULTPLR+2       ; * слово-2 множителя
        ADD     PRODUCT+4,AX    ;Сложить с предыдущим
       ADC     PRODUCT+6,DX
        ADC     PRODUCT,00      ;Прибавить перенос
        MOV     AX,MULTCND      ;Слово-1 множимого
        MUL     MULTPLR         ; * слово-1 множителя
        ADD     PRODUCT+2,AX    ;Сложить с предыдущим
        ADC     PRODUCT,DX
        RET
F10XMUL ENDP
;               Очистка области результата:
; ----------------------------------------
Z10XMUL PROC
        MOV     PRODUCT,0000
        MOV     PRODUCT+2,0000
        MOV     PRODUCT+4,0000
        MOV     PRODUCT+6,0000
        RET
Z10XMUL ENDP

CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.12.4. Многословное умножение.


     У м н о ж е н и е   "д в о й н о г о  с л о в а   н а   д в о й н о е
с л о в о". Умножение двух двойных слов включает следующие четыре операции
умножения:

               Множимое       Множитель

               слово 2   х    слово 2
               слово 2   х    слово 1
               слово 1   х    слово 2
               слово 1   х    слово 1

Каждое произведение в регистрах DX и  AX  складывается  с  соответствующим
словом в окончательном результате.  Пример  такого  умножения  приведен  в
процедуре F10XMUL на рис.12.4.  Множимое MULTCND содержит шест.3206  2521,
множитель MULTPLR - шест.6400 0A26. Результат заносится в область PRODUCT,
состоящую из четырех слов.
     Хотя логика умножения  двойных  слов  аналогична  умножению  двойного
слова на слово, имеется  одна  особенность,  после  пары  команд  сложения
ADD/ADC используется еще одна команда ADC, которая прибавляет 0 к значению
в поле PRODUCT.  Это необходимо потому, что первая команда ADC сама  может
вызвать перенос, который последующие команды могут стереть. Поэтому вторая
команда ADC прибавит 0, если переноса нет,  и  прибавит  1,  если  перенос
есть. Финальная пара команд ADD/ADC не требует дополнительной команды ADC,
так как область PRODUCT достаточно  велика  для  генерации  окончательного
результата и переноса на последнем этапе не будет.
     Окончательный результат 138A 687C 8E5C CCE6 получится в поле  PRODUCT
в обратной записи байт в словах.  Выполните трассировку  этого  примера  с
помощью отладчика DEBUG.


     СДВИГ РЕГИСТРОВОЙ ПАРЫ DX:AX
     ________________________________________________________________

     Следующая подпрограмма может  быть  полезна  для  сдвига  содержимого
pегистровой пары DX:AX вправо или влево. Можно придумать более эффективный
метод, но данный пример представляет общий подход для любого числа  циклов
(и, соответственно, сдвигов) в регистре CX. Заметьте, что сдвиг единичного
бита за разрядную сетку устанавливает флаг переноса.

                     Сдвиг влево на 4 бита
               MOV  CX,04     ;Инициализация на 4 цикла
          C20: SHL  DX,1      ;Сдвинуть DX на 1 бит влево
               SHL  AX,1      ;Сдвинуть AX на 1 бит влево
               ADC  DX,00     ;Прибавить значение переноса
               LOOP C20       ;Повторить
                     Сдвиг вправо на 4 бита
               MOV  CX,04     ;Инициализация на 4 цикла
          D20: SHR  AX,1      ;Сдвинуть AX на 1 бит вправо
               SHR  DX,1      ;Сдвинуть DX на 1 бит вправо
               JNC  D30       ;Если есть перенос,
               OR   AH,10000000B  ; то вставить 1 в AH
          D30: LOOP D20       ;Повторить

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

               MOV  CL,04     ;Установить фактор сдвига
               SHL  DX,CL     ;Сдвинуть DX влево на 4 бита
               MOV  BL,AH     ;Сохранить AH в BL
               SHL  AX,CL     ;Сдвинуть AX влево на 4 бита
               SHL  BL,CL     ;Сдвинуть BL вправо на 4 бита
               OR   DL,BL     ;Записать 4 бита из BL в DL


     ДЕЛЕНИЕ
     ________________________________________________________________

     Операция деления для беззнаковых данных выполняется командой  DIV,  a
для знаковых - IDIV. Ответственность за подбор подходящей команды лежит на
программисте. Существуют две основные операции деления:

     Д е л е н и е "с л о в а  н а  б а й т". Делимое находится в регистре
AX, а делитель - в байте памяти или а однобайтовом регистре. После деления
остаток получается в регистре AH, а частное - в AL.  Так как  однобайтовое
частное очень мало (максимально +255 (шест.FF) для беззнакового деления  и
+127 (шест.7F) для  знакового),  то  данная  операция  имеет  ограниченное
использование.

                        |  AX   |           |  AH   |  AL   |
            До деления: |Делимое|    После: |Остаток|Частное|

     Д е л е н и е  "д в о й н о г о  с л о в а  н а  с л о в о".  Делимое
находится в регистровой паре DX:AX, а делитель -  в  слове  памяти  или  а
регистре.  После деления остаток получается в регистре  DX,  а  частное  в
регистре AX.  Частное в одном слове допускает максимальное значение +32767
(шест.FFFF) для беззнакового деления и +16383 (шест.7FFF) для знакового.

                   |   DX   ||   AX   |         |  AH   ||  AL   |
       До деления: |Ст.часть||Мл.часть|  После: |Остаток||Частное|
                   |     Делимое      |

В единственном операнде команд DIV и IDIV указывается делитель. Рассмотрим
следующую команду:

               DIV  DIVISOR

     Если поле DIVISOR определено как байт (DB), то операция  предполагает
деление слова на байт.  Если поле DIVISOR определено как  слово  (DW),  то
операция предполагает деление двойного слова на слово.
     При делении, например, 13 на 3, получается результат 4  1/3.  Частное
есть 4, а остаток - 1. Заметим, что ручной калькулятор (или  программа  на
языке BASIC) выдает в этом случае результат  4,333....  Значение  содержит
целую часть (4) и дробную часть (,333). Значение 1/3 и 333... есть дробные
части, в то время как 1 есть остаток от деления.


                     Беззнаковое деление: Команда DIV
                    ----------------------------------
     Команда DIV делит беззнаковые числа.  На рис.12.5 в процедуре  D10DIV
дано четыре примера деления: слово на байт, байт на байт, двойное слово на
слово и слово на слово.  Первый пример команды DIV делит шест.2000  (8092)
на шест.80 (128).  В результате остаток 00 получается  в  регистре  AH,  а
частное шест.40 (64) - в регистре AL.
     Второй пример команды DIV выполняет прежде расширение байта BYTE1  до
размеров слова.  Так как здесь предполагается беззнаковая величина,  то  в
примере левый бит регистра AH равен нулю.  В результате деления остаток  -
шест.12 получается в регистре AH, а частное шест.05 - в регистре AL.
     Третий пример команды DIV генерирует остаток шест.1000 в регистре  DX
и частное шест.0080 в регистре AX.
     В четвертом примере команды DIV сначала выполняется расширение  слова
WORD1 до двойного слова в регистре DX.  После  деления  остаток  шест.0000
получится в регистре DX, а частное шест.0002 - в регистре AX.

__________________________________________________________________________

        page    60,132
TITLE   EXDIV   (COM) Пример операций DIV и IDIV
CODESG  SEGMENT PARA 'Code'
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; ---------------------------------------------
BYTE1   DB      80H           ;Data items
BYTE2   DB      16H
WORD1   DW      2000H
WORD2   DW      0010H
WORD3   DW      1000H
; ---------------------------------------------
MAIN    PROC    NEAR          ;Основная процедура
        CALL    D10DIV        ;Вызов подпрограммы DIV
        CALL    E10IDIV       ;Вызов подпрограммы IDIV
MAIN    ENDP
;               Примеры с командой DIV:
; ---------------------------------------------
D10DIV  PROC
        MOV     AX,WORD1      ;Слово / байт
        DIV     BYTE1         ; остаток:частное в AH:AL
        MOV     AL,BYTE1      ;Байт / байт
        SUB     AH,AH         ; расширить делимое в AH
        DIV     BYTE3         ; остаток:частное в AH:AL

        MOV     DX,WORD2      ;Двойное слово / слово
        MOV     AX,WORD3      ; делимое в DX:AX
        DIV     WORD1         ; остаток:частное в DX:AX
        MOV     AX,WORD1      ;Слово / слово
        SUB     DX,DX         ; расширить делимое в DX
        DIV     WORD3         ; остаток:частное в DX:AX
        RET
D10DIV  ENDP
;               Примеры с командой IDIV:
; ---------------------------------------------
E10IDIV PROC
        MOV     AX,WORD1      ;Слово / байт
        IDIV    BYTE1         ; остаток:частное в AH:AL
        MOV     AL,BYTE1      ;Байт / байт
        CBW                   ; расширить делимое в AH
        IDIV    BYTE3         ; остаток:частное в AH:AL

        MOV     DX,WORD2      ;Двойное слово / слово
        MOV     AX,WORD3      ; делимое в DX:AX
        IDIV    WORD1         ; остаток:частное в DX:AX
        MOV     AX,WORD1      ;Слово / слово
        CWD                   ; расширить делимое в DX
        IDIV    WORD3         ; остаток:частное в DX:AX
        RET
E10DIV  ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.12.5. Беззнаковое и знаковое деление.


                      Знаковое деление: Команда IDIV
                     --------------------------------
     Команда IDIV (Integer DIVide) выполняет деление  знаковых  чисел.  На
рис.12.5 в процедуре E10IDIV используются те же  четыре  примера  деления,
что и в процедуре D10DIV, но вместо  команд  DIV  записаны  команды  IDIV.
Первый пример  команды  IDIV  делит  шест.2000  (положительное  число)  на
шест.80 (отрицательное число).  Остаток от деления - шест. 00 получается в
регистре AH , а частное - шест.  C0 (-64) - в регистре  AL.  Команда  DIV,
используя те же числа, генерирует частное +64.
     Шестнадцатиричные  результаты  трех  остальных    примеров    деления
приведены ниже:

          Пример команды IDIV        Остаток        Частное

                      2              EE (-18)       FB (-5)
                      3            1000 (4096)    0080 (128)
                      4            0000           0002

Только в примере 4 вырабатывается такой же результат, что  и  для  команды
DIV. Таким образом, если делимое и делитель имеют одинаковый знаковый бит,
то команды DIV и IDIV генерируют одинаковый pезультат.  Но, если делимое и
делитель  имеют  разные  знаковые  биты,  то  команда    DIV    генерирует
положительное частное, а  команда  IDIV  -  отрицательное  частное.  Можно
обнаружить это, используя отладчик DEBUG для трассировки этих примеров.
     Повышение производительности. При делении на степень числа 2 (2, 4, и
т.д.) более эффективным является сдвиг вправо на требуемое число битов.  В
следующих примерах предположим, что делимое находится в регистре AX:

               Деление на 2:       SHR  AX,1

               Деление на 8:       MOV  CL,3
                                   SHR  AX,CL


                        Переполнения и прерывания
                       ---------------------------
     Используя  команды  DIV  и  особенно  IDIV,  очень  просто    вызвать
пеpеполнение. Прерывания приводят (по крайней мара в системе, используемой
при тестировании этих программ) к непредсказуемым результатам. В операциях
деления предполагается,  что  частное  значительно  меньше,  чем  делимое.
Деление на ноль всегда вызывает прерывание.  Но деление  на  1  генерирует
частное, которое равно делимому, что может также легко вызвать прерывание.
     Рекомендуется использовать следующее правило: если делитель  -  байт,
то его значение должно быть меньше, чем левый  байт  (AH)  делителя:  если
делитель - слово, то его значение должно быть меньше, чем левое слово (DX)
делителя. Проиллюстрируем данное правило для делителя, равного 1:

          Операция деления:          Делимое   Делитель  Частное

          Слово на байт:                0123        01     (1)23
          Двойное слово на слово:  0001 4026      0001   (1)4026

В обоих  случаях  частное  превышает  возможный  размер.  Для  того  чтобы
избежать подобных ситуаций, полезно вставлять перед командами DIV  и  IDIV
соответствующую проверку.  В первом из следующих примеpов предположим, что
DIVBYTE - однобайтовый делитель, а делимое находится уже в регистре AX. Во
втором примере предположим, что DIVWORD - двухбайтовый делитель, а делимое
находится в регистровой паре DX:AX.

               Слово на байт            Двойное слово на байт

               CMP  AH,DIVBYTE          CMP  DX,DIVWORD
               JNB  переполнение        JNB  переполнение
               DIV  DIVBYTE             DIV  DIVWORD

     Для команды IDIV данная логика должна учитывать тот  факт,  что  либо
делимое, либо делитель могут быть отрицательными, а так  как  сравниваются
абсолютные значения, то необходимо использовать команду NEG для временного
перевода отрицательного значения в положительное.


                            Деление вычитанием
                           --------------------
     Если частное слишком велико, то деление  можно  выполнить  с  помощью
циклического вычитания.  Метод заключается в том, что делитель  вычитается
из делимого и в этом  же  цикле  частное  увеличивается  на  1.  Вычитание
продолжается, пока делимое остается больше делителя.  В cледующем примере,
делитель находится в регистре AX, а делимое - в BX, частное вырабатывается
в CX:

                    SUB  CX,CX     ;Очистка частного
               C20: CMP  AX,BX     ;Если делимое < делителя,
                    JB   C30       ; то выйти
                    SUB  AX,BX     ;Вычитание делителя из делимого
                    INC  CX        ;Инкремент частного
                    JMP  C20       ;Повторить цикл
               С30: RET            ;Частное в CX, остаток в AX

     В конце подпрограммы регистр CX  будет  содержать  частное,  а  AX  -
oстаток.  Пример умышленно  примитивен  для  демонстрации  данной  техники
деления.  Если частное получается в регистровой паре DX:AX, то  необходимо
сделать два дополнения:

          1. В метке C20 сравнивать AX и BX  только при нулевом DX.
          2. После команды SUB вставить команду SBB DX,00.

     П р и м е ч а н и е: очень большое частное  и  малый  делитель  могут
вызвать тысячи циклов.


     ПРЕОБРАЗОВАНИЕ ЗНАКА
     ________________________________________________________________

     Команда NEG  обеспечивает  преобразование  знака  двоичных  чисел  из
положительного  в  отрицательное  и  наоборот.  Практически  команда   NEG
устанавливает противоположные значения битов и прибавляет 1. Примеры:

               NEG  AX
               NEG  BL
               NEG  BINAMT    ;(байт или слово в памяти)

     Преобразование знака для 35-битового (или  большего)  числа  включает
больше шагов.  Предположим, что регистровая пара DX:AX содержит 32-битовое
двоичное число.  Так как команда NEG не может  обрабатывать  два  регистра
одновременно, то ее использование приведет к неправильному  результату.  В
следующем примере показано использование команды NOT:

               NOT  DX   ;Инвертирование битов
               NOT  AX   ;Инвертирование битов
               ADD  AX,1 ;Прибавление 1 к AX
               ADC  DX,0 ;Прибавление переноса к DX

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


     ПРОЦЕССОРЫ INTEL 8087 И 80287 ДЛЯ ОБРАБОТКИ ЧИСЛОВЫХ ДАННЫХ
     ________________________________________________________________

     Системная плата компьютера содержит пустое гнездо,  зарезервированное
для  числового  процессора  Intel  8087  (или  80287).  Сопроцессор   8087
действует совместно с 8088, а  сопроцессор  80287  действует  совместно  с
80286. Каждый сопроцессор имеет собственный набор команд  и  средства  для
операций  с  плавающей   запятой    для    выполнения    экспоненциальных,
логарифмических и тригонометрических функций.  Сопроцессор содержит восемь
80-битовых  регистров  с  плавающей  запятой,  которые  могут  представить
числовые значения  до  10  в  400  степени.  Математические  вычисления  в
сопроцессоре выполняются примерно  в  100  раз  быстрее,  чем  в  основном
процессоре.
     Основной процессор выполняет специальные операции и передает числовые
данные  в  сопроцессор,  который  выполняет  необходимые   вычисления    и


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

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



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


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