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

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


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

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

Под редакцией Еpшова В.Г.
возвращает результат.  Для ассемблирования  с  помощью  транслятора  MASM,
необходимо добавлять параметр /E или /R, например, MASM /R.


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

     - Будьте  особенно   внимательны   при   использовании   однобайтовых
pегистров. Знаковые значения здесь могут быть от -128 до +127.

     - Для  многословного  сложения  используйте  команду  ADC  для  учета
переносов от предыдущих сложений.  Если операция выполняется в  цикле,  то
используя команду CLC, установите флаг переноса в 0.

     - Используйте  команды  MUL  или DIV для беззнаковых данных и команды
IMUL или IDIV для знаковых.

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

     - Для  умножения  или  деления  на  степень двойки используйте cдвиг.
Сдвиг вправо выполняется командой SHR для беззнаковых полей и командой SAR
для знаковых полей. Для сдвига влево используются идентичные команды SHL и
SAL.

     - Будьте внимательны при ассемблировании по умолчанию. Например, если
поле  FACTOR  определено  как  байт  (DB),  то команда MUL FACTOR полагает
множимое в регистре AL,  а команда DIV FACTOR полагает делимое в  регистре
AX.  Если FACTOR определен как слово (DW),  то команда MUL FACTOR полагает
множимое  в  регистре  AX,  а  команда  DIV  FACTOR  полагает  делимое   в
регистровой паре DX:AX.


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

     Все вопросы имеют отношение к следующим данным:

               DATAX     DW   0148H
                         DW   2316H
               DATAY     DW   0237H
                         DW   4052H

     12.1. Закодируйте команды для  сложения  а)  слова  DATAX  со  словом
DATAY; б) двойного слова, начинающегося по адресу DATAX, с двойным  словом
в DATAY.

     12.2. Объясните действие следующих команд:

               STC
               MOV BX,DATAX
               ADC BX,DATAY

     12.3. Закодируйте команды для умножения  (MUL):  а)  слова  DATAX  на
слово DATAY;  б) двойного слова ,  начинающегося по адресу DATAX, на слово
DATAY.

     12.4. Какой делитель, кроме нуля, вызывает ошибку переполнения?

     12.5. Закодируйте команды для деления (DIV): а) слова DATAX на 23; б)
двойного слова, начинающегося по адресу DATAX, на слово DATAY.

     12.6. Последний пример  в  разделе  "Сдвиг  регистровой  пары  DX:AX"
является более эффективным по сравнению с предыдущими примерами для сдвига
влево на четыре бита. Измените пример для сдвига вправо на четыре бита.



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

     Ц е л ь: Рассмотреть ASCII и BCD форматы данных  и  дать  сведения  о
преобразованиях между этими форматами и двоичным форматом.


     ВВЕДЕНИЕ
     ________________________________________________________________

     Для  получения  высокой  производительности    компьютер    выполняет
aрифметические операции над числами в двоичном  формате.  Как  показано  в
гл.12, этот формат не вызывает особых трудностей, если данные определены в
самой  программе.  Во  многих  случаях  новые данные вводятся программой с
клавиатуры в виде ASCII символов в деcятичном  формате.  Аналогично  вывод
информации  на  экран осуществляется в кодах ASCII.  Например,  число 23 в
двоичном представлении выглядит как 00010111 или шест.17;  в коде ASCII на
каждый cимвол требуется один байт и число 25 в ASCII-коде имеет внутpеннее
представление шест.3235.
     Назначение данной главы - показать технику преобразования  данных  из
ASCII-формата в двоичный формат для выполнения арифметических  операций  и
обратного преобразования двоичных результатов в ASCII-формат для вывода на
экран или принтер.  Программа, приведенная в конце главы  ,  демонстрирует
большую часть матеpиала гл.1 - 12.
     При программировании на языках высокого уровня, таких как  BASIC  или
Pascal, для обозначения порядка числа  или  положения  десятичной  запятой
(точки) можно положиться на компилятор.  Однако, компьютер  не  распознает
десятичную запятую (точку) в арифметических полях.  Так как двоичные числа
не имеют возможности установки десятичной (или двоичной) запятой  (точки),
то  именно  программист  должен  подразумевать  и    определить    порядок
обрабатываемых чисел.


     ASCII-ФОРМАТ
     ________________________________________________________________

     Данные, вводимые с клавиатуры,  имеют ASCII-формат,  например,  буквы
SAM  имеют  в памяти шестнадцатиричное представление 53414D,  цифры 1234 -
шест.31323334.  Во многих случаях формат алфавитных данных,  например, имя
человека или описание статьи,  не меняется в программе.  Но для выполнения
арифметических   операций   над   числовыми   значениями,    такими    как
шест.31323334, требуется специальная обработка.
     С  помощью  следующих    ассемблерных    команд    можно    выполнять
арифметические операции непосредственно над числами в ASCII-формате:

AAA (ASCII Adjust for Addition - коррекция для сложения ASCII-кода)
AAD (ASCII Adjust for Division - коррекция для деления ASCII-кода)
AAM (ASCII Adjust for Multiplication - коррекция для умножения ASCII-кода)
AAS (ASCII Adjust for Subtraction - коррекция для вычитания ASCII-кода)

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


                         Сложение в ASCII-формате
                        --------------------------
     Рассмотрим процесс сложения чисел 8 и 4 в ASCII-формате:

                         Шест.     38
                                   34
                                   --
                         Шест.     6C

Полученная сумма  неправильна  ни  для  ASCII-формата,  ни  для  двоичного
формата.  Однако, игнорируя левую 6 и прибавив 6 к правой шест.C: шест.C +
6  =  шест.12  -  получим  правильный  результат  в  десятичном   формате.
Правильный пример слегка упрощен,  но  он  хорошо  демонстрирует  процесс,
который выполняет команда AAA при коррекции.
     В качестве примера, предположим, что регистр AX содержит шест.0038, а
регистр BX   -   шест.0034.  Числа  38  и  34  представляют  два  байта  в
ASCII-формате, которые необходимо сложить. Сложение и коррекция кодируется
следующими командами:

               ADD  AL,BL     ;Сложить 34 и 38
               AAA            ;Коррекция для сложения ASCII-кодов

Команда AAA проверяет правую шест. цифру (4 бита) в регистре AL.  Если эта
цифра находится между A и F  или  флаг  AF  равен  1,  то  к  регистру  AL
прибавляется  6,  а  к  регистру  AH  прибавляется  1,  флаги  AF  и    CF
устанавливаются в 1. Во всех случаях команда AAA устанавливает в  0  левую
шест. цифру в регистре AL. Результат - в регистре AX:

               После команды ADD:  006C
               После команды AAA:  0102

     Для  того,  чтобы  выработать   окончательное    ASCII-представление,
достаточно просто поставить тройки на место левых шест. цифр:

               OR   AX,3030H  ;Результат 3132

     Все  показанное  выше  представляет  сложение  однобайтовых    чисел.
Сложение многобайтовых  ASCII-чисел  требует  организации  цикла,  который
выполняет обработку справа налево с учетом переноса.  Пример ,  показанный
на рис.13.1 складывает  два  трехбайтовых  ASCII-числа  в  четырехбайтовую
сумму. Обратите внимание на следующее:

__________________________________________________________________________

TITLE   ASCADD  (COM) Сложение чисел в ASCII-формате
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; -----------------------------------------------
ASC1    DB      '578'        ;Элементы данных
ASC2    DB      '694'
ASC3    DB      '0000'
; -----------------------------------------------
MAIN    PROC    NEAR
        CLC
        LEA     SI,AASC1+2   ;Адреса ASCII-чисел
        LEA     DI,AASC2+2
        LEA     BX,AASC1+3
        MOV     CX,03        ;Выполнить 3 цикла
A20:
        MOV     AH,00        ;Очистить регистр AH
        MOV     AL,[SI]      ;Загрузить ASCII-байт
        ADC     AL,[DI]      ;Сложение (с переносом)
        AAA                  ;Коррекция для ASCII
        MOV     [BX],AL      ;Сохранение суммы
        DEC     SI
        DEC     DI
        DEC     BX
        LOOP    A20          ;Циклиться 3 раза
        MOV     [BX],AH      ;Сохранить перенос
        RET
MAIN    ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.13.1. Сложение в ASCII-формате.


          - В программе используется команда ADC,  так как любое  сложение
     может  вызвать  перенос,  который  должен быть прибавлен к следующему
     (слева) байту. Команда CLC устанавливает флаг CF в нулевое состояние.
          - Команда MOV очищает регистр AH в каждом цикле, так как команда
     AAA может прибавить к нему единицу.  Команда ADC учитывает  пеpеносы.
     Заметьте,  что  использование команд XOR или SUB для oчистки регистра
     AH изменяет флаг CF.
          - Когда    завершается   каждый   цикл,   происходит   пересылка
     содержимого pегистра AH (00 или 01) в левый байт суммы.
          - В  результате  получается сумма в виде 01020702.  Программа не
     использует команду OR после команды AAA для занесения  левой  тройки,
     так как  при  этом  устанавливается  флаг  CF,  что изменит pезультат
     команды ADC.  Одним из решений в данном  случае  является  сохранение
     флагового регистра с помощью команды PUSHF, выполнение команды OR, и,
     затем, восстановление флагового регистра командой POPF:

               ADC  AL,[DI]   ;Сложение с переносом
               AAA            ;Коррекция для ASCII
               PUSHF          ;Сохранение флагов
               OR   AL,30H    ;Запись левой тройки
               POPF           ;Восстановление флагов
               MOV  [BX],AL   ;Сохранение суммы

     Вместо команд PUSHF и POPF можно использовать команды LAHF  (Load  AH
with Flags - загрузка флагов в регистр  AH)  и  SAHF  (Store  AH  in  Flag
register - запись флагов из регистра AH во флаговый регистр). Команда LAHF
загружает в регистр AH  флаги  SF,  ZF,  AF,  PF  и  CF;  а  команда  SAHF
записывает  содержимое  регистра  AH  в  указанные  флаги.  В  приведенном
примере,  однако,  регистр  AH  уже  используется    для    арифметических
переполнений. Другой способ вставки троек для получения ASCII-кодов цифр -
организовать обработку суммы командой OR в цикле.


                        Вычитание в ASCII-формате
                       ---------------------------
     Команда AAS (ASCII Adjust for Subtraction - коррекция  для  вычитания
ASCII-кодов) выполняется aналогично команде  AAA.  Команда  AAS  проверяет
правую шест. цифру (четыре бита) в регистре AL. Если эта цифра лежит между
A и F или флаг AF равен 1,  то из регистра AL вычитается 6,  а из регистра
AH  вычитается  1,  флаги  AF  и  CF устанавливаются в 1.  Во всех случаях
команда AAS устанавливает в 0 левую шест.цифру в регистpе AL.
     В следующих двух примерах  предполагается,  что  поле  ASC1  содержит
шест.38, а поле ASC2 - шест.34:

               Пример 1:                  AX      AF

                    MOV  AL,ASC1        ;0038
                    SUB  AL,ASC2        ;0034     0
                    AAS                 ;0004     0


               Пример 2:                  AX      AF

                    MOV  AL,ASC2        ;0034
                    SUB  AL,ASC1        ;00FC     1
                    AAS                 ;FF06     1

В примере 1 команде AAS не требуется выполнять коррекцию. В примере 2, так
как правая цифра в регистре AL равна шест.C, команда  AAS  вычитает  6  из
регистра AL и 1 из регистра  AH  и  устанавливает  в  1  флаги  AF  и  CF.
Результат (который должен быть равен -4) имеет шест.  представление  FF06,
т.е. десятичное дополнение числа -4.


                        Умножение в ASCII-формате
                       ---------------------------
     Команда  AAM  (ASCII  Adjust  for  Multiplication  -  коррекция   для
умножения ASCII-кодов)  выполняет   корректировку   результата   умножения
ASCII-кодов  в  регистре AX.  Однако,  шест.  цифры должны быть очищены от
троек  и  полученные  данные  уже  не   будут   являться   действительными
ASCII-кодами.  (В  руководствах  фирмы  IBM  для таких данных используется
термин pаспакованный десятичный формат).  Например,  число в ASCII-формате
31323334  имеет  распакованное  десятичное  представление 01020304.  Кроме
этого,  надо помнить, что коррекция осуществляется только для одного байта
за одно выполнение,  поэтому можно умножать только oдно-байтовые поля; для
более длинных полей необходима организация цикла.
     Команда AAM делит содержимое регистра AL на 10 (шест.0A) и записывает
частное в регистр AH,  а остаток в AL.  Предположим,  что  в  регистре  AL
содержится шест.35,  а в регистре CL - шест.39. Следующие команды умножают
содержимое  регистра  AL  на  содержимое  CL  и  преобразуют  результат  в
ASCII-формат:

                                                            AX:
               AND  CL,0FH    ;Преобразовать CL в 09
               AND  AL,0FH    ;Преобразовать AL в 05        0005
               MUL  CL        ;Умножить AL на CL            002D
               AAM            ;Преобразовать в распак.дес.  0405
               OR   AX,3030H  ;Преобразовать в ASCII-ф-т    3435

Команда MUL генерирует 45 (шест.002D) в регистре AX,  после  чего  команда
AAM делит это значение на 10, записывая частное 04 в регистр AH и  остаток
05 в регистр AL.  Команда OR преобpазует  затем  распакованное  десятичное
число в ASCII-формат.
     Пример на рис.13.2 демонстрирует умножение четырехбайтового множимого
на однобайтовый  множитель.  Так как команда AAM может иметь дело только с
однобайтовыми  числами,  то  в   программе   организован   цикл,   который
обрабатывает  байты  справа  налево.  Окончательный  результат умножения в
данном примере - 0108090105.
     Если множитель больше одного байта, то необходимо обеспечить еще один
цикл,  который  обрабатывает  множитель.  В  этом  случае   проще    будет
преобразовать число из ASCII-формата  в  двоичный  формат  (см.  следующий
раздел "Преобразование ASCII-формата в двоичный формат").

__________________________________________________________________________

TITLE   ASCMUL  (COM) Умножение ASCII-чисел
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     MAIN
; ---------------------------------------------
MULTCND DB      '3783'          ;Элементы данных
MULTPLR DB      '5'
PRODUCT DB      5 DUP(0)
; ---------------------------------------------
MAIN    PROC    NEAR
        MOV     CX,04           ;4 цикла
        LEA     SI,MULTCND+3
        LEA     DI,PRODUCT+4
        AND     MULTPLR,0FH     ;Удалить ASCII-тройку
A20:
        MOV     AL,[SI]         ;Загрузить ASCII-символ
                                ; (можно LODSB)
        AND     AL,OFH          ;Удалить ASCII-тройку
        MUL     MULTPLR         ;Умножить
        AAM                     ;Коррекция для ASCII
        ADD     AL,[DI]         ;Сложить с
        AAA                     ; записанным
        MOV     [DI],AL         ; произведением
        DEC     DI
        MOV     [DI],AH         ;Записать перенос
        DEC     SI
        LOOP    A20             ;Циклиться 4 раза
        RET
MAIN    ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.13.2. Умножение в ASCII-формате.


                         Деление в ASCII-формате
                        -------------------------
     Команда AAD  (ASCII  Adjust  for  Division  -  коррекция  для деления
ASCII-кодов)    выполняет    корректировку    ASCII-кода    делимого    до
непосредственного деления. Однако, прежде необходимо очистить левые тройки
ASCII-кодов для получения распакованного десятичного формата.  Команда AAD
может оперировать с двухбайтовыми делимыми в регистре AX. Предположим, что
регистр AX содержит делимое 3238 в ASCII-формате  и  регистр  CL  содержит
делитель  37 также в ASCII-формате.  Следующие команды выполняют коррекцию
для последующего деления:
                                                            AX:
          AND  CL,0FH    ;Преобразовать CL в распак.дес.
          AND  AX,0F0FH  ;Преобразовать AX в распак.дес.    0208
          AAD            ;Преобразовать в двоичный          001C
          DIV  CL        ;Разделить на 7                    0004

Команда AAD умножает содержимое AH на 10 (шест.0A),  прибавляет  pезультат
20 (шест.14) к регистру AL и очищает регистр AH.  Значение 001C есть шест.
представление  десятичного  числа  28.  Делитель   может    быть    только
однобайтовый от 01 до 09.
     Пример на  рис.13.3  выполняет  деление  четырехбайтового делимого на
однобайтовый делитель.  В программе организован  цикл  обработки  делимого
справа  налево.  Остатки  от деления находятся в регистре AH и команда AAD
корректирует их в регистре AL. Окончательный pезультат: частное 00090204 и
в регистре AH остаток 02.
     Если делитель больше одного байта,  то  необходимо  построить  другой
цикл для обработки делителя, но лучше воспользоваться  следующим  разделом
"Преобразование ASCII-формата в двоичный формат."

__________________________________________________________________________

TITLE   ASCDIV  (COM) Деление ASCII-чисел
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; ---------------------------------------------
DIVDND  DB      '3698'          ;Элементы данных
DIVSOR  DB      '4'
QUOTNT  DB      4 DUP(0)
; ---------------------------------------------
MAIN    PROC    NEAR
        MOV     CX,04           ;4 цикла
        SUB     AH,AH           ;Стереть левый байт делимого
        AND     DIVSOR,0FH      ;Стереть ASCII 3 в делителе
        LEA     SI,DIVDND
        LEA     DI,QUOTNT
A20:
        MOV     AL,[SI]         ;Загрузить ASCII байт
                                ; (можно LODSB)
        AND     AL,0FH          ;Стереть ASCII тройку
        AAD                     ;Коррекция для деления
        DIV     DIVSOR          ;Деление
        MOV     [DI],AL         ;Сохранить частное
        INC     SI
        INC     DI
        LOOP    A20             ;Циклиться 4 раза
        RET
MAIN    ENDP
CODEGS  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.13.3. Деление в ASCII-формате.


     ДВОИЧНО-ДЕСЯТИЧНЫЙ ФОРМАТ (BCD)
     ________________________________________________________________

     В предыдущем примере деления в ASCII-формате  было  получено  частное
00090204. Если сжать это значение, сохраняя только  правые  цифры  каждого
байта, то получим 0924. Такой формат называется двоично-десятичным (BCD  -
Binary Coded Decimal) (или упакованным).  Он  содержит  только  десятичные
цифры от 0 до 9. Длина двоично-десятичного представления в два раза меньше
ASCII-представления.
     Заметим, однако, что десятичное число  0924  имеет  основание  10  и,
будучи преобразованным в основание 16 (т.е. в шест.  представление),  даст
шест.039C.
     Можно выполнять  сложение  и  вычитание  чисел  в  двоично-десятичном
представлении (BCD-формате).  Для этих целей  имеются  две  корректиpующих
команды:

DAA  (Decimal Adjustment for Addition - десятичная коррекция для сложения)
DAS  (Decimal Adjustment for Subtraction - десятичн. коррекция для вычит.)

     Обработка  полей  также  осуществляется  по  одному  байту  за   одно
выполнение. В примере  программы,  приведенном  на  рис.13.4,  выполняется
преобразование   чисел  из  ASCII-формата  в  BCD-формат  и  сложение  их.
Процедура  B10CONV  преобразует  ASCII  в  BCD.  Обработка   чисел   может
выполняться как справа налево,  так и слева направо. Кроме того, обработка
слов проще,  чем обработка байтов,  так как  для  генерации  одного  байта
BCD-кода  требуется  два  байта  ASCII-кода.  Ориентация на обработку слов
требует четного количества байтов в ASCII-поле.
     Процедура  C10ADD  выполняет   сложение    чисел    в    BCD-формате.
Окончательный результат - 127263.

__________________________________________________________________________

TITLE BCDADD (СОМ) Преобр.ASCII в BCD, сложение
CODESG  SEGMENT PARA "Code"
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; -------------------------------------------
ASC1    DB      '057836'
ASC2    DB      '069427'
BCD1    DB      '000'
BCD2    DB      '000'
BCD3    DB      4 DUP(0)
; -------------------------------------------
MAIN    PROC    NEAR
        LEA     SI,ASC1+4          ;Инициализировать для ASC1
        LEA     DI,BCD1+2
        CALL    B10CONV            ;Вызвать преобразование
        LEA     SI,ASC2+4          ;Инициализировать для ASC2
        LEA     DI,BCD2+2
        CALL    B10CONV            ;Вызвать преобразование
        CALL    C10ADD             ;Вызвать сложение
        RET
MAIN    ENDP
;               Преобразование ASCII в BCD:
;               --------------------------
B10CONV PROC
        MOV     CL,04              ;Фактор сдвига
        MOV     OX,03              ;Число слов В20:
        MOV     AX,[SI]            ;Получить ASCII-пapy
                                   (можно использовать LODSW)
        XCHG    AH,AL
        SHL     AL,CL              ;Удалить тройки
        SHL     AX,CL              ; ASCII-кода
        MOV     [DI],AH            ;Записать BCD-цифру
        DEC     SI
        DEC     SI
        DEC     DI
        DEC     DX
        JNZ     В20
        RET
B10CONV ENDP
;               Сложение BCD-чисел:
;               ------------------
C10ADD PROC
       XOR      AН,AН              ;0чистить AН
       LEA      SI,BCD1+2          ;Инициализация
       LEA      DI,BCD2+2          ; BCD
       LEA      BX,BCD3+3          ; адресов
       MOV      CX,03              ;Трехбайтные поля
       CLC
С20:
       MOV      AL,[SI]            ;Получить BCD1 (или LODSB)
       ADC      AL,[DI]            ;Прибавить BCD2
       DAA                         ;Десятичная коррекция
       MOV      [BX],AL            ;3аписать в BCD3
       DEC      SI
       DEC      DI
       DEC      BX
       LOOP     С20                ;Цикл 3 раза
       RET
C10ADD ENDP

CODESG ENDS
       END      BEGIN
__________________________________________________________________________

     Рис.13.4. BCD-преобразование и арифметика.


     ПРЕОБРАЗОВАНИЕ ASCII-ФОРМАТА В ДВОИЧНЫЙ ФОРМАТ
     ________________________________________________________________

     Выполнение арифметических  операций  над  числами  в  ASCII  или  BCD
форматах удобно  лишь  для  коротких  полей.  В  большинстве  случаев  для
арифметических операций используется  преобразование  в  двоичный  формат.
Практически  проще  преобразование  из  ASCII-формата  непосредственно   в
двоичный формат, чем преобразование из ASCII- в  BCD-формат  и,  затем,  в
двоичный формат:
     Метод  преобразования  базируется  на  том,  что  ASCII-формат  имеет
основание 10, а компьютер выполняет  арифметические  операции  только  над
числами с основанием 2. Процедура преобразования заключается в следующем:

          1. Начинают с самого  правого  байта  числа  в  ASCII-формате  и
     обрабатывают справа налево.
          2. Удаляют тройки из левых шест.цифр каждого ASCII-байта.
          3. Умножают ASCII-цифры на 1, 10, 100 (шест.1, A, 64) и  т.д.  и
     складывают результаты.

     Для примера рассмотрим преобразование числа 1234 из  ASCII-формата  в
двоичный формат:

                              Десятичное     Шестнадцатиричное

               4 х 1 =                4              4
               3 х 10 =              30             1E
               2 х 100 =            200             C8
               1 х 1000 =          1000            3E8
               Результат:                         04D2

     Проверьте,  что  шест.04D2  действительно  соответствует  десятичному
1234. На   рис.13.5   в   процедуре   B10ASBI  выполняется  преобразование
ASCII-числа 1234 в двоичный формат.  В примере предполагается,  что  длина
ASCII-числа равна 4 и она записана в поле ASCLEN.  Для инициализации адрес
ASCII-поля ASCVAL-1 заносится в регистр  SI,  а  длина  -  в  регистр  BX.
Команда по метке B20 пересылает ASCII-байт в регистр AL:

               MOV  AL,[SI+BX]

     Здесь используется адрес ASCVAL-1 плюс содержимое  регистра  BX  (4),
т.е. получается адрес ASCVAL+3 (самый правый байт поля ASCVAL).  В  каждом
цикле содержимое регистра BX уменьшается на 1, что приводит к обращению  к
следующему слева байту.  Для данной адресации можно  использовать  регистр
BX, но не CX, и, следовательно, нельзя применять команду  LOOP.  В  каждом
цикле происходит также умножение поля MULT10 на 10, что дает в  результате
множители 1,10,100 и  т.д.  Такой  прием  применен  для  большей  ясности,
однако, для большей производительности множитель можно хранить в  регистре
SI или DI.

__________________________________________________________________________

TITLE   EXCONV (СОМ) Преобр. ASCII и дв. ф-тов
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,SS:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; -------------------------------------------
ASCVAL  DB      '1234'             ;Элементы данных
BINVAL  DB      0
ASCLEN  DB      4
MULT10  DB      1
; -------------------------------------------
MAIN    PROC    NEAR               ;Основная процедура:
        CALL    B10ASBI            ;Вызвать преобразование ASCII
        CALL    C10BIAS            ;Вызвать преобразование двоичное
        RET
MAIN    ENDP
;               -------------------------------
;               Преобразование ASCII в двоичное:
;               -------------------------------
B10ASBI PROC
        MОV     CX,10              ;Фактор умножения
        LEA     SI,ASCVAL-1        ;Адрес ASCVAL
        MOV     BX,ASCLEN          ;Длина ASCVAL
В20:
        MOV     AL,[SI+BX]         ;Выбрать ASCII-символ
        AND     AX,000FH           ;Очистить зону тройки
        MUL     MULT10             ;Умножить на фактор 10
        ADD     BINVAL,AX          ;Прибавить к двоичному
        MOV     AX,MULT10          ;Вычислить следующий
        MUL     CX                 ; фактор умножения
        MOV     MULT10,AX
        DEC     BX                 ;Последн. ASCII-символ?
        JNZ     В20                ; Нет - продолжить
        RET
B10ASBI ENDP
;               -------------------
;               Преобр. дв. в ASCII:
;               -------------------
C10BIAS PROC
        MOV     CX,0010            ;Фактор деления
        LEA     SI,ASCVAL+3        ;Адрес ASCVAL
        MOV     AX,BINVAL          ;Загрузить дв. число
С20:
        CMP     AХ,0010            ;Значение меньше  10?
        JB      С30                ; Да - выйти
        XOR     DX,DX              ;Очистить часть частного
        DIV     CX                 ;Разделить на 10
        OR      DL,30H
        MOV     [SI],DL            ;Записать ASCII-символ
        OEC     SI
        JMP     С20
C30:
        OR      AL,30H             ;3аписать поcл. частное
        MOV     [SI],AL            ; как ASCII-символ
        RET
C10BIAS ENDP

CODESG  ENDS
        END    BEGIN
__________________________________________________________________________

     Рис.13.5. Преобразование ASCII и двоичного форматов.


     ПРЕОБРАЗОВАНИЕ ДВОИЧНОГО ФОРМАТА В ASCII-ФОРМАТ
     ________________________________________________________________

     Для того, чтобы напечатать или отобразить  на  экране  арифметический
pезультат, необходимо преобразовать его в  ASCII-формат.  Данная  операция
включает  в  себя  процесс  обратный   предыдущему.    Вместо    умножения
используется деление  двоичного  числа  на  10 (шест.0A) пока результат не
будет меньше 10.  Остатки,  которые лежат в границах от 0 до  9,  образуют
число  в  ASCII-формате.  В  качестве  примера  рассмотрим  преобразование
шест.4D2 обратно в десятичный формат:

                            Частное       Остаток

               4D2 : A        7B             4
                7B : A         C             3
                 C : A         1             2

Так как последнее частное 1 меньше, чем  шест.A,  то  операция  завершена.
Остатки вместе с последним частным  образуют  результат  в  ASCII-формате,
записываемый справа налево 1234. Все остатки и  последнее  частное  должны
записываться в память с тройками, т.е. 31323334.
     На рис.13.5  процедура  C10BIAS   преобразует   шест.4D2   (результат
вычисления в процедуре B10ASBI) в ASCII-число 1234. Полезно переписать всю
программу (рис.13.5) в компьютер и выполнить трассиpовку ее выполнения  по
шагам.


     СДВИГ И ОКРУГЛЕНИЕ
     ________________________________________________________________

     Рассмотрим процесс округления числа до двух десятичных  знаков  после
запятой.  Если  число  равно  12,345,  то  необходимо  прибавить    5    к
отбрасываемому разряду и сдвинуть число вправо на один десятичный разряд:

               Число:              12,345
               Плюс 5:                 +5
                                   ------
               Округленное число:  12,350 = 12,35

     Если округляемое число равно 12,3455, то необходимо  прибавить  50  и
сдвинуть на два десятичных разряда.  Для 12,34555 необходимо прибавить 500
и сдвинуть на три десятичных разряда:

               12,3455             12,34555
                   +50                 +500
               -------             --------
               12,3505 = 12,35     12,35055 = 12,35

К числу, имеющему шесть знаков после запятой, необходимо прибавить 5000  и
сдвинуть  на  четыре  десятичных  разряда  и   т.д.    Поскольку    данные
представляются в  компьютере  в  двоичном  виде,  то  12345  выглядит  как
шест.3039. Прибавляя 5 к 3039, получим 303E, что соответствует числу 12350
в десятичном представлении. Пока все хорошо. Но вот сдвиг на одну двоичную
цифру дает в результате шест.181F, или 1675 - т.е. сдвиг на одну  двоичную
цифру просто делит число пополам.  Но нам необходим такой  сдвиг,  который
эквивалентен сдвигу вправо на одну десятичную  цифру.  Такой  сдвиг  можно
осуществить делением на 10 (шест.A):

               Шест.303E : Шест.A = 4D3 или дес.1235

Преобразование шест.4D3 в ASCII-формат дает число  1235.  Теперь  oстается
лишь вставить запятую в правильную позицию числа 12,35, и можно выдать  на
экран округленное и сдвинутое значение.
     Таким образом можно округлять и сдвигать любые  двоичные  числа.  Для
трех знаков после запятой необходимо прибавить 5 и разделить  на  10,  для
четырех знаков после запятой: прибавить 50 и pазделить на 100. Возможно вы
заметили модель: фактор округления (5, 50, 500 и т.д.)  всегда  составляет
половину фактора сдвига (10, 100, 1000 и т.д.).
     Конечно, десятичная запятая в двоичном числе только подpазумевается.


  ПРОГРАММА: ПРЕОБРАЗОВАНИЕ ВРЕМЕНИ И РАСЦЕНКИ РАБОТ ДЛЯ РАСЧЕТА ЗАРПЛАТЫ
  _______________________________________________________________________

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

B10INPT   Вводит значения времени работы на ее расценку с клавиатуры.  Эти
          значения могут содержать десятичную запятую.
D10HOUR   Выполняет преобразование значения времени из  ASCII  в  двоичный
          формат.
E10RATE   Выполняет преобразование значения расценки из ASCII  в  двоичный
          формат.
F10MULT   Выполняет умножение,  округление и сдвиг.  Величина зарплаты без
          дробной части или с одним или двумя  знаками  после  запятой  не
          требует округления и сдвига.  Данная процедура  ограничена  тем,
          что позволяет обрабатывать  величину  зарплаты  с  точностью  до
          шести десятичных знаков, что, конечно, больше, чем требуется.
G10WAGE   Вставляет десятичную  запятую,  определяет  правую  позицию  для
          начала записи ASCII символов  и  преобразует  двоичное  значение
          зарплаты в ASCII-формат.
K10DISP   Заменяет лидирующие нули на пробелы и выводит результат на экран
M10ASBI   Преобразует ASCII в двоичный формат (общая процедура для времени
          и расценки)  и определяет число цифр после запятой  в  введенном
          значении.

__________________________________________________________________________

TITLE   SCREMP  (EXE) Ввод времени и расценки,
                      ;вывод величины оплаты
; ----------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
        DW      32 DUP(?)
STACKSG ENDS
; ----------------------------------------------------
DATASG  SEGMENT PARA 'Data'
HRSPAR  LABLE   BYTE            ;Список параметров для
                                ; ввода времени:
MAXHLEN DB      6               ;---------------------
ACTHLEN DB      ?
HRSFLD  DB      6 DUP(?)

RATEPAR LABLE   BYTE            ;Список параметров для
                                ; ввода расценки:
MAXRLEN DB      6               ;---------------------
ACTRLEN DB      ?
RATEFLN DB      6 DUP(?)

MESSG1  DB      'Hours worked? ','$'
MESSG2  DB      'Rate of pay? ','$'
MESSG3  DB      'Wage = '
ASCWAGE DB      10 DUP(30H), 13, 10, '$'
ADJUST  DW      ?
ASCHRS  DB      0
ASCRATE DB      0
BINVAL  DW      00
BINHRS  DW      00
BINRATE DW      00
COL     DB      00
DECIND  DB      00
MULT10  DW      01
NODEC   DW      00
ROW     DB      00
SHIFT   DW      ?
TENWD   DW      10
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
        MOV     AX,0600H
        CALL    Q10SCR          ;Очистить экран
        CALL    Q20CURS         ;Установить курсор
A20LOOP:
        CALL    B10INPT         ;Ввести время и расценку
        CMP     ACTHLEN,00      ;Завершить работу?
        JE      A30
        CALL    D10HOUR         ;Получить двоичное время
        CALL    E10RATE         ;Получить двоичную расценку
        CALL    F10MULT         ;Расчитать оплату
        CALL    G10WAGE         ;Преобразовать в ASCII
        CALL    K10DISP         ;Выдать результат на экран
        JMP     A20LOOP
A30:
        MOV     AX,0600H
        CALL    Q10SCR          ;Очистить экран
        RET                     ;Выйти из программы
BEGIN   ENDP
;               Ввод времени и расценки
; ----------------------------------------------------
B10INPT PROC
        LEA     DX,MESSG1       ;Запрос для ввода времени
        MOV     AH,09
        INT     21H
        LEA     DX,HRSPAR       ;Ввести время
        MOV     AH,0AH
        INT     21H
        CMP     ACTHLEN,00      ;Пустой ввод?
        JNE     B20
        RET                     ; да - вернуться A20LOOP
B20:
        MOV     COL,25          ;Установить столбец
        CALL    Q20CURS
        LEA     DX,MESSG2       ;Запрос для ввода расценки
        MOV     AH,09
        INT     21H
        LEA     DX,RATEPAR      ;Ввести расценку
        MOV     AH,0AH
        INT     21H
        RET
B10INPT ENDP
;               Обработка времени:
;               -----------------
D10HOUR PROC
        MOV     NODEC,00
        MOV     CL,ACTHLEN
        SUB     CH,CH
        LEA     SI,HRSFLD-1     ;Установить правую позицию
        ADD     SI,CX           ; времени
        CALL    M10ASBI         ;Преобразовать в двоичное
        MOV     AX,BINVAL
        MOV     BINHRS,AX
        RET
D10HOUR ENDP
;               Обработка расценки:
;               ------------------
E10RATE PROC
        MOV     CL,ACTRLEN
        SUB     CH,CH
        LEA     SI,RATEFLD-1    ;Установить правую позицию
        ADD     SI.CX           ; расценки
        CALL    M10ASBI         ;Преобразовать в двоичное
        MOV     AX,BINVAL
        MOV     BINRATE,AX
        RET
E10RATE ENDP
;               Умножение, округление и сдвиг:
;               -----------------------------
F10MULT PROC
        MOV     CX,05
        LEA     DI,ASCWAGE      ;Установить формат оплаты
        MOV     AX,3030H        ; в код ASCII (30)
        CLD
        REP STOSW
        MOV     SHIFT,10
        MOV     ADJUST,00
        MOV     CX,NODEC
        CMP     CL,06           ;Если более 6 десятичных
        JA      F40             ; знаков, то ошибка
        DEC     CX
        DEC     CX
        JLE     F30             ;Обойти, если менее 3 знаков
        MOV     NODEC,02
        MOV     AX,01
F20:
        MUL     TENWD           ;Вычислить фактор сдвига
        LOOP    F20
        MOV     SHIFT,AX
        SHR     AX,1            ;Округлить результат
        MOV     ADJUST,AX
F30:
        MOV     AX,BINHRS
        MUL     BINRATE         ;Вычислить оплату
        ADD     AX,ADJUST       ;Округлить оплату
        ADC     DX,00
        CMP     DX,SHIFT        ;Результат слишком велик
        JB      F50             ; для команды DIV?
F40:
        SUB     AX,AX
        JMP     F70
F50:
        CMP     ADJUST,00       ;Сдвиг нее требуется?
        JZ      F80
        DIV     SHIFT           ;Сдвинуть оплату
F70:    SUB     DX,DX           ;Стереть остаток
F80:    RET
F10MULT ENDP
;               Преобразование в ASCII формат:
;               -----------------------------
G10WAGE PROC
        LEA     SI,ASCWAGE+7    ;Установить дес. точку
        MOV     BYTE PTR[SI],'.'
        ADD     SI,NODEC        ;Установить правую позицию
G30:
        CMP     BYTE PTR[SI],'.'
        JNE     G35             ;Обойти, если дес.поз.
        DEC     SI
G35:
        CMP     DX,00           ;Если dx:ax < 10,
        JNZ     G40
        CMP     AX,0010         ; то операция завершена
        JB      G50
G40:
        DIV     TENWD           ;Остаток - ASCII-цифра
        OR      DL,30H
        MOV     [SI],DL         ;Записать ASCII символ
        DEC     SI
        SUB     DX,DX           ;Стереть остаток
        JMP     G30
G50:
        OR      AL,30H          ;Записать последний ASCII
        MOV     [SI],AL         ; символ
        RET
G10WAGE ENDP
;               Вывод величины оплаты:
;               ---------------------
K10DISP PROC
        MOV     COL,50          ;Установить столбец
        CALL    Q20CURS
        MOV     CX,09
        LEA     SI,ASCWAGE
K20:                            ;Стереть лидирующие нули
        CMP     BYTE PTR[SI],30H
        JNE     K30             ; пробелами
        MOV     BYTE PTR[SI],20H
        INC     SI
        LOOP    K20
K30:
        LEA     DX,MESSG3       ;Вывод на экран
        MOV     AH,09
        INT     21H
        CMP     ROW,20          ;Последняя строка экрана?
        JAE     K80
        INC     ROW             ; нет - увеличить строку
        JMP     K90
K80:
        MOV     AX,0601H        ; да --
        CALL    Q10SCR          ; прокрутить и
        MOV     COL,00          ; установить курсор
        CALL    Q20CURS
K90:    RET
K10DISP ENDP
;               Преобразование ASCII-чисел
;               в двоичное представление:
;               --------------------------
M10ASBI PROC
        MOV     MULT10,0001
        MOV     BINVAL,00
        MOV     DECIND,00
        SUB     BX,BX
M20:
        MOV     AL,[SI]         ;ASCII-символ
        CMP     AL,'.'          ;Обойти, если дес.точка
        JNE     M40
        MOV     DECIND,01
        JMP     M90
M40:
        AND     AX,000FH
        MUL     MULT10          ;Умножить на фактор
        ADD     BINVAL,AX       ;Сложить с дв.значением
        MOV     AX,MULT10       ;Вычислить следующий
        MUL     TENVD           ; фактор x 10
        MOV     MULT10,AX
        CMP     DECIND,00       ;Десятичная точка?
        JNZ     M90
        INC     BX              ; да - обойти точку
M90:
        DEC     SI
        LOOP    M20
                                ;Конец цикла
        CMP     DECIND,00       ;Была дес.точка?
        JZ      M100            ; да --
        ADD     NODEC,BX        ; сложить с итогом
M100:   RET
M10ASBI ENDP
;               Прокрутка экрана:
;               ----------------
Q10SCR  PROC    NEAR            ;AX установлен при вызове
        MOV     BH,30           ;Цвет (07 для ч/б)
        SUB     CX,CX
        MOV     DX,184FH
        INT     10H
        RET
Q10SCR  ENDP
;               Установка курсора:
;               -----------------
Q20CURS PROC    NEAR
        MOV     AH,02
        SUB     BH,BH
        MOV     DH,ROW
        MOV     DL,COL
        INT     10H
        RET
Q20CURS ENDP

CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.13.6. Расчет заработной платы.


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

     К о н т р о л ь   о ш и б о к.     Программа,    разработанная    для
пользователей, не являющихся программистами,  должна  не  только  выдавать
предупреждающие  сообщения,  но  также  проверять  корректность   вводимых
значений. Правильными символами при вводе числовых значений являются цифры
от 0 до 9 и символ десятичной запятой. Для любых других символов программа
должна выдать предупреждающее сообщение и вновь повторить запрос на  ввод.
Полезной командой для проверки  корректности  вводимых  символов  является
XLAT (см. гл.14).
     Тщательно проверяйте программы для любых возможных состояний: нулевое
значение, максимально большие и малые значения, отрицательные значения.


                          Отрицательные величины
                         ------------------------
     Некоторые  применения  программ  допускают   наличие    отрицательных
величин.  Знак минус может устанавливаться после числа, например,  12,34-,
или перед числом -12,34. Программа  может  проверять  наличие  минуса  при
преобразовании  в  двоичный  формат.  Можно  оставить    двоичное    число
положительным,  но  установить    соответствующий    индикатор    исходной
отрицательной величины.  После  завершения  арифметических  операций  знак
минус при необходимости может быть вставлен в ASCII поле.
     Если необходимо, чтобы двоичное число было  также  отрицательным,  то
можно преобразовать, как обычно, ASCII-формат в двоичный, а для  изменения
знака двоичного  числа  воспользоваться  командами,  описанными  в   гл.12
"Преобразование знака". Будьте внимательны при использовании команд IMUL и
IDIV для обработки знаковых данных.  Для  округления  отрицательных  чисел
следует не прибавлять, а вычитать фактор 5.


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

     - ASCII-формат требует один байт на каждый символ. Если поле содержит
только цифры от 0 до 9,  то замена старших троек в каждом  байте  на  нули
создает распакованный десятичный формат. Сжатие числа до двух цифр в байте
создает упакованный десятичный формат.

     - После  ASCII-сложения  необходимо  выполнить  коррекцию  с  помощью
команды AAA; после ASCII-вычитания - коррекция с помощью команды AAS.

     - Прежде  чем  выполнить  ASCII-умножение,  необходимо  преобразовать
множимое и множитель в "распакованный десятичный" формат, обнулив в каждом
байте   левые  тройки.  После  умножения  необходимо  выполнить  коррекцию
результата с помощью команды AAM.

     - Прежде чем выполнить ASCII-деление,  необходимо:  1)  преобразовать
делимое  и делитель в "распакованный десятичный" формат,  обнулив в каждом
байте левые тройки и 2) выполнить коррекцию  делимого  с  помощью  команды
AAD.

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


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

     13.1. Предположим, что регистр AX содержит 9 в ASCII коде, а  регистр
BX -7 также в ASCII коде. Объясните и дайте точный результат для следующих
несвязанных операций:

          а)   ADD  AX,33H         б)   ADD  AX,BX
               AAA                      AAA
          в)   SUB  AX,BX          г)   SUB  AX,0DH
               AAS                      AAS

     13.2. Поле UNPAK содержит шест. 01040705 в  распаковочном  десятичном
формате. Напишите цикл, который преобразует это содержимое в ASCII-формат,
т.е. 31343735.

     13.3. Поле ASCA содержит значение 313733 в  ASCII-формате,  а  другое
поле ASCB содержит  35.  Напишите  команды  для  умножения  этих  чисел  в
ASCII-формате и записи произведения в поле ASCPRO.

     13.4. Используя данные из вопроса 13.3,  разделите  ASCA  на  ASCB  и
запишите частное в поле ASCQUO.

     13.5. Выполните следующие вычисления вручную: а) преобразовать  ASCII
46328 в двоичный формат и показать результат в шест.виде; б) преобразовать
полученное шест. значение обратно в ASCII-формат.

     13.6. Напишите  и  выполните  программу,  которая  определяет  размер
памяти компьютера (INT 12H - см.  гл.2), преобразует полученное значение в
ASCII-формат и выводит результат на экран в следующем виде:

               Размер памяти nnn байтов.



ГЛАВА 14                                                  Обработка таблиц
__________________________________________________________________________

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


     ВВЕДЕНИЕ
     ________________________________________________________________

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


     ОПРЕДЕЛЕНИЕ ТАБЛИЦ
     ________________________________________________________________

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

               STACK     DW   64 DUP(?)

     Следующие  две  таблицы  инициализированы  символьными  и   числовыми
значениями:

               MONTAB    DB   'JAN','FEB','MAR', ... ,'DEC'
               COSTAB    DB   205,208,209,212,215,224,...

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

          STOKTBL DB 12,'Computers',14,'Paper....',17,'Diskettes'

Для ясности можно закодировать элементы таблицы вертикально:

               STOKTBL   DB   12,  'Computers'
                         DB   14,  'Paper....'
                         DB   17,  'Diskettes'

     Рассмотрим  теперь  различные  способы   использования    таблиц    в
программах.


     ПРЯМОЙ ТАБЛИЧНЫЙ ДОСТУП
     ________________________________________________________________

     Предположим, что пользователь ввел номер  месяца  -  03  и  программа
должна преобразовать этот номер в алфавитное значение March. Программа для
выполнения такого преобразования включает определение  таблицы  алфавитных
названий месяцев, имеющих одинаковую длину. Так как самое длинное название
- September, то таблица имеет следующий вид:

               MONTBL    DB   'January..'
                         DB   'February.'
                         DB   'March....'

Каждый элемент таблицы имеет длину 9  байт.  Адрес  элемента  'January'  -
MONTBL+0, 'February' - MONTBL+9,  'March'  -  MONTBL+18.  Для  локализации
месяца 03, программа должна выполнить следующее:

          1. Преобразовать введенный номер месяца из ASCII 33  в  двоичное
     03.
          2. Вычесть единицу из номера месяца: 03 - 1 = 02
          3. Умножить результат на длину элемента (9): 02 х 9 = 18
          4. Прибавить произведение (18) к  адресу  MONTBL;  в  результате
     получится адрес требуемого названия месяца: MONTBL+18.

__________________________________________________________________________

        page    60,132
TITLE   DIRECT  (COM) Прямой табличный доступ
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,ES:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; ------------------------------------------------
THREE   DB      3
MONIN   DB      '11'
ALFMON  DB      '???','$'
MONTAB  DB      'JAN','FEB','MAR','APR','MAY','JUN'
        DB      'JUL','AUG','SEP','OKT','NOV','DEC'
; ------------------------------------------------
MAIN    PROC    NEAR            ;Основная процедура
        CALL    C10CONV         ;Получить двоичное значение
        CALL    D10LOC          ;Выделить месяц из таблицы
        CALL    F10DISP         ;Выдать месяц на экран
        RET
MAIN    ENDP
;               Перевод ASCII в двоичное представление:
;               --------------------------------------
C10CONV PROC
        MOV     AH,MONIN        ;Загрузить номер месяца
        MOV     AL,MONIN+1
        XOR     AX,3030H        ;Удалить ASCII тройки
        CMP     AH,00           ;Месяц 01-09?
        JZ      C20             ; да - обойти
        SUB     AH,AH           ; нет - очистить AH,
        ADD     AL,10           ; и перевести в двоичное
C20     RET
C10CONV ENDP
;               Выделение месяца из таблицы:
;               ---------------------------
D10LOC  PROC
        LEA     SI,MONTAB
        DEC     AL              ;Коррекция для таблицы
        MUL     THREE           ;Умножить AL на 3
        ADD     SI,AX
        MOV     CX,03           ;Трехсимвольная пересылка
        CLD
        LEA     DI,ALFMON
        REP MOVSB               ;Переслать 3 символа
        RET
D10LOC  ENDP
;               Вывод на экран симв.месяца:
;               --------------------------
F10DISP PROC
        LEA     DX,ALFMON
        MOV     AH,09
        INT     21H
        RET
F10DISP ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.14.1. Прямая табличная адресация.


     На рис.14.1  приведен  пример  прямого  доступа  к  таблице  названий
месяцев.  Для краткости в программе используются  вместо  девятисимвольных
названий - трехсимвольные.  Введенный номер месяца определен в поле MONIN.
Предположим, что некоторая подпрограмма формирует запрос  на  ввод  номера
месяца в ASCII-формате в поле MONIN.
     Описанная техника  работы  с  таблицей  называется  прямым  табличным
доступом.  Поскольку  данный  алгоритм  непосредственно  вычисляет   адpес
необходимого элемента в таблице, то в  программе  не  требуется  выполнять
операции поиска.
     Хотя прямая табличная адресация очень эффективна, она возможна только
при  последовательной  организации.  То  есть  можно  использовать   такие
таблицы, если элементы располагаются в регулярной  последовательности:  1,
2, 3,... или 106, 107, 108,... или даже  5,  10,  15.  Однако,  не  всегда
таблицы построены  таким  образом.  В  следующем  разделе  рассматриваются
таблицы, имеющие нерегулярную организацию.


     ТАБЛИЧНЫЙ ПОИСК
     ________________________________________________________________

     Некоторые  таблицы  состоят  из    чисел,    не    имеющих    видимой
закономерности.  Характерный  пример  -  таблица  инвентарных  номеров   с
последовательными номерами, например, 134, 138, 141, 239 и 245. Другой тип
таблиц состоит из распределенных по ранжиру величин, таких как  подоходный
налог.  В следующих разделах рассмотрим  эти  типы  таблиц  и  организацию
табличного поиска.


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

               STOKNOS DB '101','107','109',...
               STOKDCR DB 'Excavators','Processors','Assemblers',...

или в одной таблице, например:

               STOKTAB   DB   '101','Excavators'
                         DB   '107','Processors'
                         DB   '109','Assemblers'
                         ...

     Программа на рис.14.2  определяет  инвентарную  таблицу  и  выполняет
табличный поиск.  Таблица содержит шесть пар номеров и наименований.  Цикл
поиска начинается со  сравнения  введенного  инвентарного  номера  в  поле
STOKNIN с первым номером в таблице.  Если номера  различные,  то  адрес  в
таблице увеличивается для сравнения со следующим инвентарным номером. Если
номера равны, то  программа  (A30)  выделяет  наименование  из  таблицы  и
записывает его в поле DESCRN.
     Поиск выполняет максимум шесть сравнений и  если  требуемый  номер  в
таблице отсутствует, то происходит переход на программу обработки  ошибки,
которая выводит на экран соответствующее сообщение.
     Обратите внимание, что в начале программы  имеется  команда,  которая
пересылает содержимое поля STOKNIN в регистр AX.  Хотя STOKNIN определенно
как 3233, команда MOV загрузит  в  регистр  AX  это  значение  в  обратной
последовательности байтов 3332. Так  как  элементы  таблицы  имеют  прямую
последовательность байтов, то после  команды  MOV  имеется  команда  XCHG,
которая  меняет  местами  байты  в  регистре  AX,  возвращая  им    прямую
последовательность,  т.е.  3233.  Команда  CMP,    предполагая    обратную
последовательность, сравнивает сначала правые  байты,  а  затем  -  левые.
Следовательно, проверка на pавенство  будет  корректной,  но  проверки  на
больше или меньше дадут неправильные результаты.  Для сравнения на  больше
или меньше  следует  опустить  команду  XCHG,  переслать  элемент  таблицы
командой MOV, скажем, в регистр BX и затем сравнить  содержимое  регистров
AX и BX следующим образом:

                    MOV  AX,STOKNIN
                    LEA  SI,STOKTAB
               C20:
                    MOV  BX,[SI]
                    CMP  AX,BX
                    JA или JB ...

     В программе такого типа другая  таблица  может  определять  стоимость
единицы товара.  Программа может локализовать элемент  таблицы,  вычислить
продажную стоимость  (количество  товара  умножить  на  стоимость  единицы
товара) и выдать на экран наименование и продажную стоимость товара.
     В примере   на   рис.14.2  таблица  содержит  двухбайтовые  номера  и
десятибайтовые наименования.  Детальное программирование будет  oтличаться
для   различного   числа   и  длины  элементов.  Например,  для  сравнения
трехбайтовых полей можно использовать команду REPE CMPSB, хотя эта команда
также включает использование pегистра CX.

__________________________________________________________________________

        page    60,132
TITLE   TABSRCH (COM) Табличный поиск
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,ES:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; -----------------------------------------------
STOKNIN DW      '23'
STOKTAB DB      '05','Excavators'
        DB      '08','Lifters   '
        DB      '09','Presses   '
        DB      '12','Valves    '
        DB      '23','Processors'
        DB      '27','Pumps     '
DESCRN  10      DUP(?)
; -----------------------------------------------
MAIN    PROC    NEAR
        MOV     AX,STOKNIN      ;Загрузить номер элемента
        XCHG    AL,AH
        MOV     CX,06           ;Число элементов в таблице
        LEA     SI,STOKTAB      ;Начальный адрес таблицы
A20:
        CMP     AX,[SI]         ;Сравнить элементы
        JE      A30             ;Если равны - выйти,
        ADD     SI,12           ; нет - следующий элемент
        LOOP    A20
        CALL    R10ERR          ;Элемент в таблице не найден
        RET
A30:
        MOV     CX,05           ;Длина описания элемента
        LEA     DI,DESCRN       ;Адрес описания элемента
        INC     SI
        INC     SI              ;Выделить описание
        REP MOVSW               ; из таблицы
        RET
MAIN    ENDP
;
R10ERR  PROC
;               <Вывод сообщения об ошибке>
        RET
R10ERR  ENDP

CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.14.2. Табличный поиск


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

               Размер дохода    Процент налога    Поправочный к-нт

                     0-1000.00           10               0,00
               1000,01-2500,00           15             050,00
               2500,01-4250,00           18             125,00
               4250,01-6000,00           20             260,00
               6000,01 и более           23             390,00

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

               TAXTBL    DD   100000,250000,425000,600000,999999

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

          - если меньше или равно, то использовать соответствующий процент
     и поправку;
          - если больше, то перейти к следующему элементу таблицы.

Величина налога рассчитывается по формуле:

               Доход  х  Процент налога : 100 - поправочный к-нт


             Табличный поиск с использованием сравнения строк
            --------------------------------------------------
     Если элемент таблицы превышает длину в два  байта,  то  для  операции
сравнения можно использовать команду REPE CMPS.  Предположим, что  таблица
инвентарных номеров (рис.14.2) переделана для трехбайтовых  номеров.  Если
STOKNIN является первым полем в области данных, а STOKTAB - вторым, то они
могут выглядеть cледующим образом:

          Данные: |123|035Excavators|038Lifters   |049Presses   | ...
                   |   |  |          |  |          |  |
          Адрес:  00  03 06         16 19         29 32

Программа  на  рис.14.3  определяет  таблицу  STOKTAB,  включая  последний
элемент '999' для индикации конца таблицы  при  поиске.  Программа  поиска
сравнивает содержимое каждого элемента таблицы с содержимым поля STOKNIN:

          Элемент таблицы     STOKNIN   Результат сравнения

               035            123       Меньше: проверить след.эл-т
               038            123       Меньше: проверить след.эл-т
               049            123       Меньше: проверить след.эл-т
               102            123       Меньше: проверить след.эл-т
               123            123       Равно: элемент найден

     Заметим, что команда CMPSB на рис.14.3  сравнивает  байт  за  байтом,
пока байты не будут равны и автоматически увеличивает регистpы SI и DI.
     Регистр CX инициализируется значением 03, а  начальные  относительные
адреса в регистрах SI и DI устанавливаются равными 03 и 00 соответственно.
Сравнение с первым  элементом  таблицы  (035:123)  завершается  на  первом
байте, после этого регистр SI содержит 04, DI: 01, CX: 02. Для  следующего
сравнения регистр SI должен иметь значение 16,  а  DI:  00.  Корректировка
регистра DI сводится к простой  перезагрузке  адреса  STOKNIN.  Увеличение
адреса следующего элемента таблицы, который должен  быть  в  регистре  SI,
зависит от того, на каком байте (первом, втором или  третьем)  закончилось
предыдущее сравнение.  Регистр CX содержит число байт,  не  участвующих  в
сравнении, в данном случае  -  02.  Прибавив  к  содержимому  регистра  SI
значение в регистре CX и длину наименования, получим  относительный  адрес
следующего элемента:

               Адрес в SI после CMPSB             04
               Прибавить CX                       02
               Прибавить длину наименования       10
                                                  --
               Относительный адрес след.элемента  16

     Так как регистр CX всегда  содержит  число  байт,  не  участвующих  в
сравнении (если такие  есть),  то  расчет  справедлив  для  всех  случаев:
прекращение сравнения после 1, 2 или 3 байта. Если сравниваются одинаковые
элементы, то регистр CX получит значение 00, а адрес в pегистре SI  укажет
на требуемое наименование.

__________________________________________________________________________

        page    60,132
TITLE   TABSRCH (COM) Табличный поиск,  использующий CMPSB
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,ES:CODESG
        ORG     100H
BEGIN:  JMP     SHORT MAIN
; ----------------------------------------------------
STOKNIN DW      '123'
STOKTAB DB      '035','Excavators'       ;Начало таблицы
        DB      '038','Lifters   '
        DB      '049','Presses   '
        DB      '102','Valves    '
        DB      '123','Processors'
        DB      '127','Pumps     '
        DB      '999', 10 DUP(' ')       ;Конец таблицы
DESCRN  10      DUP(?)
; ----------------------------------------------------
MAIN    PROC    NEAR
        CLD
        LEA     SI,STOKTAB     ;Начальный адрес таблицы
A20:
        MOV     CX,03          ;Сравнивать по 3 байта
        LEA     DI,STOKNIN     ;Адрес искомого элемента
        REPE CMPSB             ;Сравнение
        JE      A30            ;Если равно - выйти,
        JA      A40            ;если больше - нет в таблице
        ADD     SI,CX          ;Прибавить CX к адресу
        JMP     A20            ;Следующий элемент таблицы
A30:
        MOV     CX,05          ;Пересылать 5 слов
        LEA     DI,DESCRN      ;Адрес описания
        REP MOVSV              ;Переслать из таблицы
        RET
A40:
        CALL    R10ERR         ;элемент в таблице не найден
        RET
MAIN    ENDP

R10ERR  PROC
;               <Вывод на экран сообщения об ошибке>
        RET
R10ERR  ENDP

CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.14.3. Табличный поиск с использованием команды CMPSB


                  Таблицы с элементами переменной длины
                 ---------------------------------------
     Существуют таблицы, в которых элементы имеют переменную длину. Каждый
элемент  такой  таблицы    может    завершаться    специальным    символом
ограничителем,  например,  шест.00;  конец  таблицы    можно    обозначить
огpаничителем шест.FF.  В  этом  случае  необходимо  гарантировать,  чтобы
внутри элементов таблицы не встречались указанные  ограничители.  Помните,
что двоичные  числа  могут  выражаться  любыми битовыми комбинациями.  Для
поиска можно использовать команду SCAS.


     ТРАНСЛИРУЮЩАЯ КОМАНДА XLAT
     ________________________________________________________________

     Команда  XLAT  транслирует  содержимое  одного   байта    в    другое
предопределенное  значение.  С  помощью  команды  XLAT  можно    проверить
корректность содержимого  элементов  данных.  При  передаче  данных  между
персональным компьютером и ЕС ЭВМ  (IBM)  с  помощью  команды  XLAT  можно
выполнить перекодировку данных между форматами ASCII и EBCDIC.
     В следующем примере происходит преобразование цифр от 0 до 9 из  кода
ASCII в код EBCDIC.  Так как  представление  цифр  в  ASCII  выглядит  как
шест.30-39, а в EBCDIC - шест.F0-F9, то замену  можно  выполнить  командой
OR.  Однако, дополнительно преобразуем все остальные коды ASCII  в  пробел
(шест.40) в коде EBCDIC.  Для команды XLAT необходимо  определить  таблицу
перекодировки, которая учитывает все  256  возможных  символов,  с  кодами
EBCDIC в ASCII позициях:

               XLTBL  DB   47 DUP(40H)    ;Пробелы в коде EBCDIC
                      DB   0F0H,0F1H,0F2H,0F3H,...,0F9H  ;0-9 (EBCDIC)
                      DB   199 DUP(40H)   ;Пробелы в коде EBCDIC

Команда XLAT предполагает адрес таблицы в  регистре  BX,  а  транслируемый
байт (например, поля ASCNO) в регистре  AL.  Следующие  команды  выполняют
подготовку и трансляцию байта:

               LEA  BX,XLTBL
               MOV  AL,ASCNO
               XLAT

Команда XLAT использует значение в регистре AL в  качестве  относительного
aдреса в таблице, т.е. складывает адрес  в  BX  и  смещение  в  AL.  Если,
например, ASCNO содержит 00, то адрес байта в  таблице  будет  XLTBL+00  и
команда XLAT заменит 00 на шест.40 из таблицы.  Если поле  ASCNO  cодержит
шест.32, то адрес соответствующего байта в таблице  будет  XLTBL+50.  Этот
байт содержит шест.F2 (2 в коде EBCDIC), который команда XLAT загружает  в
регистр AL.
     В программе на рис.14.4  добавлено  преобразование  десятичной  точки
(2E)  и  знака  минус  (2D)  из  кода  ASCII  в  код  EBCDIC  (4B  и    60
соответственно). В программе организован цикл для обработки шестибайтового
поля.  Поле ASCNO в начале выполнения программы содержит значение  31.5  с
последующим пробелом, или шест.2D33312E3520. В конце выполнения  программы
в поле EBCNO должно быть шест.60F3F14BF540.

__________________________________________________________________________

        page    60,132
TITLE   XLATE   (COM) Перевод кода ASCII в код EBCDIC
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,ES:CODESG
        ORG     100H
BEGIN:  JMP     MAIN
; ----------------------------------------------------
ASCNO   DB      '-31.5'
EBCNO   DB      6 DUP(' ')
XLTAB   DB      45 DUP(40H)
        DB      60H, 2DH
        DB      5CH
        DB      0F0H,0F1H,0F2H,0F3H,0F4H
        DB      0F5H,0F6H,0F7H,0F8H,0F9H
        DB      199 DUP(40H)
; ----------------------------------------------------
MAIN    PROC    NEAR           ;Основная процедура
        LEA     SI,ASCNO       ;Адрес символов ASCNO
        LEA     DI,EBCNO       ;Адрес поля EBCNO
        MOV     CX,06          ;Длина
        LEA     BX,XLTAB       ;Адрес таблицы
A20:
        MOV     AL,[SI]        ;Получить ASCII символ
        XLAT                   ;Перекодировка
        MOV     [DI],AL        ;Записать в поле EBCNO
        INC     DI
        INC     SI
        LOOP    A20            ;Повторить 6 раз
        RET
MAIN    ENDP
CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.14.4. Преобразование ASCII в EBCDIC.


     ПРОГРАММА: ОТОБРАЖЕНИЕ ШЕСТ. И ASCII-КОДОВ
     ________________________________________________________________

__________________________________________________________________________

        page    60,132
TITLE   ASCHEX  (COM) Преобразование ASCII в шест.
CODESG  SEGMENT PARA 'Code'
        ASSUME  CS:CODESG,DS:CODESG,ES:CODESG
        ORG     100H
BEGIN:  JMP     MAIN
; -----------------------------------------------
DISPROW DB      16 DUP('     '), 13
HEXSTR  DB      00
XLATAB  DB      30H,31H,32H,33H,34H,35H,36H,37H,38H,39H
        DB      41H,42H,43H,44H,45H,46H
; -----------------------------------------------
MAIN    PROC    NEAR            ;Основная процедура
        CALL    Q10CLR          ;Очистить экран
        LEA     SI,DISPROW
A20LOOP:
        CALL    C10HEX          ;Перекодировать
        CALL    D10DISP         ; и вывести на экран
        CMP     HEXCTR,0FFH     ;Последнее значение (FF)?
        JE      A50             ; да - завершить
        INC     HEXCTR          ; нет - перейти к следующему
        JMP     A20LOOP
A50:    RET
MAIN    ENDP

C10HEX  PROC    NEAR            ;Перекодировка в шест.
        MOV     AH,00
        MOV     AL,HEXCTR       ;Получить шест.пару
        SHR     AX,CL           ;Сдвиг правой шест.цифры
        LEA     BX,XLATAB       ;Установить адрес таблицы
        MOV     CL,04           ;Установить величину сдвига
        XLAT                    ;Перекодировка в шест.
        MOV     [SI],AL         ;Записать левый символ

        MOV     AL,HEXCTR
        SHL     AX,CL           ;Сдвиг левой цифры
        XLAT
        MOV     [SI]+1,AL       ;Перекодировка в шест.
        RET                     ;Записать правый символ
C10HEX  ENDP

D10DISP PROC    NEAR            ;Вывод на экран
        MOV     AL,HEXCTR
        MOV     [SI]+3,AL
        CMP     AL,1AH          ;Символ EOF?
        JE      D20             ; да - обойти
        CMP     AL,07H          ;Меньше/равно 08?
        JB      D30             ; да - OK
        CMP     AL,10H          ;Больше/равно 0F?
        JAE     D30             ; да - OK
D20:
        MOV     BYTE PTR [SI]+3,20H
D30:
        ADD     SI,05           ;Следующий элемент в строке
        LEA     DI,DISPROW+80
        CMP     DI,SI
        JNE     D40
        MOV     AH,40H          ;Функция вывода на экран
        MOV     BX,01           ;Номер устройства
        MOV     CX,81           ;Вся строка
        LEA     DX,DISPROW
        INT     21H
        LEA     SI,DISPROW      ;Начальный адрес строки
D40:    RET
D10DISP ENDP

Q10CLR  PROC    NEAR            ;Очистка экрана
        MOV     AX,0600H
        MOV     BH,03           ;Цвет (07 для ч/б)
        MOV     CX,0000
        MOV     DX,184FH
        INT     10H
        RET
Q10CLR  ENDP

CODESG  ENDS
        END     BEGIN
__________________________________________________________________________

     Рис.14.5. Отображение шест. и ASCII-кодов


     Программа, приведенная на рис.14.5, отображает на  экране  почти  все
ASCII-символы, а также  их  шест.  значения.  Например,  ASCII-символ  для
шест.53  - это буква S,  эти данные программа выводит в виде 53 S.  Полное
изображение на экране выглядит в виде матрицы 16х16:

               00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
               .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
               .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
               .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
               F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF

     Как было показано еще на рис.8.1,  отображение ASCII-символов, oсобых
проблем не вызывает. Что же касается отображения шест. значений в символах
ASCII, то этот процесс более сложный. Например, для вывода на экран в коде
ASCII  шест.00,  01  и т.д.  необходимо преобразовать шест.00 в шест.3030,
шест.01 в шест.3031 и т.д.
     В программе начальное значение поля HEXCTR  равно  00.  Это  значение
последовательно увеличивается  на  1.  Процедура  C10HEX  расщепляет  байт
HEXCTR на две щест.  цифры. Предположим, что байт HEXCTR содержит шест.4F.
Процедура сначала выделяет шест.  цифру 4 и использует  это  значение  для
перекодировки   по   таблице  XLATAB.  В  регистре  AL  устанавливается  в
результате значение шест.34. Затем процедура выделяет вторую шест. цифру F
и перекодирует ее в шест.46.  В результате oбработки получается шест.3446,
что отображается на экране как 4F.
     Так как функция DOS  для  вывода  на  экран  (шест.40)  рассматривает
шест.1A как конец файла, то в программе это значение заменяется на пробел.
Программа, использующая для вывода на экран функцию DOS (шест.09),  должна
заменять символ ограничитель '$' на пробел.
     Существует много  различных  способов  преобразования  шест.  цифр  в
ASCII-символы. Можно поэкспериментировать с операциями сдвига и сравнения.


     ПРОГРАММА: СОРТИРОВКА ЭЛЕМЕНТОВ ТАБЛИЦЫ
     ________________________________________________________________

     Часто  возникает  необходимость  сортировки  элементов   таблицы    в
восходящем  или  нисходящем  порядке.   Например,    пользователю    может
потребоваться список наименований товара в алфавитном порядке  или  список
общих цен в нисходящей последовательности.  Обычно,  табличные  данные  не
определяются как в предыдущей программе, а загружаются с клавиатуры или  с
диска.  Данный раздел посвящен сортировке элементов таблицы, что  касается
различных применений, включающих сортировку записей на  дисках,  то  здесь
возможны более сложные программы.
     Существует несколько алгоритмов сортировки таблиц  от  неэффективных,
но  понятных,  до  эффективных  и  непонятных.    Программа    сортировки,
предлагаемая в данном разделе, весьма эффективна и может  применяться  для
большинства табличных сортировок.  Конечно, если  не  проверить  различные
алгоритмы  сортировок,  то  даже  самая  неэффективная  программа    может
показаться работающей со скоростью света.  Но цель данной книги - показать
технику  ассемблера,  а  не  сортировки.  Основной  подход  заключается  в
сравнении соседних элементов таблицы.  Если первый элемент больше второго,
то  элементы  меняются  местами.  Таким  образом  выполняется    сравнение
элементов 1 со 2, 2 с 3 и т.д. до конца таблицы с перестановкой  элементов
там, где это необходимо. Если в проходе были сделаны перестановки, то весь
процесс повторяется с начала таблицы т.е. сравниваются снова элементы 1-2,
2-3 и т.д. Если в проходе не было перестановок, то таблица отсортирована и
можно прекратить процесс.
     Ниже  приведен  алгоритм,  в  котором  переменная    SWAP    является
индикатором: была перестановка элементов (YES) или нет (NO):

          G10:   Определить адрес последнего элемента
          G20:   Установить SWAP=NO
                 Определить адрес первого элемента
          G30:   Элемент > следующего элемента?
                      Да:  Представить элементы
                          Установить SWAP=YES
                 Перейти к следующему элементу
                 Конец таблицы?
                      Нет: Перейти на G30
                      Да:  SWAP=YES?
                           Да:  Перейти на G20 (повторить сорт.)
                           Нет: Конец сортировки

     Программа, показанная на рис.14.6, обеспечивает ввод с клавиатуры  до
30 имен, сортировку введенных имен в алфавитном порядке и вывод  на  экран
отсортированного списка имен.

__________________________________________________________________________

        page    60,132
TITLE   NMSORT  (EXE) Ввод и сортировка имен
; -----------------------------------------------
STACK   SGMENT  PARA STACK 'Stack'
        DW      32 DUP(?)
STACK   ENDS
; -----------------------------------------------
DATASG  SEGMENT PARA 'Data'
NAMEPAR LABEL   BYTE            ;Имя списка параметров:
MAXNLEN DB      21              ; макс. длина
NAMELEN DB      ?               ; число введенных символов
NAMEFLD DB      21 DUP(' ')     ; имя

CRLF    DB      13, 10, '$'
ENDADDR DW      ?
MESSG1  DB      'Name?', '$'
NAMECTR DB      00
NAMETAB DB      30 DUP(20 DUP(' ')) ;Таблица имен
NAMESAV DB      20 DUP(?), 13, 10, '$'
SWAPPED DB      00


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

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



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


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