Системное программирование

Автор: Пользователь скрыл имя, 10 Марта 2013 в 15:44, курс лекций

Описание работы

Один из основных принципов машины фон Неймана — то, что и программы, и данные хранятся в одной и той же памяти. Сохраняемая в памяти программа представляет собой некоторые коды, которые могут рассматриваться как данные. Возможно, с точки зрения программиста программа — активный компонент, она выполняет некоторые действия. Но с точки зрения процессора команды программы — это данные, которые процессор читает и интерпретирует. С другой стороны программа — это данные с точки зрения обслуживающих программ, например, с точки зрения компилятора, который на входе получает одни данные — программу на языке высокого уровня (ЯВУ), а на выходе выдает другие данные — программу в машинных кодах.

Работа содержит 1 файл

Системное программирование 15 лекций.doc

— 946.00 Кб (Скачать)

 

 

 

4

Умножение

Операция умножения  для беззнаковых данных выполняется  командой MUL, а для знаковых — IMUL (Integer MULtiplication — умножение целых  чисел).

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

Байт на байт

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

Слово на слово

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

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

MUL MULTR

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

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

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

Беззнаковое умножение: Команда MUL

Команда MUL (MULtiplication — умножение) умножает беззнаковые  числа.

Знаковое умножение: Команда IMUL

Команда IMUL (Integer MULtiplication — умножение целых чисел) умножает знаковые числа.

Команда MUL рассматривает шест.80 как +128, а команда IMUL — как -128. В результате умножения -128 на +64 получается -8192 или шест.E000.

Если множимое и множитель имеет одинаковый знаковый бит, то команды MUL и IMUL генерируют одинаковый результат. Но, если сомножители  имеют разные знаковые биты, то команда MUL вырабатывает положительный результат умножения, а команда IMUL — отрицательный.

Повышение эффективности умножения

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

Умножение на 2:

SHL AL,1

Умножение на 8:

MOV CL,3

SHL AX,CL

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

Обычно умножение имеет два типа: «байт на байт» и «слово на слово».

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

1365

х

12

------

2730

1365

------

16380

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

13  65

х   х

12  12

---  ---

26  130

13  65

---  ---

156  780

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

15600

+

780

-------

16380

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

Умножение двойного слова на слово

Процедура E10XMUL умножает двойное слово на слово. Множимое, MULTCND, состоит из двух слов, содержащих соответственно шест.3206 и шест.2521. Определение данных в виде двух слов (DW) вместо двойного слова (DD) обусловлено необходимостью правильной адресации для команд MOV, пересылающих слова в регистр AX. Множитель MULTPLR содержит шест.6400.

Область для записи произведения, PRODUCT, состоит из трех слов. Первая команда MUL перемножает MULTPLR и правое cлово поля MULTCND; произведение — шест.0E80 E400 записывается в PRODUCT+2 и PRODUCT+4. Вторая команда MUL перемножает MULTPLR и левое слово поля MULTCND, получая в результате шест. 138A 5800. Далее выполняется сложение двух произведений следующим образом:

Произведение 1: 0000 0E80 E400

Произведение 2: 138A 5800

Результат: 138A 6680 E400 

 

Так как первая команда ADD может выработать перенос, то второе cложение выполняется командой сложения с переносом ADC (ADd with Carry).

В силу обратного представления  байтов в словах, область PRODUCT в действительности будет содержать значение 8A13 8066 00E4. Программа предполагает, что первое слово в области PRODUCT имеет начальное значение 0000.

Умножение двойного слова на двойное  слово

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

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

слово 2 слово 2

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

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

слово 1 слово 1

Каждое произведение в регистрах DX и AX складывается с  соответствующим словом в окончательном  результате.

Хотя логика умножения  двойных слов аналогична умножению  двойного слова на слово, имеется  одна особенность, после пары команд сложения ADD/ADC используется еще одна команда ADC, которая прибавляет 0 к значению в итоговом поле.

Это необходимо потому, что  первая команда ADC сама может вызвать  перенос, который последующие команды  могут стереть. Поэтому вторая команда ADC прибавит 0, если переноса нет, и прибавит 1, если перенос есть.

Финальная пара команд ADD/ADC не требует дополнительной команды ADC, так как область итога достаточно велика для генерации окончательного результата и переноса на последнем  этапе не будет.

4

Сдвиг регистровой пары DX:AX

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

Сдвиг влево на 4 бита

MOV CX,04 ;Инициализация на 4 цикла  C20:

SHL DX,1 ;Сдвинуть DX на 1 бит  влево 

SHL AX,1 ;Сдвинуть AX на 1 бит  влево 

ADC DX,00 ;Прибавить значение  переноса 

LOOP C20 ;Повторить Сдвиг  вправо на 4 бита 

MOV CX,04 ;Инициализация  на 4 цикла D20:

SHR AX,1 ;Сдвинуть AX на 1 бит  вправо 

SHR DX,1 ;Сдвинуть DX на 1 бит  вправо 

JNC D30 ;В случае, если  есть перенос, 

OR AH,10000000B ; то вставить 1 в AH D30:

LOOP D20 ;Повторить

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

Пример написан для  сдвига на 4 бита, но может быть адаптирован для других величин сдвигов:

MOV CL,04 ;Установить фактор сдвига 

SHL DX,CL ;Сдвинуть DX влево  на 4 бита 

MOV BL,AH ;Сохранить AH в  BL

SHL AX,CL ;Сдвинуть AX влево  на 4 бита 

SHL BL,CL ;Сдвинуть BL вправо  на 4 бита 

OR DL,BL ;Записать 4 бита  из BL в DL

4

Деление

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

Существуют две  основные операции деления:

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

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

Деление двойного слова на слово

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

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

DIV DIVISOR

В случае, если поле DIVISOR определено как байт (DB), то операция предполагает деление слова на байт. В случае, если поле DIVISOR определено как слово (DW), то операция предполагает деление двойного слова на слово.

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

Беззнаковое деление: Команда DIV

Команда DIV делит  беззнаковые числа.

Знаковое деление: Команда IDIV

Команда IDIV (Integer DIVide) выполняет деление знаковых чисел.

Повышение производительности

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

Деление на 2:

SHR AX,1

Деление на 8:

MOV CL,3

SHR AX,CL

Переполнения и прерывания

Используя команды DIV и особенно IDIV, очень просто вызвать  пеpеполнение. Прерывания приводят (по крайней мере в системе, используемой при тестировании этих программ) к непредсказуемым результатам. В операциях деления предполагается, что частное значительно меньше, чем делимое.

Деление на ноль всегда вызывает прерывание. Но деление  на 1 генерирует частное, которое равно  делимому, что может также легко  вызвать прерывание.

Рекомендуется использовать следующее правило: если делитель — байт, то его значение должно быть меньше, чем левый байт (AH) делителя: если делитель — слово, то его значение должно быть меньше, чем левое слово (DX) делителя.

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

Деление вычитанием

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

Метод заключается в том, что делитель вычитается из делимого и в этом же цикле частное увеличивается на 1.

Вычитание продолжается, пока делимое остается больше делителя. В cледующем примере, делитель находится  в регистре AX, а делимое — в BX, частное вырабатывается в CX:

SUB CX,CX ;Очистка частного C20:

CMP AX,BX ;В случае, если делимое  < делителя,

JB C30 ; то выйти 

SUB AX,BX ;Вычитание делителя из  делимого 

INC CX ;Инкремент частного 

JMP C20 ;Повторить цикл С30:

RET ;Частное в CX, остаток в AX

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

1. В метке  C20 сравнивать AX и BX только при нулевом DX.

2. После команды  SUB вставить команду SBB DX,00.

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

4

Преобразование знака

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

NEG AX

NEG BL

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

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

NOT DX ;Инвертирование битов 

NOT AX ;Инвертирование битов 

ADD AX,1 ;Прибавление 1 к AX

ADC DX,0 ;Прибавление переноса к  DX

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

 

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

Важно:

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

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

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

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

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

Информация о работе Системное программирование