Доступ к числовым данным в памяти может быть получен с помощью методов адресации через байты ModR/M и SIB (необязательно). Это означает, что числовые типы данных могут быть объединены в совокупности данных, изменяющиеся от простых к сложным в соответствии с необходимостью применения. Методы адресации и нотация ассемблера ASM386/486, используемая для определения методов в командах, обеспечивают прямой и последовательный доступ к структурам, массивам, массивам структур и другим организациям данных. В таблице 18-3 даны несколько примеров числовых команд вместе с операндами, иллюстрирующие различные методы адресации.
Таблица 18-3. Примеры методов адресации +----------------------------------------------------------------+| Код | Интерпретация |+----------------------------+-----------------------------------|| FIADD ALPHA | ALPHA - простой скаляр (прямой || | доступ) || FDIVR ALPHA, BETA | BETA - поле в структуре, которое || | перекрывается с ALPHA ( прямой || | доступ ) || FMUL QWORD PTR [BX] | BX содержит адреса длинных вещест-|| | венных переменных (косвенная адре-|| | сация по регистру) || FSUB ALPHA [SI] | ALPHA - массив, SI содержит смеще-|| | ние относительно начала массива || | (индексный метод доступа) || FILD [BP], BETA | BP содержит адрес структуры в сте-|| | ке ЦПУ, BETA - поле в структуре || | (базированный метод доступа) || FBLD TBYTE PTR [BX] [DI] | BX содержит адрес упакованного де-|| | сятичного массива, DI - смещение || | элемента массива (индексный метод || | доступа) |+----------------------------------------------------------------+
Пример сравнительного программирования
На рисунках 18-5 и 18-6 даны примеры простой числовой программы ARRSUM на языках ассемблера ASM386/486 и PL/M386/486. В программе рассматривается массив X$ARRAY, содержащий простые вещественные величины от 0 до 100; целая переменная N$OF$X указывает размерность массива. Вычисляются три суммы:
SUM$X, сумма элементов массива
SUM$INDEXES, сумма каждого элемента, умноженного на его индекс, где первый элемент массива имеет индекс 1, второй - 2 и так далее
SUM$SQUARES, сумма квадратов элементов массива
(Настоящая программа, конечно, кроме этих шагов должна сохранять и использовать результаты вычислений.) Управляющеему слову присваивается одна из величин: округление до ближайшего целого, 64-разрядная точность, запрещение прерываний и блокировка всех исключений, кроме недопустимых операций. Предполагается, что обработчик исключений был написан с учетом недопустимой операции, и в случае их обнаружения они обрабатывается по прерыванию 16.
Версия ARRSUM на языке PL/M-386/486 очень последовательна и иллюстрирует насколько легко в языке могут быть использованы числовые возможности процессора i486. После объявления переменных программа вызывает встроенные процедуры для инициализации модуля обработки операций с плавающей точкой и загрузки управляющего слова. Программа "очищает" пременные суммы и затем выполняет шаги цикла DO. Управление циклом принимает в расчет, что обычно в языке PL/M-386/486 индекс первого элемента массива имеет величину 0. При вычислении SUM$INDEXES встроенная процедура FLOAT преобразует величину I+1 от целого типа к вещественному, так как язык не поддерживает "смешанного" арифметического режима. Один из недостатков модуля обработки операций с плавающей точкой процессора i486, что он не поддерживает вычисления над смешанными типами данных (все величины преобразуются к 80-битовому вещественному формату расширенной точности)
+---------------------------------------------------------------+| || /************************************************** || * * || * МОДУЛЬ СУММА_МАССИВА * || * * || **************************************************/ || || array$sum: do; || || declare (sum$x, sum$indexes, sum$squares) real; || declare x$array(100) real; || declare (n$of$x, i) integer; || declare control $ FPU literally '033eh'; || || /* Инициализация массива x$array и n$of$x */ || call init$real$math$unit; || call set$real$mode(control $ FPU); || || /* Очистить суммы */ || sum$x, sum$indexes, sum$squares = 0.0; || || /* Цикл по массиву с подсчетом сумм */ || do i = 0 to n$of$x - 1; || sum$x = sum$x + x$array(i); || sum$indexes = sum$indexes + (x$array(i)* || float(i+1)); || sum$squares = sum$squares + (x$array(i)* || x$array(i)); || end; || || /* и так далее */ || || end array$sum; || |+---------------------------------------------------------------+ Рисунок 18-5. Пример программы на языке PL/M-386/486
Версия программы на ассемблере ASM386/486 (Рисунок 18-6) определяет внешнюю процедуру INITFPU, которая производит инициализацию процессора и его эмулятора независимо от текста исходной программы. После определения данных, настройки сегментных регистров и указателя стека программа вызывает процедуру INITFPU и загружает управляющее слово. Вычисления начинаются со следующих трех команд, которые очищают три регистра загрузкой 0 в стек. Как показано на Р1исунке 18-7 эти регистры сохраняют значение вершины стека во время вычислений, когда временные величины помещаются и удаляются из стека.
Для управления итерацией по массиву XARRAY программа использует цикл LOOP; в регистр EСX, содержимое которого LOOP автоматически уменьшает, помещается переменная N_OF_X, число суммируемых элементов массива. Регистр ESI используется для выборки (индексирования) элементов массива. Программа выполняется по массиву с конца к началу, так что регистр ESI инициализируется указателем на элемент, который должен обрабатываться. Оператор TYPE используется для определения числа байтов в каждом элементе массива. Такое представление позволяет изменить тип массива на вещественный двойной точности простым измененим определения (DD на DQ) и еще одним проходом ассемблера.
Рисунок 18-6 Пример программы на языке ассемблер ASM386/486 +---------------------------------------------------------------+| || name arraysum || || ; Определить процедуру инициализации || || extrn initFPU:far || || ; Выделить память под данные || || data segment rw public || control_FPU dw 033eh || n_of_x dd ? || x_array dd 100 dup (?) || || sum_squares dd ? || sum_indexes dd ? || sum_x dd ? || data ends || || ; Выделить память под стек ЦПУ || || stack stackseg 400 || || ; Начало тела программы || || code segment er public || || assume ds:data, ss:stack || || start: || mov ax, data || mov ds, ax || mov ax, stack || mov eax, ax || mov esp, stackstart stack || || ; Проинициализироавали массив x_array и || ; переменную n_of_x || || ; Подготовить модуль обработки операций || ; с плавающей точкой или его эмулятор || || call initFPU || fldsw control_FPU || || ; Очистить три регистра для хранения || ; результатов суммирования || || fldz || fldz || fldz || || || ; Установить ECX как счетчик цикла и || ; ES как индекс массива x_array || || mov ecx, n_of_x || imul ecx || mov esi, eax || || ; ESI содержит индекс последнего элемента || ; массива плюс 1 || ; Цикл по массиву с подсчетом суммы || || sum_next: || ; возвращение на один элемент назад || ; и помещение в стек || || sub esi, type x_array || fld x_array[esi] || || ; прибавление к сумме и копирование x || ; в стек || || fadd st(3), st || fld st || || ; возведение в квадрат и сложение с суммой || ; (index+1) || || fmul st, st || faddp st(2), st || || ; уменьшение индекса для следующей итерации || || dec n_of_x || loop sum_next || || ; Перемещение x в память || || pop_results: || fstp sum_squares || fstp sum_indexes || fstp sum_x || fwait || || ; || ; И так далее. || ; || code ends || end start, ds:data, ss:stack || || |+---------------------------------------------------------------+ +---------------------------------------------------------------+| || || FLDZ, FLDZ,FLDZ FLD X_ARRAY[SI] || +--------+----------------------+--------+ || ST(0) | 0.0 | SUM_SQUARES ST(0) | 2.5 | X_ARRAY || +--------| +--------| || ST(1) | 0.0 | SUM_INDEXES ST(1) | | SUM_SQUARES || +--------| +--------| || ST(2) | 0.0 | SUM_X ST(2) | 0.0 | SUM_INDEXES || +--------+ +--------| || ST(3) | 0.0 | SUM_X || +--------+ || _________________________________| || || FIADD ST(3), ST FLD ST || +--------+----------------------+--------+ || ST(0) | 2.5 | X_ARRAY(19) ST(0) | 2.5 | X_ARRAY(19) || +--------| +--------| || ST(1) | 0.0 | SUM_SQUARES ST(1) | 2.5 | X_ARRAY(19) || +--------| +--------| || ST(2) | 0.0 | SUM_INDEXES ST(2) | 0.0 | SUM_SQUARES || +--------| +--------| || ST(3) | 2.5 | SUM_X ST(3) | 0.0 | SUM_INDEXES || +--------+ +--------| || ST(4) | 2.5 | SUM_X || +--------+ || _________________________________| || || FMUL ST, ST FADDP ST(2), ST || +--------+----------------------+--------+ || ST(0) | 6.25 | X_ARRAY(19)(2) ST(0) | 2.5 | X_ARRAY(19) || +--------| +--------| || ST(1) | 2.5 | X_ARRAY(19) ST(1) | 6.25 | SUM_SQUARES || +--------| +--------| || ST(2) | 0.0 | SUM_SQUARES ST(2) | 0.0 | SUM_INDEXES || +--------| +--------| || ST(3) | 0.0 | SUM_INDEXES ST(3) | 2.5 | SUM_X || +--------| +--------+ || ST(4) | 2.5 | SUM_X | || +--------+ | || _________________________________| || || FIMUL N_of_X FADDP ST(2), ST || +--------+----------------------+--------+ || ST(0) | 50.0 | X_ARRAY(19) ST(0) | 6.25 | SUM_SQUARES || +--------| +--------| || ST(1) | 6.25 | SUM_SQUARES ST(1) | 50.0 | SUM_INDEXES || +--------| +--------| || ST(2) | 0.0 | SUM_INDEXES ST(2) | 2.5 | SUM_X || +--------| +--------+ || ST(3) | 2.5 | SUM_X || +--------+ || || |+---------------------------------------------------------------+ Рисунок 18-7 Команды и регистровый стек
На Рисунке 18-7 показано содержимое регистрового стека модуля обработки операций с плавающей точкой во время выполнения команд в цикле программы. Предполагается, что программа находится в ее первой итерации, величина переменной N_OF_X равна 20, и 20-й элемент массива XARRAY(19) имеет величину 2.5. По окончании цикла суммы извлекаются из вершины стека, и программа заканчивается простым перемещением сумм в переменные памяти.