Приложение 2. Подпрограммы умножения и деления
Как и подавляющее число других восьмиразрядных микропроцессоров, Z80 не содержит специальных инструкций для выполнения операций умножения и деления: эти действия реализуются с помощью подпрограмм.
Умножение 8-разрядных чисел
Вход: E – множимое; H – множитель.
Выход: HL – произведение.
Изменяемые регистры: B, D, E, H, L.
MULT: LD B,8 ; Счётчик разрядов
LD D,0 ; Подготовка 16-разрядных полей множимого и
LD L,D ; промежуточного результата/произведения
LOOP: ADD HL,HL ; Сдвиг HL влево на один разряд
JR NC,DECB ; Если флаг C = 0, очередной разряд
; множителя равен нулю, сложение не нужно
ADD HL,DE ; Прибавление множимого
DECB: DJNZ LOOP ; Повторение, пока не обработаны все биты
RET ; Возврат
Умножение 16-разрядных чисел
Вход: BC – множимое; DE – множитель.
Выход: DEHL – произведение.
Во время работы подпрограммы аккумулятор используется в качестве счётчика разрядов.
MPY: LD HL,0000h ; Начальная инициализация произведения
LD A,16 ; Счётчик разрядов
LOOP: ADD HL,HL ; Сдвиг промежуточного результата влево
EX DE,HL
ADC HL,HL
EX DE,HL
JR NC,DECA ; Если очередной разряд множителя равен 0,
; сложение не требуется
ADD HL,BC ; Прибавление множимого
JR NC,DECA
INC DEC
DECA: DEC A ; Уменьшение счётчика и повтор операции,
JP NZ,LOOP ; пока не обработаны все разряды
RET
8-разрядное деление
Вход: HL – делимое; A – делитель.
Выход: C – частное; H – остаток.
В процессе работы используются все регистры общего назначения.
DIV: LD BC,0800h ; Счётчик разрядов в регистре B
LD H,C
LD E,H
LD D,A ; Копия делителя
LOOP: ADD HL,HL ; Сдвиг делимого влево
LD A,H ; Определение очередной цифры результата
CP D
JR C,NEXT
SBC HL,DE ; Вычитание делителя из текущего остатка
NEXT: CCF ; Формирование очередной цифры результата
RL C
DJNZ LOOP ; Цикл, пока не обработаны все разряды
RET
[1] В этой и последующих командах данной группы, кроме LDIR и LDDR, флаг P/V сбрасывается, если после уменьшения BC становится равным нулю, и устанавливается в противном случае.
[2] Каждый проход инструкций с автоматическим повторением выполняются за 21 такт, если условие повторения выполнено, и за 16 тактов в противном случае.
[3] Сравнение выполняется путём вычитания содержимого ячейки памяти (HL) из аккумулятора. Результат вычитания теряется, но на его основе формируются значения флажков.
[4] В этой и последующих инструкциях условного относительного перехода выполнение команды занимает 7 тактов, если условие перехода не соблюдается, и 12 тактов, если соблюдается.
[5] Инструкция DJNZ выполняется 8 тактов, если после декремента B=0 (переход не выполняется), и 13, тактов, если B≠0 (переход выполняется).
[6] В командах условного вызова подпрограммы (CALL cc,nn) и условного возврата из подпрограммы (RET cc) меньшее количество тактов соответствует ситуации, когда условие не выполнено (переход не происходит), а большее количество тактов – ситуации, когда условие выполнено (переход происходит).
[7] В инструкциях IN A,(p) и OUT (p),A младший байт адреса порта содержится в коде команды (p), а старший – в аккумуляторе. В остальных инструкциях этой группы 16-разрядный адрес порта содержится в регистровой паре BC.
Инструкция IN r,(C) при коде r, равном 110, производит соответствующую прочитанному значению установку или сброс флажков, но само прочитанное значение не сохраняет. Действия инструкции OUT (C),r в такой же ситуации не определены.
В инструкциях INI, IND, OUTI и OUTD флажок Z устанавливается, если содержимое регистра B было обнулено, или сбрасывается, если в B получено ненулевое значение. По этой же причине в инструкциях INIR, INDR, OTIR и OTDR этот флажок всегда устанавливается.
Большая длительность инструкций INIR, INDR, OTIR и OTDR соответствует ситуации, когда содержимое регистра B отлично от нуля, т.е. когда выполнение инструкции продолжается; меньшая длительность – когда регистр B обнуляется, и выполнение инструкции прекращается.