Первый пример - исходный текст программы на Фиг. 7.23.  Эта  программа распечатывает короткое действительное представление  степеней 10 от 103 до 1039.  Как мы уже видели в разделе,  посвященном представлению данных, Макроассемблер фирмы IBM не имеет  возможностей непосредственно генерировать действительные числа.  Наличие такой таблицы чисел облегчит вам представление в виде  констант степеней 10.  По этой таблице вы сможете определить  шестнадцатеричное представление числа, которое нужно использовать в  программе.

Программа вычисляет только каждую третью степень 10, и  использует короткий действительный формат.  Если вы работаете с  много большими числами, или вам нужна большая точность, тогда нужно  выполнить этот пример, используя длинный действительный формат  чисел, а также построить каждую степень 10.  Выполните это в  качестве упражнения.

Первоочередная цель этого примера - введение в программирование  и работу на сопроцессоре 8087.  Это отдельная самостоятельная  программа, предназначенная для выполнения в виде файла типа .EXE.  Прежде чем разобраться в самой программе, заметим, что в нее входит  сегмент STACK, необходимый в файле типа .EXE.  Сначала в сегменте  CODE записаны поля данных, а выполнение программы начинается с  метки CALCULATE_POWER.  Заглянув вперед, на оператор END, вы  увидите, что первой выполняемой командой будет команда, помеченная  CALCULATE_POWER, так как она указана в операторе END.

Первая часть программы выполняет инициализацию.  Перед  загрузкой указателя на сегмент CODE в регистр DS программа помещает  в стек адрес возврата из файла типа .EXE.  Затем с помощью команды  FINIT инициализируется сопроцессор 8087, что аналогично аппаратному  сбросу. Тем самым сопроцессор 8087 оказывается настроенным на  обработку особых ситуаций по умолчанию, что наилучшим образом  подходит для примеров этой книги.  Команда FINIT также сбрасывает  регистровый стек сопроцессора 8087, освобождая все его восемь  позиций. Программа должна использовать команду FINIT только в  момент запуска.  Команда FINIT никогда не должна быть использована  внутри подпрограммы для сопроцессора 8087.

Следующие команды загружают число 1000 в регистр ST1 и число 1  в регистр ST0.  Все следующие команды сопроцессора 8087 используют  эти два регистра стека.  В регистре ST0 находится текущая степень  десяти, а в регистре ST1 находится значение 103.  Мы будем  использовать число в регистре ST1 для увеличения числа в регистре  ST0 после каждой итерации программы.  Целая переменная POWER  содержит текущую степень 10, находящуюся в регистре ST0.

После метки POWER_LOOP элемент ST0 умножается на элемент ST1,  (в котором содержится число 1000), чтобы увеличить содержимое  регистра ST0 в 103 раз.  Команда FST записывает результат в память.  Оставшаяся часть программы после метки POWER_LOOP печатает  результаты вычислений.  В подпрограмме TRANSLATE шестнадцатеричный  байт преобразуется в двухбайтовую строку в коде ASCII так, что  программа может его распечатать.  Текущее значение POWER (степень  десяти), а также шестнадцатеричная строка, записанная процессором  8087, преобразуются в код ASCII.  Затем функция DOS печатает строку  на дисплее.  Цикл POWER_LOOP продолжается до тех пор, пока  последнее напечатанное значение не станет больше 1038.  Это  значение выбрано потому, что 1038 - это максимальное число, которое  может быть представлено в коротком действительном формате.  Если бы использовался длинный действительный формат чисел, это значение  было бы равно 10308.  Заключительная часть Фиг. 7.23 показывает,  как выглядит результат работы этой программы на дисплее.

          Microsoft (R) Macro Assembler Version 5.00              1/1/80 04:04:33

          Фиг. 7.23 Степени 10                             Page  1-1

 

                                         PAGE   ,132

                                         TITLE  Фиг. 7.23 Степени 10

 

           0000                   STACK  SEGMENT STACK

           0000  0040[                  DW      64 DUP (?)

                  ????

                              ]

 

           0080                   STACK  ENDS

 

           0000                   CODE   SEGMENT

                                         ASSUME CS:CODE

 

           0000  ????????              POWER_OF_TEN    DD      ?                 ; Область данных для 10**x,

                                                                 ;      короткое плавающее

           0004  0002[           OUTPUT_POWER    DB      2 DUP (' ')     ; Текстовый буфер для значения

                  20

                              ]

 

           0006  48 20 20 20 20                     DB      'H    '        ;  степени

           000B  0008[           OUTPUT_STRING   DB      8 DUP ('         ')     ; Текстовый буфер для результата -

                  20 20 20 20 20

                  20 20 20 20

                              ]

           0053  48 0D 0A 24                        DB     'H',13,10,'$'   ; Конец строки

           0057  00                     POWER      DB      0               ; Текущая степень 10

           0058  03E8             THOUSAND         DW      1000      ; Константа

           005A  03BF             CONTROL_87      DW     03BFH

 

           005C                   CALCULATE_POWER PROC    FAR

           005C  1E                          PUSH    DS        ; Занесение в стек адреса возврата

           005D  B8 0000                      MOV     AX, 0

           0060  50                           PUSH   AX

           0061  0E                          PUSH    CS

           0062  1F                          POP     DS

                                         ASSUME DS:CODE         ; Адресация области данных

           0063  9B DB E3                     FINIT             ; Инициализация сопроцессора 8087

 

                  Фиг. 7.23 (а) (начало)

           0066  9B DF 06 0058 R             FILD    THOUSAND        ; Загрузка 10**3 в стек сопроцессора 8087

           006B  9B D9 E8                     FLD1              ; Загрузка начального значения в стек 8087

           006E                   POWER_LOOP:

           006E  9B DC 8E 0000               FMUL    ST(1)          ; Умножение ST(0) на ST(1)

           0073  9B D9 16 0000 R             FST     POWER_OF_TEN    ; Сохранение в памяти результата

           0078  80 06 0057 R 03             ADD     POWER, 3        ; Увеличение показателя степени

           007D  A0 0057 R                    MOV     AL, POWER       ; Выборка показателя степени

           0080  8D 1E 0004 R                LEA     BX, OUTPUT_POWER

           0084  E8 00AC R                    CALL    TRANSLATE

           0087  B9 0004                      MOV     CX, 4

           008A  8D 1E 000B R                LEA     BX, OUTPUT_STRING

           008E  8D 36 0003 R                LEA     SI, POWER_OF_TEN+3

           0092  FD                           STD              ; Установка пересылки с уменьшением адреса

           0093                   VALUE_OUTPUT:

 

           0093  AC                          LODSB             ; Выборка байта результата

           0094  E8 00AC R                    CALL    TRANSLATE       ; Занесение символа в выводимую строку

           0097  E2 FA                  LOOP   VALUE_OUTPUT    ; Цикл по всем байтам результата

 

           0099  8D 16 0004 R                LEA     DX, OUTPUT_POWER

           009D  B4 09                  MOV     AH, 9H

           009F  CD 21                  INT     21H

           00A1  80 3E 0057 R 26             CMP     POWER, 38

           00A6  72 C6                  JB      POWER_LOOP

           00A8  9B DE D9                     FCOMPP            ; Удаление из стека двух чисел

           00AB  CB                           RET

           00AC                   CALCULATE_POWER ENDP

 

           00AC                   TRANSLATE         PROC   NEAR

           00AC  50                          PUSH    AX        ; Сохранение исходного значения

           00AD  51                          PUSH    CX

           00AE  B1 04                  MOV    CL, 4           ; Сдвиг старшей цифры выводимого числа

           00B0  D2 E8                  SHR     AL, CL         ;      на место младшей цифры

           00B2  59                          POP     CX

           00B3  E8 00CB R                    CALL   XLAT_OUTPUT     ; Вывод старшей цифры выводимого числа

           00B6  58                          POP     AX        ; Восстановление младшей цифры

           00B7  E8 00CB R                    CALL   XLAT_OUTPUT     ; Вывод младшей цифры выводимого числа

           00BA  C3                           RET

           00BB                   TRANSLATE         ENDP

 

           00BB  30 31 32 33 34 35 36     ASCII_TABLE     DB     '0123456789ABCDEF'

               37 38 39 41 42 43 44

               45 46

           00CB                   XLAT_OUTPUT     PROC    NEAR

           00CB  24 0F                  AND     AL, 0FH        ; Выделение младшей цифры

           00CD  53                          PUSH    BX

           00CE  8D 1E 00BB R                LEA     BX, ASCII_TABLE ; Адрес таблицы трансляции

           00D2  D7                          XLAT    ASCII_TABLE     ; Преобразование в символьную форму

           00D3  5B                          POP     BX

           00D4  88 07                  MOV     [BX], AL        ; Сохранение очередного символа результата

           00D6  43                          INC     BX        ; Переключение на следующий символ

           00D7  C3                           RET

           00D8                   XLAT_OUTPUT     ENDP

           00D8                   CODE   ENDS

                                         END    CALCULATE_POWER

                  Фиг. 7.23 (а) (продолжение)

           A>PRINT10

           03H   447A0000H

           06H   49742400H

           09H   4E6E6B28H

           0CH   5368D4A5H

           0FH   58635FA9H

           12H   5D5E0B6BH

           15H   6258D727H

           18H   6753C21CH

           1BH   6C4ECB8FH

           1EH   7149F2CAH

           21H   76453719H

           24H   7B4097CEH

           27H   7F800000H

           Фиг. 7.23 (b)

Фиг. 7.23 (a) Степени 10; (b) Вывод процедуры степеней 10

Вам нужно посмотреть часть программы TRANSLATE, несмотря на то,  что она не использует ни одной команды сопроцессора 8087.  Эта  часть - пример подготовки чисел к печати.  В частности, команда  XLAT преобразует для печати шестнадцатеричную тетраду (значение от  0 до 0FH) в правильный символ кода ASCII (от 0 до F).  Просто  прибавлять значение тетрады к значению 0 нельзя, так как в коде  ASCII символы от A до F не следуют непосредственно за символами от  0 до 9; преобразование прекрасно выполняет команда перекодировки.  Мы используем аналогичный метод, когда преобразуем число с  плавающей точкой в пригодную для печати десятичную форму.