ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 21

Глава 20. Примеры вычислительного программирования
----------------------------------------------------------------
В следующем разделе приведены примеры программ вычислительного
характера для процессора i486, написанные на ассемблере
ASM386/486. Эти примеры иллюстрируют некоторые полезные приемы
для разработки вычислительных программных продуктов и систем на
основе процессора i486.
20.1 Пример условного ветвления
----------------------------------------------------------------
Как уже обсуждалось в Главе 15, различные числовые команды
воздействуют на биты кода условия слова состояния
модуля обработки операций с плавающей точкой, хотя существует
множество способов для того, чтобы реализовать условное
ветвление, следующее за сравнением. Далее приведен основной
подход:
- Выполнить сравнение.
- Сохранить слово состояния. (Слово состояния модуля обработки
  операций с плавающей точкой можно сохранить прямо в регистре
  AX.)
- Проверить биты кода условия.
- Перейти по результату.
На Рисунке 20-1 представлен фрагмент программы, который
иллюстрирует, как можно сравнить два находящиеся в памяти
вещественные числа двойного формата (подобная программа может
быть использована с командой FTST). Числа, которые
сравниваются в этом фрагменте, названы A и B.
Операция сравнения требует загрузки числа A в вершину
регистрового стека модуля обработки операций с плавающей точкой,
а затем сравнения его с числом B, выполняя при этом операцию
восстановления из стека. После чего слово состояния записывается
в регистр AX.
A и B имеют четыре возможных порядка, а биты C3, C2 и C0 кода
условия указывают, какой из этих порядков установлен. Три бита
располагаются в верхнем байте слова состояния модуля обработки
операций с плавающей точкой так, чтобы соответствовать флагам
нуля, четности и переноса (ZF, PF и CF), когда байт записывается
во флаги. Во фрагменте программы флаги ZF, PF и CF регистра
EFLAGS устанавливаются в значения битов C3, C2 и C0 слова
состояния, а затем используется операция условного перехода для
того, чтобы проверить флаги. Результирующая программа очень
компактна и записывается всего лишь в семь команд.
Команда FXAM обновляет все четыре бита кода условия. На Рисунке
20-2 показано, как таблица переходов может быть использована для
того, чтобы определить характер просмотренных величин. Таблица
переходов (FXAM_TBL) содержит 16 меток, по одной для каждого
возможного кода условия. Обратите внимание, что четыре значения
содержат одно и тоже - "EMPTY" (пусто). Первые два значения кода
условия соответствуют позиции "EMPTY". Два другие значения в
таблице, которые содержат "EMPTY", никогда не будут использованы
процессором i486 или математическим сопроцессором 387(TM), но
могут быть использованы при выполнении программы на сопроцессоре
80287.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і                                                               і
і        .                                                      і
і        .                                                      і
і        .                                                      і
і  A     DQ     ?                                               і
і  B     DQ     ?                                               і
і        .                                                      і
і        .                                                      і
і        .                                                      і
і        FLD    A   ;  Загружает A в вершину стека FPU          і
і        FCOMP  B   ;  Сравнение A:B, выталкивание A            і
і        FSTSW  AX  ;  Сохраняет результат в регистре AX        і
і  ;                                                            і
і  ; Коды условия содержатся в регистре ЦПУ AX                  і
і  ;     (Результаты сравнения)                                 і
і  ; Загрузка кодов условия во флаги                            і
і  ;                                                            і
і        SAHF                                                   і
і  ;                                                            і
і  ; Использует условные переходы для того, чтобы               і
і  ; определить как упорядочены A и B                           і
і  ;                                                            і
і        JP  A_B_UNORDERED  ; Тестирование бита C2 (PF)         і
і        JB  A_LESS         ; Тестирование бита C0 (CF)         і
і        JE  A_EQUAL        ; Тестирование бита C3 (ZF)         і
і  A_GREATHER:              ; C0 (CF) = 0, C3 (ZF) = 0          і
і        .                                                      і
і        .                                                      і
і  A_EQUAL:                 ; C0 (CF) = 0, C3 (ZF) = 1          і
і        .                                                      і
і        .                                                      і
і  A_LESS:                  ; C0 (CF) = 1, C3 (ZF) = 0          і
і        .                                                      і
і        .                                                      і
і  A_UNORDERED:             ; C2 (PF) = 1                       і
і        .                                                      і
і        .                                                      і
і                                                               і
і                                                               і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
         Рисунок 20-1. Условное Ветвление при Сравнении
Фрагмент программы выполняет команду FXAM и сохраняет слово
состояния. Затем идет манипуляция битами кода условия для
окончательного вывода числа в регистр AX, которое равно коду
условия, умноженному на 2. При этом включается обнуление
неиспользуемых битов в байте, который содержит код, происходит
сдвиг бита C3 вправо так, чтобы он был соседним с битом C2, и
затем происходит сдвиг кода для того, чтобы умножить его на 2.
Полученное значение используется как индекс, по которому
выбирается одна из меток из таблицы переходов FXAM TBL
(умножение кода условия требуется из-за 2-х байтной длины
каждого значения в таблице). Команда безусловного перехода JMP
направляет процессор через таблицу переходов к процедуре,
которая содержит программу (не показанную в примере) для
обработки каждого возможного результата каманды FXAM.
20.2 Примеры обработки исключений
----------------------------------------------------------------
Существует множество подходов для написания обработчиков
исключительных ситуаций. Один полезный прием - это сделать
процедуру обработки исключчений из трех частей: "пролога",
"тела" и "эпилога". Эта процедура вызывается через прерывание
номер 16.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і                                                               і
і    ; Таблица переходов для процедуры проверки                 і
і    ;                                                          і
і    FXAM_TBL    DD POS_UNNORM, POS_NAN, NEG_UNNORM, NEG_NAN,   і
і    &           POS_NORM, POS_INFINITY, NEG_NORM,              і
і    &           NEG_INFINITY, POS_ZERO, EMPTY, NEG_ZERO,       і
і    &           EMPTY, POS_DENORM, EMPTY, NEG_DENORM, EMPTY    і
і         .                                                     і
і         .                                                     і
і    ; Проверить ST и сохранить результат (коды условия)        і
і    ;                                                          і
і         FXAM                                                  і
і         XOR   EAX, EAX   ; Очистить регистр EAX               і
і         FSTSW AX                                              і
і    ;                                                          і
і    ; Вычисление смещения в таблице переходов                  і
і    ;                                                          і
і         AND   AX, 0100011100000000B  ; Очистить все биты      і
і    ;                                   кроме C3, C2-C0        і
і         SHR   EAX, 6   ; Сдвинуть C2-C0 как (000XXX00)        і
і         SAL   AH, 5    ; Установить C3 как  (00X00000)        і
і         OR    AL, AH   ; Слить C3 и C2-C0   (00XXXX00)        і
і         XOR   AH, AH   ; Удалить старую копию C3              і
і    ;                                                          і
і    ; Перейти к процедуре по коду условия                      і
і    ;                                                          і
і         JMP   FXAM_TBL[EAX]                                   і
і    ;                                                          і
і    ; Метки таблицы перехода, по одной для обработки           і
і    ; каждого возможного результата FXAM                       і
і    ;                                                          і
і    POS_UNNORM:                                                і
і        .                                                      і
і    POS_NAN:                                                   і
і        .                                                      і
і    NEG_UNNORM:                                                і
і        .                                                      і
і    NEG_NAN:                                                   і
і        .                                                      і
і    POS_NORM:                                                  і
і        .                                                      і
і    POS_INFINITY:                                              і
і       .                                                       і
і    NEG_NORM:                                                  і
і        .                                                      і
і    NEG_INFINITY:                                              і
і        .                                                      і
і    POS_ZERO:                                                  і
і        .                                                      і
і    EMPTY:                                                     і
і        .                                                      і
і    NEG_ZERO:                                                  і
і        .                                                      і
і    POS_DENORM:                                                і
і        .                                                      і
і    NEG_DENORM:                                                і
і        .                                                      і
і                                                               і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
       Рисунок 20-2. Условное Ветвление при Операции FXAM
При передаче управления на процедуру обработки исключения,
все прерывания запрещаются аппаратным способом. Пролог
выполняет все функции, которые должны быть защищены от
возможного прерывания более высоким приоритетом. Обычно это
сохранение регистров и передача диагностической информации из
модуля обработки операций с плавающй точкой в память. Когда
критический процесс будет завершен, пролог может разрешить
прерывания для того, чтобы позволить обработчикам прерываний с
более высоким приоритетом выполниться раньше обработчика
исключения.
Тело обработчика исключения просматривает диагностическую
информацию и реагирует на то, что необходимо с точки зрения
программного продукта. Эта реакция может иметь диапазон от
прерывания выполнения для того, чтобы выдать сообщение, до
попытки уладить неполадку и продолжить нормальное выполнение.
Эпилог, собственно, выполняет действия обратные прологу,
возвращая процессор в состояние, при котором можно было бы
возобновить нормальное выполнение. Эпилог не должен загружать
немаскированный флаг исключения в модуль обработки операций с
плавающей точкой или потребовать немедленно другой
исключительной ситуации.
На рисунках от Рисунка 20-3 до Рисунка 20-5 показаны скелетные
схемы исходных кодов на ассемблере ASM386/486 трех обработчиков
исключительных ситуаций. При этом демонстрируется как могут быть
написаны пролог и эпилог в различных ситуациях, а на месте, где
должно быть помещено зависящее от применения тело обработчика
исключения, стоят комментарии.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і                                                               і
і    SAVE_ALL        PROC                                       і
і    ;                                                          і
і    ; Сохранить регистры, занести                              і
і    ; отображение состояния FPU в стек                         і
і    ;                                                          і
і       PUSH  EBP                                               і
і       MOV   EBP, ESP                                          і
і       SUB   ESP, 108                                          і
і    ;                                                          і
і    ; Сохранить полное состояние FPU, разрешить прерывания     і
і    ;                                                          і
і       FNSAVE [EBP-108]                                        і
і       STI                                                     і
і    ;                                                          і
і    ; Далее следует программа обработчика исключений           і
і    ; в зависимости от цели обработки                          і
і    ;                                                          і
і    ; Очистить флаги исключений в слове состояния              і
і    ; (которое находится в памяти)                             і
і    ; Восстановить модифицированное отображение состояния      і
і    ;                                                          і
і       MOV     BYTE PTR [EBP-104], 0H                          і
і       FRSTOR  [EBP-108]                                       і
і    ;                                                          і
і    ; Освободить стек, восстановить регистры                   і
і    ;                                                          і
і       MOVE  ESP, EBP                                          і
і         .                                                     і
і         .                                                     і
і       POP   EBP                                               і
і    ;                                                          і
і    ; Вернуться к прерванным вычислениям                       і
і    ;                                                          і
і       IRET                                                    і
і    SAVE_ALL        ENDP                                       і
і                                                               і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
   Рисунок 20-3. Обработчик Исключения Полного Состояния
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і                                                               і
і    SAVE_ENVIRONMENT  PROC                                     і
і    ;                                                          і
і    ; Сохранить регистры, занести                              і
і    ; окружение FPU в стек                                     і
і    ;                                                          і
і       PUSH  EBP                                               і
і                                                               і
і       MOV   EBP, ESP                                          і
і       SUB   ESP, 28                                           і
і    ;                                                          і
і    ; Сохранить окружение FPU, разрешить прерывания            і
і    ;                                                          і
і       FNSTENV [EBP-28]                                        і
і       STI                                                     і
і    ;                                                          і
і    ; Далее следует программа обработчика исключений           і
і    ; в зависимости от цели обработки                          і
і    ;                                                          і
і    ; Очистить флаги исключений в слове состояния              і
і    ; (которое находится в памяти)                             і
і    ; Восстановить модифицированное отображение окружения      і
і    ;                                                          і
і       MOV     BYTE PTR [EBP-24], 0H                           і
і       FLDENV  [EBP-28]                                        і
і    ;                                                          і
і    ; Освободить стек, восстановить регистры                   і
і    ;                                                          і
і       MOVE  ESP, EBP                                          і
і       POP   EBP                                               і
і    ;                                                          і
і    ; Вернуться к прерванным вычислениям                       і
і    ;                                                          і
і       IRET                                                    і
і    SAVE_ENVIRONMENT  ENDP                                     і
і                                                               і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
    Рисунок 20-4. Обработчик Исключения Сокращенного Времени
                  Ожидания
Тексты на Рисунках 20-3 и 20-4 очень похожи; их отличие
заключается только в выборе команд для сохранения и
восстановления состояния модуля обработки операций с плавающей
точкой. Выбор здесь заключается в повышенной диагностической
информации, предоставляемой командой FNSAVE, и более быстрым
выполнением команды FNSTENV. Для тех приложений, которые
чувствительны к времени ожидания прерывания или которым не надо
просматривать содержимое регистров, команда FNSTENV сокращает
продолжительность "критической зоны", в течение которой
процессор не видит требования других прерываний.
После выполнения тела обработчика исключения, эпилог
приготавливает процессор для того, чтобы продолжить выполнение
от точки прерывания (то есть, команды, следующей за той, которая
создала немаскированное исключение). Обратите внимание, что
флаги исключения в памяти, которые загружается в модуль
обработки операций с плавающей точкой, обращаются в нуль для
перезагрузки (фактически, в этих примерах обнуляется все
отображение слова состояния).
Примеры на Рисунках 20-3 и 20-4 предполагают, что сам обработчик
исключения не будет причиной другой немаскируемого исключения.
Там, где это возможно, можно применять общий подход, показанный
на Рисунке 20-5. Основной прием - сохранить полное состояние
модуля обработки операций с плавающей точкой и затем загрузить
новое управляющее слово в прологе. Обращаем ваше внимание на то,
что надо быть очень осторожным при разработке обработчиков
исключений этого типа для того, чтобы уберечь обработчик от
бесконечных обращений.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і                                                               і
і            .                                                  і
і            .                                                  і
і            .                                                  і
і        LOCAL_CONTROL  DW  ?   ; Инициализировано              і
і            .                                                  і
і            .                                                  і
і            .                                                  і
і    REENTRANT       PROC                                       і
і    ;                                                          і
і    ; Сохранить регистры, занести                              і
і    ; отображение состояния FPU в стек                         і
і    ;                                                          і
і       PUSH  EBP                                               і
і         .                                                     і
і         .                                                     і
і         .                                                     і
і       MOV   EBP, ESP                                          і
і       SUB   ESP, 108                                          і
і    ;                                                          і
і    ; Сохранить полное состояние FPU, загрузить новое          і
і    ; управляющее слово, разрешить прерывания                  і
і    ;                                                          і
і       FNSAVE [EBP-108]                                        і
і       FLDCW  LOCAL_CONTROL                                    і
і       STI                                                     і
і         .                                                     і
і         .                                                     і
і         .                                                     і
і    ;                                                          і
і    ; Далее следует программа обработчика исключений           і
і    ; в зависимости от цели обработки                          і
і    ; Генерируемое здесь немаскированное исключение            і
і    ; будет причиной вызова обработчика исключений.            і
і    ; Если необходима локальная память, то можно               і
і    ; расположить данные в стеке.                              і
і         .                                                     і
і         .                                                     і
і         .                                                     і
і    ; Очистить флаги исключений в слове состояния              і
і    ; (которое находится в памяти)                             і
і    ; Восстановить модифицированное отображение состояния      і
і    ;                                                          і
і       MOV     BYTE PTR [EBP-104], 0H                          і
і       FRSTOR  [EBP-108]                                       і
і    ;                                                          і
і    ; Освободить стек, восстановить регистры                   і
і    ;                                                          і
і       MOVE  ESP, EBP                                          і
і         .                                                     і
і         .                                                     і
і       POP   EBP                                               і
і    ;                                                          і
і    ; Вернуться в точку, где произошло прерывание              і
і    ;                                                          і
і       IRET                                                    і
і    REENTRANT       ENDP                                       і
і                                                               і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
      Рисунок 20-5. Реентерабельный Обработчик Исключений
20.3 Пример перевода числа с плавающей точкой в символьный вид
----------------------------------------------------------------
Вычислительные программы должны формировать свои результаты для
того, чтобы они были воспринимаемы и читаемы пользователем
программы. В большинстве случаев, числовые результаты выводятся
как строки символов ASCII для того, чтобы их можно было
отпечатать или просмотреть на дисплее. Этот пример показывает,
как величина с плавающей точкой может быть переведена в
десятичную строку ASCII символов. Функция, показанная на
Рисунке 20-6, может быть вызвана из программ на языках
PL/M-386/486, Pascal386/486, FORTRAN-386/486 или ASM386/486.
При написании программы обращалось внимание скорее на
краткость, скорость и точность выполнения, чем на максимизацию
числа значащих цифр. Сделана попытка оставить целые в их
собственных областях для того, чтобы предотвратить случайные
ошибки при переводе чисел.
Используя числа в вещественном формате расширенной точности, эта
подпрограмма достигает в наихудшем случае точности три единицы в
16-том десятичном разряде для нецелелочисленных величин или
целых чисел, больших чем 10**(18). Это двойная точность. Если
число имеет десятичный порядок, меньший чем 100, точность
составляет одна единица в 17-том десятичном разряде.
Более высокая точность может быть достигнута при больших
затратах на программирование, увеличении размера программы более
низкой эффективности.
20.3.1 Составляющие части функции
----------------------------------------------------------------
Перевод чисел реализуется тремя отдельными модулями. Главную
работу выполняем модуль FLOATING TO ASCII (Число с плавающей
точкой в ASCII-строку). Другие модули представлены отдельно,
потому что они имеют более общее назначение. Один из них, GET
POWER 10 (Взять степень 10-ти), используется также и
подпрограммой перевода чисел из ASCII вида в формат с плавающей
точкой. Другой маленький модуль, TOP STATUS (Состояние вершины
стека), идентифицирует то, что находится в вершине числового
регистрового стека.
20.3.2 Соглашения по исключениям
----------------------------------------------------------------
Необходимо предотвратить функцию от генерирования внутри нее
исключительных ситуаций. Принимаются любые числовые значения,
при этом только возможные исключения занимают стек числовых
регистров.
Любое значение, посланное в числовой стек, проверяется на
существование, тип (NaN или бесконечность) и состояние
(денормальное, ноль, знак). Размер строки тестируется при
минимальном и максимальном значении. Если вершина регистрового
стека пуста или размер строки слишком мал, функция возвращает
код ошибки.
Переполнение и отрицательное переполнение при очень больших или
очень маленьких числах предотврашаются внутри функции.
Рисунок 20-6. Подпрограмма Перевода Чисел с Плавающей Точкой в
              ASCII Вид
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
ДДДДДДДДДї
і                                                                
         і
і                                                                
         і
і  SOURCE                                                        
         і
і                                                                
         і
і  +1 $title('Перевод числа с плавающей точкой в ASCII вид')     
         і
і                                                                
         і
і                                                                
         і
і       name    floating_to_ascii                                
         і
і                                                                
         і
і       public  floating_to_ascii                                
         і
і       extrn   get_power_10:near, tos_status:near               
         і
і  ;                                                             
         і
і  ; Эта подпрограмма переводит число с плавающей точкой         
         і
і  ; из вершины стека FPU в строку типа ASCII и отделяет         
         і
і  ; степень 10, масштабируя значение (в двоичном виде).         
         і
і  ; Максимальная длина строки символов регулируется             
         і
і  ; параметром, который должен быть больше 1.                   
         і
і  ; Не-нормальные значения, денормальные значения и псевдо      
         і
і  ; -нули переводятся корректно. Однако, не-нормальные          
         і
і  ; величины и псевдо-нули более не поддерживаются              
         і
і  ; процессором i486 (в соответствии со стандартом IEEE) и      
         і
і  ; внутренне не генерируются. Возвращаемое значение            
         і
і  ; указывает сколько двоичных разрядов точности было           
         і
і  ; потеряно в не-нормальных или денормальных значениях.        
         і
і  ; Также указывается и величина мантиссы псевдо-нуля           
         і
і  ; (двоичным порядком). Целые числа меньшие 10**18             
         і
і  ; переводятся точно, если принимающая строка ASCII            
         і
і  ; символов содержит достаточное количество позиций для        
         і
і  ; цифр этих чисел. В противном случае значение                
         і
і  ; переводиться в научную нотацию.                             
         і
і  ;                                                             
         і
і  ; В зависимости от результата подпрограмма выдает             
         і
і  ; следующие значения:                                         
         і
і  ;                                                             
         і
і  ;       0  перевод выполнен, размер строки определен          
         і
і  ;       1  недопустимые аргументы                             
         і
і  ;       2  точный целочисленный перевод, размер строки        
         і
і  ;            определен                                        
         і
і  ;       3  неопределенность                                   
         і
і  ;       4  + NaN (не-число)                                   
         і
і  ;       5  - NaN                                              
         і
і  ;       6  + Бесконечность                                    
         і
і  ;       7  - Бесконечность                                    
         і
і  ;       8  встречен псевдо-ноль, размер строки определен      
         і
і  ;                                                             
         і
і  ;    Интерфейс для вызова из PLM-386/486                      
         і
і  ;                                                             
         і
і  ; floating_to_ascii:                                          
         і
і  ;   procedure (number, denormal_ptr, string_ptr, size_ptr,    
         і
і  ;   field_size, power_ptr) word external;                     
         і
і  ;   declare (denormal_ptr, string_ptr, power_ptr, size_ptr)   
         і
і  ;   pointer;                                                  
         і
і  ;   declare field_size word;                                  
         і
і  ;   string_size based size_ptr word;                          
         і
і  ;   declare number real;                                      
         і
і  ;   declare denormal integer based denormal_ptr;              
         і
і  ;                                                             
         і
і                                                                
         і
і  ; declare power integer based power_ptr;                      
         і
і  ; end floating_to_ascii;                                      
         і
і  ;                                                             
         і
і  ; Величина с плавающей точкой должна быть на вершине          
         і
і  ; стека FPU. Эта подпрограмма требует три свободные           
         і
і  ; регистра в стеке и после отработки выталкивает              
         і
і  ; передаваемые значения из стека. Полученная строка           
         і
і  ; будет иметь начальный символ либо "+", либо "-",            
         і
і  ; указывая на знак величины. Затем следуют десятичные         
         і
і  ; цифры в ASCII виде. Числовое значение строки типа           
         і
і  ; ASCII будет равно (ASCII СТРОКА)*10**ПОРЯДОК. Если          
         і
і  ; данное число было нулем, то строка будет содержать          
         і
і  ; только знак и один символ 0. Величина размера строки        
         і
і  ; (string_size) указывает на полную длину строки символов,    
         і
і  ; включая символ знака. Строка (0) всегда будет               
         і
і  ; содержать знак. Возможно, что размер строки будет           
         і
і  ; меньше размера поля. Это бывает при нулях и целых           
         і
і  ; числах. Псевдо-ноль выдает особый код возврата. При         
         і
і  ; денормальных числах указывается степень двух                
         і
і  ; представленного значения. Степень десяти и строка будут     
         і
і  ; такими же, как если бы величина была бы простым нулем.      
         і
і  ;                                                             
         і
і  ; Эта подпрограмма точно выдает десятичные целые до 18        
         і
і  ; цифр. Целые величины имеют десятичный показатель            
         і
і  ; степени в строке из нулей. При нецелых величинах            
         і
і  ; точность результата заключена в двух последних              
         і
і  ; десятичных цифрах (двойная точность). Для                   
         і
і  ; масштабирования величины в диапазоне, приемлемом для        
         і
і  ; данных типа BCD, используются команды возведения в          
         і
і  ; степень. Для перевода используется режим округления,        
         і
і  ; действующий при входе в подпрограмму.                       
         і
і  ;                                                             
         і
і  ;       Следующие регистры не видны:                          
         і
і  ;                                                             
         і
і  ;      eax ebx ecx edx esi edi eflags                         
         і
і  ;                                                             
         і
і  ; Определение стека.                                          
         і
і  ;                                                             
         і
і  ebp_save       equ     dword ptr [ebp]                        
         і
і  es_save        equ     ebp_save + size ebp_save               
         і
і  return_ptr     equ     es_save + size es_save                 
         і
і  power_ptr      equ     return_ptr + size return_ptr           
         і
і  field_size     equ     power_ptr + size power_ptr             
         і
і  size_ptr       equ     field_size + size size_ptr             
         і
і  string_ptr     equ     size_ptr + size size_ptr               
         і
і  denormal_ptr   equ     string_ptr + size string_ptr           
         і
і                                                                
         і
і  parms_size     equ     size power_ptr + size field_size +     
         і
і  &              size    size_ptr + size string_ptr +           
         і
і  &              size    denormal_ptr                           
         і
і  ;                                                             
         і
і  ; Определение используемых констант.                          
         і
і  ;                                                             
         і
і  BCD_DIGITS   equ  18  ; Количество цифр в величине типа BCD   
         і
і  WORD_SIZE    equ  4                                           
         і
і  BCD_SIZE     equ  10                                          
         і
і  MINUS        equ  1   ; Определение выдаваемых значений       
         і
і  NAN          equ  4   ; Выбраные здесь точные величины -      
         і
і  INFINITY     equ  6   ; важны. Они должны соответствовать     
         і
і  INDEFINITE   equ  3   ; возможным выдаваемым значениям и      
         і
і  PSEUDO_ZERO  equ  8   ; тестироваться в том же порядке, как   
         і
і  INVALID      equ  -2  ; показано в этой программе.            
         і
і  ZERO         equ  -4                                          
         і
і  DENORMAL     equ  -6                                          
         і
і  UNNORMAL     equ  -8                                          
         і
і  NORMAL       equ  0                                           
         і
і  EXACT        equ  2                                           
         і
і  ;                                                             
         і
і  ;       Определение положения временной области хранения.     
         і
і  ;                                                             
         і
і  power_two    equ  word ptr [ebp - WORD_SIZE]                  
         і
і  bcd_value    equ  tbyte ptr power_two - BCD_SIZE              
         і
і  bcd-byte     equ  byte ptr bcd_value                          
         і
і  fraction     equ  bcd_value                                   
         і
і                                                                
         і
і  lokal_size   equ  size power_two + size bcd_value             
         і
і  ;                                                             
         і
і  ;      Выделить достаточный объем стека для                   
         і
і  ;      временных результатов.                                 
         і
і  ;                                                             
         і
і  stack  stackseg (lokal_size+6) ; Выделить пространство стека  
         і
і                                 ; для локальных данных.        
         і
і  +1 $eject                                                     
         і
і                                                                
         і
і  code         segment public er                                
         і
і               extrn   power_table:qword                        
         і
і  ;                                                             
         і
і  ;      Константы, используемые этой функцией.                 
         і
і  ;                                                             
         і
і               even               ; Оптимизировать до 16 цифр.  
         і
і  const10      dw      10         ; Подрегулировать значение для
         і
і  ;                               ; слишком больших BCD.        
         і
і  ;                                                             
         і
і  ;        Перевести биты C3, C2, C1 и C0 в                     
         і
і  ;        значащие флаги и величины, используя                 
         і
і  ;        процедуру tos_status.                                
         і
і  ;                                                             
         і
і  status_table db      UNNORMAL, NAN, UNNORMAL+MINUS,           
         і
і  &                    NAN+MINUS, NORMAL, INFINITY,             
         і
і  &                    NORMAL+MINUS, INFINITY+MINUS,            
         і
і  &                    ZERO, INVALID, ZERO+MINUS, INVALID,      
         і
і  &                    DENORMAL, INVALID, DENORMAL+MINUS, INVALI
D        і
і  floting_to_ascii proc                                         
         і
і                                                                
         і
і         call   tos_status   ; Посмотреть на состояние ST(0)    
         і
і  ;                                                             
         і
і  ;         Взять дескриптор из таблицы                         
         і
і  ;                                                             
         і
і         movzx      eax, status_table[eax]                      
         і
і         cmp        al,INVALID             ; ST(0) пуст?        
         і
і         jne        not_empty                                   
         і
і  ;                                                             
         і
і  ;        ST(0) - пуст! Возвращает значение состояния.         
         і
і  ;                                                             
         і
і         ret    parms_size                                      
         і
і  ;                                                             
         і
і  ;        Удалить бесконечность из стека и выйти.              
         і
і  ;                                                             
         і
і  found_infinity:                                               
         і
і         fstp       st(0)       ; Оставить fstp                 
         і
і         jmp        short exit_proc                             
         і
і  ;                                                             
         і
і  ;              Длина строки слишком мала!                     
         і
і  ;              Выдает код недопустимости.                     
         і
і  ;                                                             
         і
і  small_string:                                                 
         і
і         mov    al,INVALID                                      
         і
і  exit_proc:                                                    
         і
і         leave                          ; Восстановить стек     
         і
і                                                                
         і
і         pop    es                                              
         і
і         ret    parms_size                                      
         і
і  ;                                                             
         і
і  ;          В ST(0) находится NaN или                          
         і
і  ;          неопределенность. Сохранить значение               
         і
і  ;          в памяти и просмотреть дробное поле                
         і
і  ;          для того, чтобы отличить                           
         і
і  ;          неопределенность от обычного NaN.                  
         і
і  ;                                                             
         і
і  NAN_or_indefinite:                                            
         і
і         fstp   fraction       ; Для проверки - удалить значение
         і
і                               ; из стека.                      
         і
і         test   al,MINUS       ; Посмотреть бит знака.          
         і
і         fwait                 ; Убедиться в выполнении сохранен
ия.      і
і         jz     exit_proc      ; Если положительный знак, то не 
         і
і                               ; может быть неопределенностью.  
         і
і                                                                
         і
і         mov    ebx,0C0000000H ; Подавить верхние 32 разряда дро
бной     і
і                               ; части.                         
         і
і                                                                
         і
і  ;             Сравнить разряды 63-32                          
         і
і         sub    ebx,dword ptr fraction + 4                      
         і
і                                                                
         і
і  ;             Разряды 31-0 должны быть нулями                 
         і
і         or     ebx,dword ptr fraction                          
         і
і         jnz    exit_proc                                       
         і
і                                                                
         і
і  ;             Установить выдаваемое значение на               
         і
і  ;             неопределенность                                
         і
і         mov   al,INDEFINITE                                    
         і
і         jmp   exit_proc                                        
         і
і  ;                                                             
         і
і  ;             Выделить место в стеке для локальных переменных 
         і
і  ;             и установить параметр адресации.                
         і
і  ;                                                             
         і
і  not_empty:                                                    
         і
і         push  es            ; Сохранить рабочий регистр        
         і
і         enter local_size,0  ; Установить адресацию стека       
         і
і                                                                
         і
і  ;             Проверить, достаточно ли места в строке         
         і
і         mov   ecx,field_size                                   
         і
і         cmp   ecx,2                                            
         і
і         jl    small_string                                     
         і
і                                                                
         і
і         dec   ecx           ; Установить символ знака          
         і
і                                                                
         і
і  ; Посмотреть, может быть строка слишком большая для типа BCD  
         і
і         cmp   ecx,BCD_DIGITS                                   
         і
і         jbe   size_ok                                          
         і
і                                                                
         і
і  ; Иначе установить максимальный размер строки                 
         і
і         mov   ecx,BCD_DIGITS                                   
         і
і  size_ok:                                                      
         і
і         cmp   al,INFINITY   ; Может быть бесконечность?        
         і
і                                                                
         і
і  ; Возвратить значение состояния для + или - бесконечности     
         і
і         jge   found_infinity                                   
         і
і                                                                
         і
і         cmp   al,NAN              ; Можеть быть NaN или        
         і
і         jge   NAN_or_indefinite   ; неопределенность           
         і
і                                                                
         і
і  ;                                                             
         і
і  ;             Установить выдаваемое значение по умолчанию     
         і
і  ;             и проверить, нормализовано ли число.            
         і
і  ;                                                             
         і
і         fabs        ; Использовать только положительные значени
я        і
і  ;                                                             
         і
і  ;      Бит знака в регистре AL содержит истинный знак величины
.        і
і  ;                                                             
         і
і         xor   edx,edx          ; Подготовить константу 0       
         і
і         mov   edi,denormal_ptr ; Обнулить счетчик денормальных 
чисел    і
і         mov   [edi],dx                                         
         і
і         mov   ebx,power_ptr    ; Обнулить значение степени деся
ти       і
і         mov   [ebx],dx                                         
         і
і         mov   dl,al                                            
         і
і         and   dl,1                                             
         і
і         add   dl,EXACT                                         
         і
і         cmp   al,ZERO          ; Проверить на ноль             
         і
і         jae   convert_integer  ; Перейти программу возведения в
         і
і                                ; степень, если значение равно н
улю.     і
і                                                                
         і
і         fstp  fraction                                         
         і
і         fwait                                                  
         і
і         mov   al,bcd_byte + 7                                  
         і
і         or    byte ptr bcd_byte +7,80h                         
         і
і         fld   fraction                                         
         і
і         fxtract                                                
         і
і         test  al,80h                                           
         і
і         jnz   normal_value                                     
         і
і                                                                
         і
і         fld1                                                   
         і
і         fsub                                                   
         і
і         ftst                                                   
         і
і         fstsw ax                                               
         і
і         sahf                                                   
         і
і         jnz   set_unnormal_count                               
         і
і  ;                                                             
         і
і  ;                 Найден псевдо-ноль                          
         і
і  ;                                                             
         і
і         fldlg2                 ; Оценить степень десяти        
         і
і         add   dl,PSEUDO_ZERO - EXACT                           
         і
і         fmulp st(2),st                                         
         і
і         fxch                   ; Взять степеть десяти          
         і
і         fistp word ptr [ebx]   ; Установить степень десяти     
         і
і         jmp   convert_integer                                  
         і
і                                                                
         і
і  set_unnormal_count:                                           
         і
і         fxtract                ; Взять исходную дробь,         
         і
і                                ; и нормализовать.              
         і
і         fxch              ; Взять счетчик не-нормальных чисел  
         і
і         fchs                                                   
         і
і         fistp word ptr [edi]   ; Установить счетчик            
         і
і                                ; не-нормальных чисел           
         і
і                                                                
         і
і  ;                                                             
         і
і  ; Вычислить десятичную величину вместе с этим числом          
         і
і  ; внутри одного порядка.                                      
         і
і  ;                                                             
         і
і  ; Всегда при округлении будет присутствовать ошибка           
         і
і  ; из-за потери точности. В результате, мы                     
         і
і  ; преднамеренно при вычислении порядка не стали               
         і
і  ; рассматривать LOG10 от значения дроби. Так как              
         і
і  ; дробь всегда больше или равна 1 и меньше двух, то           
         і
і  ; LOG10 от этой дроби не изменяет основной точности           
         і
і  ; функции. Для того, чтобы получить десятичный                
         і
і  ; порядок величины, надо просто умножить степень              
         і
і  ; двух на LOG10(2) и округлить результат с                    
         і
і  ; отсечением до целого.                                       
         і
і  ;                                                             
         і
і  normal_value:                                                 
         і
і         fstp  fraction    ; Для дальнейшего использования      
         і
і                           ; сохранить поле дроби.              
         і
і         fist  power_twq   ; Сохранить степень двух             
         і
і         fldlg2            ; Взять LOG10(2)                     
         і
і                           ; Теперь можно безопасно использовать
         і
і                           ; power_two                          
         і
і         fmul              ; Подготовить LOG10 от порядка числа 
         і
і         fistp word ptr [ebx]   ; Здесь может быть применен любо
й        і
і                                ; режим округления              
         і
і  ;                                                             
         і
і  ;            Проверить, чтобы величина числа                  
         і
і  ;            интерпретировалась как целое.                    
         і
і  ;                                                             
         і
і  ; CX имеет максимально позволенное количество десятичных цифр.
         і
і  ;                                                             
         і
і         fwait              ; Ожидать допустимой степени десяти 
         і
і  ;                                                             
         і
і  ;             Возвести десять в степень величины значения     
         і
і  ;                                                             
         і
і         movsx si,word ptr [ebx]                                
         і
і         sub   esi,ecx         ; Подготовить в AX необходимый   
         і
і                               ; коэффициент масштабирования.   
         і
і         ja    adjust_result   ; Перейти, если число не подходит
         і
і  ;                                                             
         і
і  ; Число между 1 и 10**(field_size - размер поля)              
         і
і  ; Проверить на целочисленность                                
         і
і  ;                                                             
         і
і         fild  power_two         ; Восстановить начальное значен
ие       і
і         sub   dl,NORMAL-EXACT   ; Перевести в точное выдаваемое
         і
і                                 ; значение                     
         і
і         fld   farction                                         
         і
і         fscale              ; Подготовить полную величину, в эт
ой       і
і                             ; команде она не портится          
         і
і         fst   st(1)         ; Скопировать значение для сравнени
я        і
і         frndint             ; Проверить на целочисленность     
         і
і         fcomp               ; Сравнить значения                
         і
і         fstsw ax            ; Сохранить состояние              
         і
і         sahf                ; C3 = 1, значит это была целое чис
ло       і
і                                                                
         і
і         jnz   convert_integer                                  
         і
і                                                                
         і
і         fstp  st(0)            ; Удалить нецелочисленное значен
ие       і
і         add   dl,NORMAL-EXACT  ; Восстановить начальнное выдава
емое     і
і                                ; значение                      
         і
і  ;                                                             
         і
і  ; Масштабировать число внутри диапазона,                      
         і
і  ; позволенного форматом BCD. Операция                         
         і
і  ; масштабирования выдает число внутри одного                  
         і
і  ; десятичного порядка величины наибольшего                    
         і
і  ; десятичного числа, представимого для данной длины           
         і
і  ; строки.                                                     
         і
і  ;                                                             
         і
і  ;     Величина степени десяти для масштабирования             
         і
і  ;     находится в регистре SI.                                
         і
і  ;                                                             
         і
і  adjust_result:                                                
         і
і         mov   eax,esi            ; Подготовить для возведения в
         і
і                                  ; степень                     
         і
і         mov   word ptr [ebx],ax  ; Установить начальную величин
у        і
і                                  ; степени десяти              
         і
і         neg   eax                ; Вычесть единицу для каждого 
порядка  і
і                                  ; величины, на который масштаб
ируется  і
і                                  ; значение.                   
         і
і         call  get_power_10       ; Коэффициент масштабирования 
         і
і                                  ; представляется как порядок и
 дробь.  і
і         fld   fraction           ; Взять дробь                 
         і
і         fmul                     ; Комбинировать дроби         
         і
і         mov   esi,ecx            ; Возвести десять в максимальн
ую       і
і                                  ; степень                     
         і
і         shl   esi,3              ; Для того, чтобы значение BCD
         і
і                                  ; входило в строку            
         і
і         fild  power_two          ; Комбинировать степень двух  
         і
і         faddp st(2),st                                         
         і
і         fscale                   ; Подготовить полное значение,
         і
і                                  ; порядок остался не тронутым 
         і
і         fstp  st(1)              ; Удалить порядок             
         і
і  ;                                                             
         і
і  ; Проверьте установленное значение по таблице                 
         і
і  ; точных степеней десяти. Суммарные ошибки оценки             
         і
і  ; величины и степенной функции могут привести к               
         і
і  ; тому, что значение одного порядка величины будет            
         і
і  ; либо слишком маленькое, либо слишком большое для            
         і
і  ; поля типа BCD. Для устранения этой проблемы                 
         і
і  ; протестируйте полученное значение - является ли             
         і
і  ; оно слишком большим или слишком маленьким. Затем            
         і
і  ; отрегулируйте его и значение степени десяти.                
         і
і  ;                                                             
         і
і  test_power:                                                   
         і
і  ;                                                             
         і
і  ; Сравните с точной степенью. Используйте следующую           
         і
і  ; степень при уменьшении CX на единицу.                       
         і
і  ;                                                             
         і
і         fcom  power_table[esi]+type power_table                
         і
і         fstsw ax               ; Не надо ждать                 
         і
і         sahf                   ; Если C3=C0=0, то слишком больш
ое       і
і         jb    test_for-small                                   
         і
і         fidiv const10          ; Иначе установить значение     
         і
і         and   dl,not EXACT     ; Удалить флаг того, что число т
очное    і
і         inc   word ptr [ebx]   ; Установить значение степени де
сяти     і
і         jmp   short in_range   ; Перевести значение в целое тип
а BCD    і
і                                                                
         і
і  test_for_small:                                               
         і
і         fcom  power_table[esi] ; Проверить относительный размер
         і
і                                                                
         і
і         fstsw ax               ; Не ждать                      
         і
і         sahf                   ; Если C0 = 0, то ST(0) больше и
ли       і
і                                ; равен нижшей границе          
         і
і         jc    in_range         ; Перевести значение в целое тип
а BCD    і
і         fimul const10          ; Подогнать значение под диапазо
н        і
і         dec   word ptr [ebx]   ; Подобрать значение степени дес
яти      і
і  in_range:                                                     
         і
і         frndint                ; Подготовить целое значение    
         і
і  ;                                                             
         і
і  ; Утверждение: 0 <= TOS <= 999,999,999,999,999,999            
         і
і  ; Число TOS будет точно представлено                          
         і
і  ; 18-ю цифрами в формате BCD.                                 
         і
і  ;                                                             
         і
і  convert_integer:                                              
         і
і         fbstp bcd_value        ; Сохранить число в формате BCD 
         і
і  ;                                                             
         і
і  ; При сохранении формата BCD установить регистры              
         і
і  ; для перевода в ASCII вид.                                   
         і
і  ;                                                             
         і
і         mov   esi,BCD_SIZE-2   ; Инициализировать значение     
         і
і                                ; индекса BCD                   
         і
і         mov   cx,0f04h         ; Установить счетчик сдвига и ма
ску      і
і         mov   ebx,1            ; Установить начальный размер AS
CII      і
і                                ; поля для знака                
         і
і         mov   edi,string_ptr   ; Взять адрес начала ASCII строк
и        і
і         mov   ax,ds            ; Скопировать DS в ES           
         і
і         mov   es,ax                                            
         і
і         cld                    ; Установить режим автоматическо
го       і
і                                ; добавления единицы            
         і
і         mov   al,'+'           ; Очистить поле знака           
         і
і         test  dl,MINUS         ; Проверить на отрицательное зна
чение    і
і         jz    positive_result                                  
         і
і                                                                
         і
і         mov   al,'-'                                           
         і
і  positive_result:                                              
         і
і         stosb                  ; Установить указатель строки на
         і
і                                ; последний знак                
         і
і         and   dl,not MINUS     ; Выключить бит знака           
         і
і         fwait                  ; Ожидать окончания команды fbst
p        і
і  ;                                                             
         і
і  ; Используемые регистры:                                      
         і
і  ;                                                             
         і
і  ;          AH:          байт со значением типа BCD            
         і
і  ;          AL:          значение строки ASCII                 
         і
і  ;          DX:          возвращаемое значение                 
         і
і  ;          CH:          маска BCD = 0fh                       
         і
і  ;          CL:          счетчик сдвига BCD = 4                
         і
і  ;          BX:          ширина поля строки ASCII              
         і
і  ;          ESI:         индекс поля BCD                       
         і
і  ;          DI:          указатель поля строки BCD             
         і
і  ;          DS, ES:      адрес сегмента строки ASCII           
         і
і  ;                                                             
         і
і  ;          Удалить начальные нули из числа.                   
         і
і  ;                                                             
         і
і  ;                                                             
         і
і  skip_leading_zeroes:                                          
         і
і         mov   ah,bcd_byte[esi]    ; Взять байт BCD             
         і
і         mov   al,ah               ; Скопировать значение       
         і
і         shr   al,cl               ; Взять верхнюю по порядку ци
фру      і
і         and   al,0fh              ; Установить флаг нуля       
         і
і         jnz   enter_odd           ; Выйти из цикла, если в нача
ле       і
і                                   ; обнаружены не нули         
         і
і                                                                
         і
і         mov   al,ah               ; Взять снова байт BCD       
         і
і         and   al,0fh              ; Взять нижнюю по порядку циф
ру       і
і         jnz   enter-even          ; Выйти из цикла, если обнару
жены     і
і                                   ; ненулевые цифры            
         і
і                                                                
         і
і         dec   esi                 ; Уменьшить индекс BCD       
         і
і         jns   skip_leading_zeroes                              
         і
і  ;                                                             
         і
і  ;               Вся мантисса состоит из нулей                 
         і
і  ;                                                             
         і
і         mov   al,'0'              ; Установить начальный ноль  
         і
і         stosb                                                  
         і
і         inc   ebx                 ; Увеличить длину строки     
         і
і         jmp   short exit_with_value                            
         і
і  ;                                                             
         і
і  ;               Теперь расширяем строку цифрами от            
         і
і  ;               0 до 9 по одной цифре на байт                 
         і
і  ;                                                             
         і
і  digit_loop:                                                   
         і
і         mov   ah,bcd_byte[esi]    ; Взять байт BCD             
         і
і         mov   al,ah                                            
         і
і         shr   al,cl               ; Взять верхнюю по порядку ци
фру      і
і  enter_odd:                                                    
         і
і         add   al,'0'              ; Перевести в ASCII          
         і
і         stosb                     ; Занести цифру в строку ASCI
I        і
і         mov   al,ah               ; Взять нижнюю по порядку циф
ру       і
і         and   al,0fh                                           
         і
і         inc   ebx                 ; Увеличить счетчик размера п
оля      і
і  enter-even:                                                   
         і
і         add   al,'0'              ; Перевести в ASCII          
         і
і         stosb                     ; Занести цифру в строку ASCI
I        і
і         inc   ebx                 ; Увеличить счетчик размера п
оля      і
і         dec   esi                 ; Перейти к следующему байту 
BCD      і
і         jns   digit_loop                                       
         і
і  ;                                                             
         і
і  ;           Перевод закончен. Установить размер               
         і
і  ;           строки и остаток                                  
         і
і  ;                                                             
         і
і  exit_with_value:                                              
         і
і         mov   edi,size_ptr                                     
         і
і         mov   word ptr [edi],bx                                
         і
і         mov   eax,edx             ; Установить выдаваемое значе
ние      і
і         jmp   exit_proc                                        
         і
і                                                                
         і
і  floating_to_ascii       endp                                  
         і
і                          code          ends                    
         і
і                                        end                     
         і
і                                                                
         і
і                                                                
         і
і  +1 $title(Вычислить значение 10**AX)                          
         і
і                                                                
         і
і  ; Эта подпрограмма вычисляет значение степени                 
         і
і  ; 10**EAX. Точный результат выдается для значений в           
         і
і  ; диапазоне 0 <= EAX < 19. Все регистры прозрачны             
         і
і  ; и значение выдается в TOS как два числа: порядок            
         і
і  ; в ST(1) и дробь в ST(0). Величина порядка может             
         і
і  ; превышать наибольший порядок числа в расширенном            
         і
і  ; вещественном формате. В программе используются              
         і
і  ; три стековых регистра.                                      
         і
і  ;                                                             
         і
і         name      get_power_10                                 
         і
і         public    get_power_10, power_table                    
         і
і                                                                
         і
і  stack  stackseg  8                                            
         і
і                                                                
         і
і  code  segment public er                                       
         і
і  ;                                                             
         і
і  ;          Использовать точные значения от 1.0 до 1E18.       
         і
і  ;                                                             
         і
і         even               ; Оптимизировать 16-ти битовый досту
п        і
і  power_table   dq  1.0,1e1,1e2,1e3                             
         і
і                                                                
         і
і                dq  1e4,1e5,1e6,1e7                             
         і
і                                                                
         і
і                dq  1e8,1e9,1e10,1e11                           
         і
і                                                                
         і
і                dq  1e12,1e13,1e14,1e15                         
         і
і                                                                
         і
і                dq  1e16,1e17,1e18                              
         і
і                                                                
         і
і  get_power_10  proc                                            
         і
і         cmp   eax,18      ; Проверить диапазон 0 <= AX < 19    
         і
і         ja    out_of_range                                     
         і
і                                                                
         і
і         fld   power_table[eax*8]  ; Взять точное значение      
         і
і         fxtract           ; Отделить степень и                 
         і
і                           ; дробную часть                      
         і
і         ret               ; Оставить fxtract                   
         і
і  ;                                                             
         і
і  ; Вычислить значение, используя команду возведения            
         і
і  ; в степень. Используются следующие соотношения:              
         і
і  ;                                                             
         і
і  ; 10**X = 2**(log2(10)*X)                                     
         і
і  ; 2**(I+F) = 2**I * 2**F                                      
         і
і  ;                                                             
         і
і  ; Если ST(1) = I и ST(0) = 2**F, то команда fscale            
         і
і  ; выдает 2**(I+F).                                            
         і
і  ;                                                             
         і
і  out_of_range:                                                 
         і
і                                                                
         і
і         fldl2t                   ; TOS = LOG2(10)              
         і
і         enter 4,0                                              
         і
і                                                                
         і
і  ;            Сохранить значение степени десяти, P             
         і
і  ;                                                             
         і
і         mov   [ebp-4],eax                                      
         і
і                                                                
         і
і  ;            TOS, X = LOG2(10)*P = LOG2(10**P)                
         і
і  ;                                                             
         і
і         fimul dword ptr [ebp-4]                                
         і
і         fld1                     ; Установить TOS = -1.0       
         і
і         fchs                                                   
         і
і         fld   st(1)              ; Скопировать значение степени
 по      і
і                                  ; основанию два               
         і
і         frndint                  ; TOS = I; -бесконечность < I 
<= X,    і
і                                  ; где I - целое               
         і
і                                  ; Режим округления не имеет зн
ачения   і
і         fxch  st(2)              ; TOS = X, ST(1) = -1.0       
         і
і                                  ; ST(2) = I                   
         і
і         fsub  st,st(2)           ; TOS, F = X-I:               
         і
і                                  ; -1.0 < TOS <= 1.0           
         і
і                                                                
         і
і  ;      Восстановить начальный режим управления точностью      
         і
і         pop   eax                                              
         і
і         f2xm1            ; TOS = 2**(F) - 1.0                  
         і
і         leave            ; Восстановить стек                   
         і
і         fsubr            ; Подготовить 2**(F)                  
         і
і         ret              ; Оставить fsubr                      
         і
і                                                                
         і
і  get_power_10   endp                                           
         і
і                                                                
         і
і  code           ends                                           
         і
і                 end                                            
         і
і                                                                
         і
і  +1 $title(Определение содержимого регистра TOS)               
         і
і  ;                                                             
         і
і  ; Эта подпрограмма выдает значение от 0 до 15 в               
         і
і  ; регистр EAX в соответствии с содержанием вершины            
         і
і  ; стека FPU. Все регистры прозрачны, поэтому ошибки           
         і
і  ; исключены. Выдаваемое значение соответствует                
         і
і  ; битам C3, C2, C1 и C0 команды FXAM.                         
         і
і  ;                                                             
         і
і         name           tos_status                              
         і
і         public         tos_status                              
         і
і                                                                
         і
і  stack        stackseg   6                                     
         і
і                                                                
         і
і  code         segment public er                                
         і
і                                                                
         і
і  tos_status  proc                                              
         і
і         fxam             ; Взять состояние регистра TOS        
         і
і         fstsw ax         ; Взять текущее состояние             
         і
і         mov   al,ah      ; Положить биты 10-8 в биты 2-0       
         і
і         and   eax,4007h  ; Маскировать биты C3, C2, C1 и C0    
         і
і         shr   ah,3       ; Положить бит C3 в бит 11            
         і
і         or    al,ah      ; Положить бит C3 в бит 3             
         і
і         mov   ah,0       ; Очистить возвращаемое значение      
         і
і         ret                                                    
         і
і                                                                
         і
і  tos_status endp                                               
         і
і                                                                
         і
і  code       ends                                               
         і
і             end                                                
         і
і                                                                
         і
і                                                                
         і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
ДДДДДДДДДЩ
20.3.3 Специальные команды
----------------------------------------------------------------
Функции оперируют различными числовыми командами, типами
данных и управляют точностью. Показанные команды являются
командами для автоматического перевода чисел в BCD вид (двоично-
десятичный код). При этом вычисляется 10-ти кратное значение
целочисленной величины, операции выполняются одновременно,
устанавливается синхронизация данных и используется
ориентированное округление в модуле обработки операций с
плавающей точкой.
Без типа данных расширенной точности и встроенной
экспоненциальной функции, не может быть достигнута двойная
точность этой функции при скорости и размере приведенного
примера.
Функция базируется на числовом типе данных BCD для перевода из
двоичного с плавающей точкой в десятичное. Основная работа
заключается в масштабировании значения с плавающей точкой до
приемлемого ограниченного диапазона значений типа BCD. Для то,
чтобы напечатать результат из 9-ти цифр, необходимо точно
провести масштабирование данной величины в целое между 10**(8) и
10**(9). Например, число +0.123456789 требует коэффициента
масштабирования порядка 10**(9) для того, чтобы получить число
+123456789.0, которое может храниться в 9-ти цифрах типа BCD.
Коэффициент масштабирования должен быть точной степенью 10-ти
для того, чтобы предотвратить изменения цифр при печати.
Эти подпрограммы должны правильно переводить все значения, точно
представимые в десятичном виде, в поле, заданного размера.
Целочисленные значения, которые подходят под данный размер
строки, нет необходимости масштабировать, а можно сразу
сохранять в форме BCD. Нецелочисленные значения, точно
представимые в десятичном виде в пределах размера строки, также
переводятся точно. Например, число 0.125 точно представимо как в
двоичном виде, так и в десятичном. Для того, чтобы перевести это
значение с плавающей точкой в десятичный вид, надо выбрать
коэффициент масштабирования 1000. В результате получим число
125. При масштабировании величин, функция должна оставлять след
там, где будет располагаться десятичная точка в конечном
десятичном значении.
20.3.4 Описание операции
----------------------------------------------------------------
Перевод числа с плавающей точкой в десятичный ASCII вид
проводится в три основных шага: определение величины числа,
масштабирование его для представления в виде BCD и перевод
данных типа BCD в десятичную символьную ASCII запись.
Определение величины результата требует нахождения такого
значения числа X, при котором наше число было бы представимо в
виде I x 10**(X), где 1.0 у I < 10.0. Масштабирование числа
требует его умножения на коэффициент масштабирования 10**(S)
так, чтобы результат был целым, не содержащим десятичных цифр
больше, чем отведено для этого места в строке символов ASCII.
После масштабирования, округления и перевода в тип BCD число
представляется в форме, удобной для перевода в десятичный ASCII
вид любым программным обеспечением.
Реализация всех этих трех шагов требует предельного внимания к
деталям. Для начала обратите внимание, что не каждая величина с
плавающей точкой имеет числовое значение. Подпрограммой перевода
могут быть встречены и такие величины как бесконечность,
неопределенность или NaN. Поэтому эта подпрограмма должна уметь
опознавать эти величины и однозначно определять их.
Также присутствуют особые случаи числовых значений. Ненормальные
величины имеют числовое значение и должны быть отличимы
функцией, так как они указывают на то, что была потеряна
точность в процессе проведения предшествующих вычислений.
Определив, что число имеет числовое значение и нормализовано
(при необходимости установка соответствующих флагов
сигнализирует вызванной программе, что это ненормальное число),
можно масштабировать величину в диапазоне типа BCD.
20.3.5 Масштабирование величин
----------------------------------------------------------------
Для того, чтобы масштабировать число, необходимо определить его
величину. Достаточно вычислить величину с точностью до 1 единицы
или при кратном 10 коэффициенте, умноженным на требуемую
величину. После масштабирования числа проводится проверка для
того, чтобы посмотреть попал ли результат в ожидаемый диапазон.
Если нет, то результат может быть исправлен на один десятичный
порядок вверх или вниз от требуемой величины. Подстроечный тест
после масштабирования необходим для устранения неизбежной
неточности в масштабированном значении.
Так как оценка значения для коэффициента масштабирования должна
быть достаточно близкой, то используется быстрый прием
масштабирования. Величина оценивается умножением степени 2,
которая представляет из себя несмещенный порядок числа с
плавающей точкой, соответственно с числом log(10) от 2.
Округление результата до целого дает оценку достаточной
точности. Игнорируя дроби, значение в результате может иметь
максимальную ошибку 0.32.
Используя значение величины и размер числовой строки,
можно вычислить коэффициент масштабирования. В процедуре
перевода числа вычисление коэффициента масштабирования является
самой неточной операцией. Для этого используется соотношение
10**(X) = 2**(X * log(10) 2) и команда F2XM1.
В соответствии с ограничениями на диапазон значений, разрешенных
командой F2XM1, значение степени 2 разбивается на целую и
дробную компоненты. Соотношение 2**(I + F) = 2**(I) x 2**(F)
позволяет использовать инструкцию FSCALE для того, чтобы
перекомбинировать значение 2**(F), вычисленное командой F2XM1, и
часть 2**(I).
20.3.5.1 Неточность при масштабировании
----------------------------------------------------------------
При вычислении коэффициента масштабирования возникает неточность
из-за остаточных нулей, помещенных в дробной величине степени 2
при отделении значимых битов целой части. Для каждого целого
значимого бита в значении степени 2, отделенных от дробных
битов, в дробной части теряется один бит точности при заполнении
нулями менее значащих битов.
До 14 битов может быть потеряно в дробной части, так как
наибольшая допустимая величина порядка числа с плавающей точкой
составляет 2**(14) - 1. Эти биты непосредственно и уменьшают
точность вычисления коэффициента масштабирования, чем и вызывают
уменьшение точности самого значения числа. Для чисел в диапазоне
10**(ё30) максимальное число битов точности, потерянных при
выполнении масштабирования, достигает 8-ми.
20.3.5.2 Предупреждение переполнений и отрицательных переполнений
----------------------------------------------------------------
Дробная часть числа и показатель отделены друг от друга для
того, чтобы избежать переполнений и отрицательных переполнений
при вычислении масшабированной величины. Например, для того,
чтобы произвести масштабирование числа 10**(-4932) до 10**(8),
требуется коэффициент масштабирования 10**(4950), который не
может быть представлен процессором i486.
Отделив показатель и дробь, операция масштабирования будет
просто складывать показатели отдельно от умножения дробей. Эта
арифметическая операция включает малые целые числа, легко
представимые процессором i486.
20.3.5.3 Окончательная регулировка
----------------------------------------------------------------
Возможно, что степенная функция Get Power 10 может выдать
результат масштабирования величины так, что получится число,
имеющее больше разрядов, чем может быть представлено в строке
ASCII символов. Например, масштабирование числа
9.9999999999999999 x 10**(4900) с коэффициентом
1.00000000000000010 x 10**(-4883) дало бы в результате число
1.00000000000000009 x 10**(18). И коэффициент масштабирования
соответствует точности модуля обработки операций с плавающей
точкой и результат соответствует точности перевода, но число не
может быть представлено в формате BCD. Вот почему необходо после
масштабирования провести тестирование величины результата.
Результат можно умножить или разделить на 10, в зависимости от
того, является ли результат соответственно слишком малым или
слишком большим.
20.3.6 Выходной формат
----------------------------------------------------------------
Для максимальной гибкости выходного формата, позиция десятичной
точки указывается двоичным целым числом, называемым значением
показателя степени. Если показатель степени равен нулю, то
десятичная точка предполагается справа от самой правой цифры.
Показатель степени больший нуля показывает, как много остаточных
нулей не показывается. Для каждой единице ниже нуля десятичная
точка в строке сдвигается налево.
Последний шаг перевода числа - это сохранить результат в виде
BCD и указать, где находится десятичная точка. Строка типа BCD
затем распаковывается в десятичную строку из ASCII символов.
Знак устанавливается в соответствии со знаком оригинального
значения.
20.4 Примеры тригонометрических вычислений
----------------------------------------------------------------
В этом примере моделируется кинематика руки робота по гомогенным
матрицам преобразований размером 4 x 4, предложенным Денавитом и
Хартенбергом (1,2). Передаточные и поворотные отношения между
смежными звеньями руки описаны с помощью этих матриц, используя
матричный метод D-H. Для каждого звена построена гомогенная
матрица преобразований размером 4 x 4, которая представляет
координатную систему звена (L(i)) в соединении (J(i)) в
соответствии с предыдущей координатной системой (J(i-1),L(i-1)).
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
1. J.Denavit and R.S.Hartenberg, "A Kinematic Notation for
   Lower-Pair Mechanisms Based on Matrices."
   J. Applied Mechanics, June 1955, pp. 215-221
2. C.S.George Lee, "Robert Arm Kinematics, Dinamics, and
   Control." IEEE Computer, Dec. 1982.
Следующие четыре геометрические величины полностью описывают
движение любой пары соединение/звено (J(i),L(i)), как показано
на Рисунке 20-7.
тета(i)   = Угол между осью x(i) и осью x(i-1) при повороте
            вокруг оси z(i-1) против часовой стрелки.
d(i)      = Расстояние от (i-1)-ой координатной системы вдоль
            оси z(i-1) до оси x(i).
a(i)      = Расстояние до i-ой координатной системы от оси
            z(i-1) вдоль оси -x(i).
альфа(i)  = Угол между осью z(i) и осью z(i-1) при повороте
            вокруг оси x(i) против часовой стрелки.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і                                                               і
і                                                               і
і                            zi-1          xi-1                 і
і                yi-1        і      90   /                      і
і                  \         ГДДДДДД\  /                        і
і                    \       і       /\     тета                і
і                      \     і     /    \                       і
і                        \   і   /       *                      і
і                          \ і /                                і
і             СОЕДИНЕНИЕ i-1 Г Д Д Д Д Д Д Д Д Д Д      ДДВДД   і
і                            і                            і     і
і                            і                            і     і
і                            і                            і     і
і                 альфаi     і                            і     і
і          \                 і                            і     і
і            \               і                                  і
і              \       * <ДДДіДДД\                        di    і
і                \           і     \ 90                         і
і  zi              \         і      і                     і     і
і   \             yi \       і      і                     і     і
і     \           /    \     і      і                     і     і
і       \       /        \   і      і                     і     і
і         \   /            \ і      і                     і     і
і           ДДДДДДДДДДДДДДДДДБДДДДДДБДДДДДДДДДДДДД xi   ДДБДД   і
і  СОЕДИНЕНИЕi+1                                                і
і                                                               і
і           ГДДДДДД ai ДДДДДДґ                                  і
і                                                    240486/103 і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
        Рисунок 20-7. Отношения Между Смежными Звеньями
Матрица преобразований A(i)(i-1) типа D-H для смежных
координатных участков от соединения (i-1) до соединения (i)
вычисляется как:
A(i)(i-1) = T(z,d) x T(z,тета) x T(x,a) x T(x,альфа)
где:
T(z,d)        представляет передачу вдоль оси z(i-1)
T(z,тета)     представляет поворот угла тета вокруг оси z(i-1)
T(x,a)        представляет передачу вдоль оси x(i)
T(x,альфа)    представляет поворот угла альфа вокруг оси x(i)
A(i)(i-1) =
і COSтет(i) -COSалф(i)SINтет(i)  SINалф(i)SINтет(i) COSтет(i) і
і                                                             і
і SINтет(i)  COSалф(i)COSтет(i) -SINалф(i)COSтет(i) SINтет(i) і
і                                                             і
і 0          SINалф(i)           COSалф(i)          d(i)      і
і                                                             і
і 0          0                   0                  1         і
Композитная гомогенная матрица T, которая представляет собой
позицию и ориентацию пары соединение/звено по отношению к
основной системе, получается последовательным умножением матриц
преобразований типа D-H для смежных координатных участков.
T(i)(0) = A(1)(0) x A(2)(1) x ... x A(i)(i-1)
Пример программы на Рисунке 20-8 иллюстрирует, как можно
выполнить передаточный процесс, используя возможности обработки
операций с плавающей точкой процессора i486. Программа состоит
из двух главных процедур. Первая процедура TRANS PROC
используется для вычисления элементов в каждой матрице A(i)(i-1)
типа D-H. Вторая процедура MATRIXMUL PROC находит произведение
двух последовательных матриц типа D-H.
          Рисунок 20-8. Пример Кинематики Руки Робота
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
ДДДДДДДДДї
і                                                                
         і
і                                                                
         і
і    Name ROT_MATRIX_CAL                                         
         і
і    ;                                                           
         і
і    ; Этот пример иллюстрирует использование команд с           
         і
і    ; плавающей точкой процессора i486 и частично               
         і
і    ; функцию FSINCOS, которая дает значения синуса и           
         і
і    ; косинуса одновременно. Программа вычисляет                
         і
і    ; композитные матрицы, по которым работает механизм         
         і
і    ; передачи.                                                 
         і
і    ;                                                           
         і
і    ; В этом примере рассмотрена только кинематика              
         і
і    ; робота.                                                   
         і
і    ;                                                           
         і
і    ; Если композитная матрица, упомянутая выше,                
         і
і    ; задается как:                                             
         і
і    ; T1n = A1 x A2 x ... x An, то                              
         і
і    ; T1n находится вызовом процедур trans_proc и               
         і
і    ; matrixmul_proc, пока все матрицы не будут                 
         і
і    ; перемножены.                                              
         і
і    ;                                                           
         і
і    ; Процедура trans_proc вычисляет элементы каждой            
         і
і    ; матрицы A(A1,.....An), пока процедура                     
         і
і    ; matrixmul_proc выполняет умножение матрицы Ai и           
         і
і    ; Ai+1. Для умножения процедура matrixmul_proc              
         і
і    ; использует процедуры matrix_row и matrix_elem.            
         і
і    ;                                                           
         і
і    ; Определить стековое пространство                          
         і
і    ;                                                           
         і
і    trans_stack stackseg 400                                    
         і
і                                                                
         і
і    ; Определение матричной структуры для передаточных          
         і
і    ; матриц размером 4x4                                       
         і
і                                                                
         і
і    a_matrix struc                                              
         і
і           a11   dq   ?                                         
         і
і           a12   dq   ?                                         
         і
і           a13   dq   ?                                         
         і
і           a14   dq   ?                                         
         і
і           a21   dq   ?                                         
         і
і           a22   dq   ?                                         
         і
і           a23   dq   ?                                         
         і
і           a24   dq   ?                                         
         і
і           a31   dq   0h                                        
         і
і           a32   dq   ?                                         
         і
і           a33   dq   ?                                         
         і
і           a34   dq   ?                                         
         і
і           a41   dq   0h                                        
         і
і           a42   dq   0h                                        
         і
і           a43   dq   0h                                        
         і
і           a44   dq   1h                                        
         і
і    a_matrix ends                                               
         і
і    ;                                                           
         і
і    ; Предполагаем, что одно соединение хранится в              
         і
і    ; одой позиции и, следовательно, имеет два набора           
         і
і    ; параметров. Однако, возможно и большее количество         
         і
і    ; соединений.                                               
         і
і    ;                                                           
         і
і    alp_deg struc                                               
         і
і           alpha_deg1 dd ?                                      
         і
і           alpha_deg2 dd ?                                      
         і
і    alp_deg ends                                                
         і
і    ;                                                           
         і
і    tht_deg struc                                               
         і
і           theta_deg1 dd ?                                      
         і
і           theta_deg2 dd ?                                      
         і
і    tht_deg ends                                                
         і
і    ;                                                           
         і
і    A_array struc                                               
         і
і           A1         dq ?                                      
         і
і           A2         dq ?                                      
         і
і    A_array ends                                                
         і
і    ;                                                           
         і
і    D_array struc                                               
         і
і           D1         dq ?                                      
         і
і           D2         dq ?                                      
         і
і    D_array ends                                                
         і
і    ;                                                           
         і
і    ; Сегмент данных - trans_data                               
         і
і    ;                                                           
         і
і    trans_data             segment rw   public                  
         і
і           Amx             a_matrix<>                           
         і
і           Bmx             a_matrix<>                           
         і
і           Tmx             a_matrix<>                           
         і
і           ALPHA_DEG       alp_deg<>                            
         і
і           THETA_DEG       tht_deg<>                            
         і
і           A_VECTOR        A_array<>                            
         і
і           D_VECYOR        D_array<>                            
         і
і           ZERO            dd           0                       
         і
і           d180            dd           180                     
         і
і           NUM_JOINT       equ          1                       
         і
і           NUM_ROW         equ          4                       
         і
і           NUM_COL         equ          4                       
         і
і           REVERSE         DB           1H                      
         і
і    trans_data             ends                                 
         і
і                                                                
         і
і    assume  ds:trans_data, es:trans_data                        
         і
і    ;                                                           
         і
і    ; Сегмент trans_code содержит процедуры для                 
         і
і    ; вычисления элементов матрицы и умножения матриц.          
         і
і    ;                                                           
         і
і    trans_code      segment er    public                        
         і
і    truns_proc      proc    far                                 
         і
і    ;                                                           
         і
і    ; Вычислить углы альфа и тета в радианах из их значений     
         і
і    ; в градусах                                                
         і
і    ;                                                           
         і
і           fldpi                                                
         і
і           fdiv  d180                                           
         і
і    ;                                                           
         і
і    ; Умножить на Пи/180                                        
         і
і    ;                                                           
         і
і           fld   st                                             
         і
і           fmul  qword ptr ALPHA_DEG[ecx*8]                     
         і
і           fxch  st(1)                                          
         і
і           fmul  qword ptr THETA_DEG[ecx*8]                     
         і
і    ;                                                           
         і
і    ; Тета (в радианах) в ST                                    
         і
і    ; и альфа (в радианах) в ST(1)                              
         і
і    ;                                                           
         і
і    ; Вычисление элементов матрицы                              
         і
і    ;                                                           
         і
і    ;            a11 = COS тета                                 
         і
і    ;            a12 = -COS альфа* SIN тета                     
         і
і    ;            a13 = SIN альфа * SIN тета                     
         і
і    ;            a14 = A * COS тета                             
         і
і    ;            a21 = SIN тета                                 
         і
і    ;            a22 = COS альфа * COS тета                     
         і
і    ;            a23 = -SIN альфа * COS тета                    
         і
і    ;            a24 = A * SIN тета                             
         і
і    ;            a32 = SIN альфа                                
         і
і    ;            a33 = COS альфа                                
         і
і    ;            a34 = D                                        
         і
і    ;            a31 = a41= a42 = a43 = 0.0                     
         і
і    ;            a44 = 1                                        
         і
і    ;                                                           
         і
і    ; Регистр EBX содержит смещение для матрицы                 
         і
і    ;                                                           
         і
і           fsincos                       ; COS тета в ST        
         і
і                                         ; SIN тета в ST(1)     
         і
і           fld   st                      ; удвоить COS тета     
         і
і           fst   [ebx].a11               ; COS тета в a11       
         і
і           fmul  qword ptr A_VECTOR[ecx*8]                      
         і
і           fstp  [ebx].a14               ; A * COS тета в a14   
         і
і           fxch  st(1)                   ; SIN тета в ST        
         і
і           fst   [ebx].a21               ; SIN тета в a21       
         і
і           fld   st                      ; удвоить SIN тета     
         і
і           fmul  qword ptr A_VECTOR[ecx*8]                      
         і
і           fstp  [ebx].a24               ; A * SIN тета в a24   
         і
і           fld   st(2)                   ; альфа в ST           
         і
і           fsincos                       ; COS альфа в ST       
         і
і    ;                SIN альфа в ST(1)                          
         і
і    ;                SIN тета в ST(2)                           
         і
і    ;                COS тета в ST(3)                           
         і
і           fst   [ebx].a33               ; COS альфа в a33      
         і
і           fxch  st(1)                   ; SIN альфа в ST       
         і
і           fst   [ebx].a32               ; SIN альфа в a32      
         і
і           fld   st(2)                   ; SIN тета в ST        
         і
і                                         ; SIN альфа в ST(1)    
         і
і           fmul  st,st(1)                ; SIN альфа * SIN тета 
         і
і           fchs  st(2)                   ; -COS тета * SIN альфа
         і
і           fstp  [ebx].a23               ; хранится в a23       
         і
і           fld   st(2)                   ; COS тета в ST        
         і
і                                         ; COS альфа в ST(1)    
         і
і                                         ; SIN тета в ST(2)     
         і
і                                         ; COS тета в ST(3)     
         і
і           fmul  st,st(1)                ; COS тета * COS альфа 
         і
і           fstp  [ebx].a22               ; хранится в A22       
         і
і           fmul  st,st(1)                ; COS альфа * SIN тета 
         і
і    ;                                                           
         і
і    ;                Для того, чтобы получить преимущества      
         і
і    ;                параллельной работы IU и FPU               
         і
і    ;                                                           
         і
і           push  eax                     ; сохранить EAX        
         і
і    ;                                                           
         і
і    ;                Для ускорения поместить D в A34            
         і
і    ;                                                           
         і
і           mov   eax, dword ptr D_VECTOR[ecx*8]                 
         і
і           mov   dword ptr [ebx + 88],eax                       
         і
і           mov   eax, dword ptr D_VECTOR[ecx*8 + 4]             
         і
і           mov   dword ptr [ebx + 92],eax                       
         і
і           pop   eax                     ; восстановить EAX     
         і
і           fchs                          ; COS альфа * SIN тета 
         і
і           fstp  [ebx].a12               ; хранится в a12       
         і
і                                         ; Вычисляются все      
         і
і                                         ; ненулевые элементы   
         і
і                                                                
         і
і           ret                                                  
         і
і                                                                
         і
і    trans_proc endp                                             
         і
і                                                                
         і
і    matrix_elem proc far                                        
         і
і    ;                                                           
         і
і    ;    Эта процедура вычисляет произведение i-той строки      
         і
і    ;    первой матрицы и j-того столбца второй матрицы:        
         і
і    ;                                                           
         і
і    ;       Tij, где Tij = сумме произведений Aik x Bkj по k    
         і
і    ;                                                           
         і
і    ;    Параметры, передаваемые из вызвавшей процедуры         
         і
і    ;    matrix_row:                                            
         і
і    ;                                                           
         і
і    ;                    ESI = (i-1)*8                          
         і
і    ;                    EDI = (j-1)*8                          
         і
і    ;                                                           
         і
і    ; локальный регистр, EBP = (k-1)*8                          
         і
і    ;                                                           
         і
і           push  ebp             ; сохранить EBP                
         і
і           push  ecx             ; для того, чтобы использовать 
         і
і                                 ; ESX как временный регистр    
         і
і           mov   ecx,esi         ; сохранить для дальнейшего    
         і
і                                 ;   индексирования             
         і
і    ;                                                           
         і
і    ; Взять элемент в первой матрице, A                         
         і
і    ;                                                           
         і
і           imul  ecx,NUM_COL     ; ECX содержит смещение предыду
щих      і
і                                 ; строк; смещение берется от на
чала     і
і                                 ; матрицы                      
         і
і                                                                
         і
і           xor   ebp,ebp         ; Очистить EBP, который будет н
ужен     і
і                                 ; как временный регистр для инд
екса     і
і                                 ; (k) по i-той строке первой   
         і
і                                 ; матрицы и вниз по j-тому стол
бцу      і
і                                 ; второй матрицы.              
         і
і    ;                                                           
         і
і    ;         Очистить Tij для накапливания пар Aik*Bkj         
         і
і    ;                                                           
         і
і           mov   dword ptr [edx][edi],ebp                       
         і
і           mov   dword ptr [edx][edi+4],ebp                     
         і
і                                                                
         і
і           push  ecx             ; сохранить в стеке: ESI * num_
col      і
і                                 ; = смещению начала i-той строк
и от     і
і                                 ; начала матрицы A             
         і
і                                                                
         і
і           add   ecx,ebp         ; взять k-тый элемент i-той стр
оки      і
і                                 ; матрицы A                    
         і
і    ;                                                           
         і
і    ;      Загрузить Aik в FPU                                  
         і
і    ;                                                           
         і
і           fld   qword ptr [eax][ecx]                           
         і
і    ;                                                           
         і
і    ;      Взять Bkj                                            
         і
і    ;                                                           
         і
і           mov ecx,ebp                                          
         і
і           imul  ecx,NUM_ROW     ; ECX содержит смещение начала 
         і
і                                 ; k-той строки от начала матриц
ы B      і
і           add   ecx,edi         ; Взять j-тый элемент k-той стр
оки      і
і                                 ; матрицы B                    
         і
і           fmul  qword ptr [ebx][ecx]        ; Aik * Bkj        
         і
і           pop   ecx             ; ESI * num_col                
         і
і                                 ; в ECX снова                  
         і
і           pop   ecx             ; также в вершине программного 
         і
і                                 ; стека                        
         і
і    ;                                                           
         і
і    ;      Добавить к результату в выходной матрице, Tij        
         і
і    ;                                                           
         і
і           add   ecx,edi                                        
         і
і    ;                                                           
         і
і    ;      накопленные суммы пар Aik * Bkj                      
         і
і    ;                                                           
         і
і           fadd  qword ptr [edx][ecx]                           
         і
і           fstp  qword ptr [edx][ecx]                           
         і
і    ;                                                           
         і
і    ;      увеличить на единицу k, то есть                      
         і
і    ;      EBP на 8                                             
         і
і    ;                                                           
         і
і           add ebp,8                                            
         і
і    ;                                                           
         і
і    ; Достигло ли k ширины матрицы?                             
         і
і    ;                                                           
         і
і           cmp   ebp, NUM_COL*8                                 
         і
і           jl    NXT_k                                          
         і
і    ;                                                           
         і
і    ; Восстановить регистры                                     
         і
і    ;                                                           
         і
і           pop   ecx              ; удалить из стека ESI * num_c
ol       і
і           pop   ecx              ; восстановить ECX            
         і
і           pop   ebp              ; восстановить EBP            
         і
і                                                                
         і
і    matrix_elem  endp                                           
         і
і                                                                
         і
і    matrix_row       proc     far                               
         і
і           xor       edi,edi                                    
         і
і    ;                                                           
         і
і    ;      Пройти по строке                                     
         і
і    ;                                                           
         і
і    NXT_COL:                                                    
         і
і           call  matrix_ekem                                    
         і
і           add   edi,8                                          
         і
і           cmp   edi, NUM_COL*8                                 
         і
і           jl    NXT_COL                                        
         і
і           ret                                                  
         і
і                                                                
         і
і    matrix_row endp                                             
         і
і                                                                
         і
і    matrixmul_proc proc far                                     
         і
і    ;                                                           
         і
і    ; Эта процедура перемножает матрицы, используя              
         і
і    ; процедуру matrix_row для вычисления элементов             
         і
і    ; каждой строки.                                            
         і
і    ;                                                           
         і
і    ; Умножение матриц выполняется как:                         
         і
і    ;                                                           
         і
і    ; Tij = Aik x Bkj,                                          
         і
і    ;                                                           
         і
і    ; где i и j указывают на номера строки и столбца            
         і
і    ; соответственно и k - индекс для прохода по i-той          
         і
і    ; строке первой матрицы и по j-тому столбцу второй          
         і
і    ; матрицы.                                                  
         і
і    ;                                                           
         і
і           mov   ebp,esp                ; использует базовый ука
затель   і
і                                        ; для индексирования    
         і
і           mov   edx,dword ptr[ebp+4]   ; смещение Tmx в EDX    
         і
і           mov   ebx,dword ptr[ebp+8]   ; смещение Bmx в EBX    
         і
і           mov   eax, dword ptr[ebp+12] ; смещение Amx в EAX    
         і
і    ;                                                           
         і
і    ;              Установка ESI и EDI                          
         і
і    ;              EDI указывает столбец                        
         і
і    ;              ESI указывает строку                         
         і
і    ;                                                           
         і
і           xor   esi,esi                ; очистить ESI          
         і
і                                                                
         і
і    NXT_ROW:                                                    
         і
і           call  matrix_row                                     
         і
і                                                                
         і
і           add   esi,8                                          
         і
і           cmp   esi,NUM_ROW*8                                  
         і
і           jl    NXT_ROW                                        
         і
і           ret   12                ; вытолкнуть указатели матриц
ы        і
і                                                                
         і
і    matrix_proc  endp                                           
         і
і                                                                
         і
і    trans_code   ends                                           
         і
і                                                                
         і
і    ; ********************************************** ;          
         і
і    ;                                                ;          
         і
і    ;                                                ;          
         і
і    ;                                                ;          
         і
і    ;            Главная   программа                 ;          
         і
і    ;                                                ;          
         і
і    ;                                                ;          
         і
і    ;                                                ;          
         і
і    ;                                                ;          
         і
і    ; ********************************************** ;          
         і
і                                                                
         і
і    main_code   segment  er                                     
         і
і                                                                
         і
і    START:                                                      
         і
і                                                                
         і
і           mov   esp, stackstart trans_stack                    
         і
і    ;                                                           
         і
і    ;            Сохранить все регистры                         
         і
і    ;                                                           
         і
і           pushad                                               
         і
і    ;                                                           
         і
і    ; ECX указывает число соединений, где число матриц          
         і
і    ; равно NUM_JOINT + 1.                                      
         і
і    ; Найдем первую матрицу (от основания системы до            
         і
і    ; первого соединения) и назовем ее Bmx.                     
         і
і    ;                                                           
         і
і           xor   ecx,ecx                   ; первой матрицей    
         і
і           mov   ebx,offset Bmx            ;                    
         і
і           call  trans_proc                ; является Bmx       
         і
і           inc   ecx                                            
         і
і                                                                
         і
і    NXT_MATRIX:                                                 
         і
і    ;                                                           
         і
і    ; Вторая матрица и выше будут сохраняться в Amx.            
         і
і    ; Результат умножения матриц будет сохраняться в            
         і
і    ; Tmx, но для следующего умножения будет доступен           
         і
і    ; через Bmx. Такое дублирование необходимо для              
         і
і    ; последовательного умножения матриц. Это                   
         і
і    ; достигается заменой поррядка следования указателей        
         і
і    ; на Bmx и Tmx в программном стеке, хотя и является         
         і
і    ; невидимым для процедуры перемножения матриц.              
         і
і    ; REVERSE используется как индикатор;                       
         і
і    ; если REVERSE = 0, то это значит, что результат            
         і
і    ; помещен в Tmx.                                            
         і
і    ;                                                           
         і
і           mov   ebx,offset Amx                 ; найти Amx     
         і
і           call  trans_proc                                     
         і
і           inc   ecx                                            
         і
і           xor   REVERSE,1h                                     
         і
і           jnz   Bmx_as_Tmx                                     
         і
і    ;                                                           
         і
і    ; Не менять. Bmx используется как вторая входная            
         і
і    ; матрица, а Tmx - как выходная матрица.                    
         і
і    ;                                                           
         і
і           push  offset Amx                                     
         і
і           push  offset Bmx                                     
         і
і           push  offset Tmx                                     
         і
і           jmp   CONTINUE                                       
         і
і    ;                                                           
         і
і    ; Поменять. Tmx используется как вторая входная             
         і
і    ; матрица, а Bmx - как выходная матрица.                    
         і
і    ;                                                           
         і
і    Bmx_as_Tmx:                                                 
         і
і                                                                
         і
і           push  offset Amx                                     
         і
і           push  offset Tmx         ; поменять переданные указат
ели      і
і           push  offset Bmx                                     
         і
і                                                                
         і
і    CONTINUE:                                                   
         і
і                                                                
         і
і           call  matrixmul_proc                                 
         і
і           cmp   ecx,NUM_JOINT                                  
         і
і           jle   NXT_MATRIX                                     
         і
і    ;                                                           
         і
і    ; Если REVERSE = 1, то конечный результат будет в           
         і
і    ; Bmx, а иначе в Tmx.                                       
         і
і    ;                                                           
         і
і           popad                                                
         і
і                                                                
         і
і    main_code    ends                                           
         і
і                                                                
         і
і    end    START, ds:trans_data, ss:trans_stack                 
         і
і                                                                
         і
і                                                                
         і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
ДДДДДДДДДЩ


Яндекс цитирования