Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Методы / Мышев А.В. (ч.2)

.pdf
Скачиваний:
29
Добавлен:
11.06.2015
Размер:
580.8 Кб
Скачать

Рис. 5.2. Общая схема умножения чисел произвольной длины

Для старшей половины произведения резервируется область памяти, начиная с адреса HIGH. Схема подпрограммы умножения показана на рис. 5.3.

Подпрограмма начинается с проверки длины сомножителей и сразу осуществляет возврат, если она равна нулю. Затем формируются конечные адреса множителя и старшей половины произведения, причем конечный адрес множителя помещается в регистр DE, а конечный адрес старшей половины произведения запоминается в слове ENDHP. Вычисление двух конечных адресов объясняется необходимостью сдвига вправо, а такой сдвиг начинается со старших байтов. Кроме того, начальный адрес множимого сохраняется в слове MPCND. После этого число байтов сомножителей путем умножения на 8 превращается в счетчик битов, который сохраняется в слове COUNT, т.к. для него регистров МП не хватает. В старшую половину произ-ведения загружаются нули, подготавливая ее для накопления частичных произведений.

Начиная с метки LOOР, реализован цикл умножения на последовательные биты множителя. Первое действие в цикле заключается в сдвиге старшей половины произведения вправо на один бит. При входе в цикл флажок переноса сбрасывается в ноль, а затем он будет показывать перенос, возникший при сложении суммы частичных произведений и множимого. Поэтому при сдвиге состояние

41

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

 

MDRND

Нет

Да

 

Длина=0

Образовать и сохранить конечные адреса множителя и произведения

Образовать и сохранить счетчик бит

ZERO

Очистить старшую половину произведения

Сбросить флажок переноса

LOOP

SHIFTP

Сдвинуть старшую половину произведения вправо с учетом флажка

SHIFTM

Сдвинуть множитель вправо с учетом флажка переноса

Да

Нет

 

Бит=0?

ADDI

Прибавить

множимое

DECR

Декркмент счетчика бит

COUNT

1. «Глобальный» цикл (с

 

Нет

Счетчик=0?

Да

 

метки LOOP) умножения на

 

 

 

 

Возврат

все биты множителя с чис-

 

 

 

 

 

 

 

 

 

лом

повторений, равным

Рис. 5.3. Граф-схема алгоритма ум-

числу битов в сомножите-

лях.

Этот цикл включает в

ножения чисел произвольной длины

себя все остальные циклы, а

счетчик его находится в слове памяти COUNT.

42

2.Цикл сдвига вправо на один бит старшей половины произведения с числом повторений, равным длине сомножителей в байтах; начинается с метки SHIFTP.

3.Цикл сдвига вправо на один бит множителя. Он также повторяется столько раз, чему равна длина сомножителей в байтах. Начало цикла идентифицирует метка SHIFTM.

4.Цикл суммирования множимого и старшей половины произведения начинается с метки ADDL, а число повторений равно длине сомножителей в байтах.

Программа 5.9. Умножение беззнаковых целых чисел произвольной длины

;Начальный адрес множителя в HL, начальный адрес множимого

;в DE, длина сомножителей в регистре В. Старшая половина

;произведения начиается с адреса HIGH, а младшая

;находится на месте множителя.

;

MPRND: ; Проверить нулевую длину сомножителей.

MOV

A, B

; Передать длину в аккумулятор

ORA

A

 

RZ

 

; Возврат, если она равна нулю

;Образовать конечные адреса старшей половины

;произведения и множителей.

MOV

C, B

; Длина в регистрах В и С

MVI

B,0

 

 

DAD

B

; Конечный адрес множителя в HL

MVI

B,0

 

 

DAD

B

; Конечный адрес множителя в HL

XCHG

 

; в HL - начальный адрес множимого

SHLD

MPCND

; Сохранить его в слове MPCND

LXI

H, HIGH

; Начальный адрес старшего произведения

DAD

B

; Конечный адрес старшего произведения

SHLD

ENDHP

; Сохранить его в слове ENDHP

; Образовать счетчик бит.

 

 

MOV

L, C

; Длина сомножителей в HL

MOV

H, B

 

 

DAD

H

; Умножить ее на В

DAD

H

 

 

DAD

H

 

 

SHLD

COUNT

; Сохранить счетчик битов в COUNT

; Очистить старшую половину произведения

MOV

B, C

; Счетчик байтов в регистре В

LXI

H, HIGH

; Начальный адрес старшего произведения

ZERO: MVI

M,0

; Очистить старшую

INX

H

;

половину произведения

43

 

DCR

B

 

 

 

JNZ

ZERO

 

 

 

; Подготовка к умножению закончена.

 

MOV

C, M

; Флажок переноса должен быть сброшен

LOOP:

 

 

; Сдвинуть вправо первую половину произ-

ведения

MOV

B, C

; Счетчик байтов в регистре В

 

 

LHLD

ENDHP

; Сдвиг начинается со старшего байта

SHIFTP: DCX

H

; Продвинуть указатель

 

MOV

A, M

; Произвести сдвиг байта

 

RAR

 

 

 

 

MOV

M, A

 

 

 

DCR

B

; Декремент счетчика байтов

 

JNZ

SHIFTP

; Продолжать сдвиг

 

; Сдвинуть вправо множитель

 

 

MOV

L, E

; В HL конечный адрес множителя

 

MOV

H, D

 

 

 

MOV

B, C

; Счетчик байтов в регистре В

SHIFTM:DCX

H

; Продвинуть указатель

 

MOV

A, M

; Произвести сдвиг байта

 

RAR

 

 

 

 

MOV

M, A

 

 

 

DCR

B

; Декремент счетчика байтов

 

JNZ

SHIFTM

; Продолжать сдвиг

 

; Анализ очередного бита множителя во флажке переноса

 

JNC

DECR

; Бит множителя равен нулю

 

; Прибавить множимое к произведению

 

PUSH

D

; Сохранить адрес множителя

 

LHLD

MPCND

; Начальный адрес множимого

 

XCHG

 

;

в регистре DE

 

LXI

H, HIGH

; Адрес произведенияв регистре HL

 

MOV

B, C

; Счетчик байтов в регистре В

 

ANA

A

; Сбросить флажок переноса

ADDL:

LDAX

D

; Очередной байт множимого

 

ADC

M

; Прибавить к произведению

 

MOV

M, A

 

 

 

INX

H

 

 

 

INX

D

; Продвинуть указатели

 

DCR

B

; Декремент счетчика байтов

 

JNC

ADDL

; Продолжать сложение

 

POP

D

; Восстановить адрес множителя

 

; Декремент и анализ счетчика битов.

DECR;

LDA

COUNT

; Декремент младшего байта

 

DCR

А

;

счетчика битов

 

STA

COUNT

 

 

 

JNC

LOOP

; Повторять умножение

44

PUSH

PSW

; Сохранить флажок переноса

LDA

COUNT+1

 

; Проверить на ноль старший байт

ANA

A

;

счетчика битов

JZ

EXIT

; Он равен нулю, возврат

DCR

A

; Декремент старшего байта

STA

COUNT+1

 

;

счетчика битов

POP

PSW

; Восстановить флажок переноса

JMP

LOOP

; Повторять умножение

EXIT: POP

PSW

; Очистить стек

RET

 

; Возврат

 

Умножение целых знаковых чисел, представленных в дополнительном коде, с получением произведения также в дополнительном коде не вызывает серьезных трудностей. Традиционно умножение знаковых операндов выполняется путем определения знака произведения (он равен сумме по модулю два знаковых битов сомножителей) умножения абсолютных значений (для чего привлекается любая из рассмотренных выше подпрограмм умножения) и образования дополнительного кода произведения. Можно также воспользоваться следующим приемом. Анализируется знаковый бит множителя, и сомножители преобразуются так, чтобы множитель стал положительным; когда множитель отрицательный, сомножители умножаются на –1. После этого происходит умножение операндов как беззнаковых целых чисел. В результате получается произведение, сразу представленное в дополнительном коде, если множимое берется двойной длины с расширением знака в старшую половину. Известен также алгоритм Бута умножения чисел, представленных в дополнительном коде, с автоматическим получением произведения в дополнительном коде. Однако программная реализация алгоритма Бута, в котором необходимо анализировать по два соседних бита множителя, оказывается для МП К580 громоздкой.

Для умножения целых знаковых чисел Х и Y, имеющих длину n битов и представленных в дополнительном коде, можно использовать следующий прием. Вычисляется 2n-битное произведение Р кодов Х и Y, которые интерпретируются как беззнаковые целые числа; для такого умножения применима любая из рассмотренных выше подпрограмм. В зависимости от знаков сомножителей для

45

получения произведения в дополнительном коде могут потребоваться корректирующие действия.

Если оба сомножителя положительны (X ≥ 0, Y ≥ 0), то полученное значение Р представляет собой истинное произведение и коррекция не нужна. В том случае, когда сомножители имеют разные знаки (X ≥ 0, Y < 0 или Х < 0, Y ≥ 0),

P =X(2n – Y) = X2n – XY

или

P = (2n – X)Y = Y2n – XY.

Здесь коррекция заключается в том, чтобы вычесть из Р положительный сомножитель, сдвинутый на п битов влево. Это эквивалентно прибавлению к старшей части произведения Р дополнительного кода положительного сомножителя. Если оба сомножите-

ля отрицательны,

P = (2n – X)(2n – Y) = 22n – X2n – Y2n + XY.

Для коррекции результата необходимо прибавить к Р абсолютные значения сомножителей, сдвинутые на п битов влево. Это эквивалентно прибавлению к старшей части произведения дополнительных кодов исходных операндов.

5.4. Деление

Общая циклическая схема операции деления очень похожа на операцию умножения. Различие заключается в том, что умножение выполняется в цикле «сложение – сдвиг», а для деления потребуется цикл «вычитание – сдвиг». Поскольку частное можно получать только со старших разрядов, имеются два варианта деления: со сдвигом остатка влево и со сдвигом делителя вправо. Второй вариант на практике не применяется в связи с необходимостью иметь регистры остатка и делителя двойной длины. Операция деления характерна тем, что в ней всегда возможно переполнение, когда делитель равен нулю.

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

46

сывается в 0, если текущий остаток больше делителя (бит частного равен 1), и устанавливается в 1, показывая заем, если текущий остаток меньше делителя (бит частного равен 0).

Если при вычитании получен очередной отрицательный остаток, приходится восстанавливать предыдущий положительный остаток путем прибавления делителя. Вариант деления без восстановления остатка, когда при получении отрицательного остатка он сдвигается влево, а затем суммируется с делителем, не применяется в связи с громоздкостью программы. Более распространен прием сохранения текущего положительного остатка в дополнительном буфере; при получении отрицательной разности восстановление остатка заключается в том, чтобы возвратить его из буфера.

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

Рассмотрим простую подпрограмму (5.10) деления 8-битных целых беззнаковых чисел со следующим распределением регистров МП: делимое находится в регистре Е, делитель – в регистре D, частное образуется в регистре Н и положительный остаток – в регистре С. Регистр L используется как счетчик битов. При нулевом делителе флажок нуля Z после возврата установлен в 1.

В цикле деления, который начинается с метки LOOP, делимое сдвигается влево с передачей его старшего бита во флажок переноса. Затем остаток из регистра С передается в аккумулятор и бит делимого сдвигается из флажка переноса в его младший бит. Команда SUB D вычитает делитель из остатка. Если разность (т.е. очередной остаток) положительна, происходит переход на метку NOADD. Когда при вычитании возникает заем (очередной остаток отрицательный), команда ADD D восстанавливает предыдущий положительный остаток. После этого во флажке переноса образуется цифра частного и передается посредством сдвига влево в младший бит частного.

47

Программа 5.10. Деление 8-битных целых беззнаковых чисел

 

;Делимое в регистре Е, делитель в регистре D.

 

;Частное возвращается в регистр H, а положительный

 

;остаток в регистре С.

 

 

;

 

 

DIV88:

LXI H,8

;Образовать счетчик битов, сбросить частное

 

MVI C,0

;Сбросить остаток

 

MOV A,D

;Проверить нулевой делитель

 

ORA A

 

 

 

RZ

;Возврат делителя равен нулю

LOOP:

MOV A,E

;Передать делимое в аккумулятор

 

RAL

;Сдвинуть его влево

 

MOV E,A

;Вернуть делимое в регистр Е

 

MOV A,C

;Сдвинуть остаток влево

 

RAL

 

 

 

SUB D

;Вычесть делитель

 

JNC NOADD

;Востанавливать остаток не нужно

 

ADD D

;Востановить остаток

NOADD: MOV C,A

;Вернуть отстаток в регистр С

 

CMC

;Образовать битчастного

 

RAL

;

в регистр H

 

MOV H,A

 

 

 

DCR L

;Декремент счетчика битов

 

JNZ LOOP

;Повторять деление

 

INR L

;Сбросить флажок нуля

 

RET

;Возврат

 

Деление чисел произвольной длины, как и умножение, вызывает определенные трудности, хотя общий принцип деления путем последовательных вычитаний сохраняется. Рассмотрим подпрограмму DIVRND деления целых беззнаковых чисел, в которой делимое и делитель имеют произвольную, но одинаковую длину. Общая схема реализуемой ею операции показана на рис. 5.4, а граф-схема алгоритма – на рис. 5.5

По сравнению с умножением в операции деления имеется особая ситуация, связанная с получением отрицательной разности при вычитании делителя. Чтобы вернуться к предыдущему положительному остатку, приходится суммировать делитель и отрицательную разность. Можно избежать этой операции, если выделить для старшей части делимого две буферные области (или просто буферы) с начальными адресамиBUFFl иBUFF2; длинаобоихбуферовравнадлинеисходных

48

операндов. Адреса буферов находятся в ячейках памяти PTRl и PTR2. При вычитании делителя из старшей части делимого, находящейся в одном буфере (его называют текущим буфером), разность помещается в другой буфер. Если получена отрицательная разность {цифра частного равна нулю), в текущем буфере сохранилась предыдущая положительная старшая часть делимого, и в следующем цикле деления необходимо использоватьсодержимоеэтогожебуфера.

Когда получена положительная разность (цифра частного равна единице), далее использовать именно эту разность, находящуюся в другом буфере. Следовательно, при получении положительной разности необходимо сделать текущим другой буфер, т.е. скоммутировать (переключить) буферы. Для этого достаточно обменять содержимое ячеек памяти PTR1 иPTR2, хранящих начальные адреса буферов.

Рис. 5.4. Общая схема деления чисел произвольной длины

В каждом цикле деления (он начинается с метки LOOP), число которых определяется счетчиком битов, производится сдвиг влево делимого, а также содержимого текущего буфера. При этом очередной бит делимого через флажок переноса попадает в младший бит текущего буфера. После этого выполняется вычитание делителя из содержимого текущего буфера и образованная во флажке переноса цифра частного (фактически она равна инверсии флажка

49

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

Рис 5.5. Граф-схема алгоритма деления чисел произвольной длины

50