- •Эрни Каспер Программирование на языке Ассемблера для микроконтроллеров семейства i8051
- •1.Что нужно знать программисту о микроконтроллерах семейства i8051
- •1.1.Общие сведения об архитектуре i8051
- •1.2.Правила записи команд микроконтроллера семейства i8051 на Ассемблере
- •1.3.Форматы и способы адресации данных
- •1.4.Форматы и способы адресации команд
- •1.5.Команды пересылки информации
- •1.6.Команды поразрядной обработки информации
- •1.7.Команды арифметических операций
- •1.8.Управляющие команды
- •2.Директивы ассемблера для микроконтроллеров семейства i8051
- •2.1.Общие понятия о процессах трансляции и компоновки
- •2.2.Обработка имен транслятором и компоновщиком
- •2.3.Директивы резервирования памяти и инициализации данных
- •2.4.Использование выражений в операндах
- •2.5.Директивы условной трансляции
- •2.6.Директивы подстановок
- •2.7.Директивы управления вводом и выводом
- •Глава 3.
- •3.Кросс-средства фирмы 2500 a.D. Software, Inc. Для семейства i8051
- •Глава 4
- •4.Программирование арифметических действий
- •4.1.Кодирование информации в микроконтроллере
- •4.2.Арифметические действия с большими числами
- •4.3.Арифметические действия с отрицательными числами
- •4.4.Контроль точности при программировании арифметических операций
- •Глава 5
- •5.Программирование вычисления функций
- •5.1.Возведение в квадрат и извлечение квадратного корня
- •5.2.Переход от десятичной системы счисления к двоичной и обратно
- •5.3.Вычисление функций при помощи таблиц
- •5.4.Вычисление обратной функции по таблице прямой функции
- •5.5.Компенсация систематических погрешностей при помощи таблиц
- •Глава 6
- •6.Программирование фильтрации сигналов
- •6.1.Особенности цифровой фильтрации сигналов
- •6.2.Программирование простейших фильтров нижних частот
- •6.3.Программирование фильтра для оценки параметров сигнала
- •6.4.Программирование медианного фильтра
- •Глава 7
- •7.Программирование взаимодействия с внешними устройствами
- •7.1.Общие вопросы взаимодействия
- •7.2.Порядок выполнения прерываний в микроконтроллерах семейства i8051.
- •7.3.Синхронизация работы программы внешним или внутренним сигналом
- •7.4.Программирование приема информации от датчиков
- •7.5.Программирование выдачи команд на исполнительные устройства
- •7.6.Программирование ввода и вывода информации для пользователя
- •8.Несколько рекомендаций о стиле программирования
- •8.1.Стиль программирования и использование ресурсов
- •8.2.Оформление исходного текста программы
- •8.3.Системы обозначений, выражения и простые подстановки
- •8.4.Применение подпрограмм и сложных текстовых подстановок
4.3.Арифметические действия с отрицательными числами
Команды умножения и деления дают правильные результаты только в случае положительных операндов. Поэтому для правильного вычисления произведения или частного в случае отрицательных операндов требуется сначала вычислить модули этих операндов. После умножения или деления модулей в случае необходимости можно вернуться к первоначальному кодированию с учетом знака результата. Таким образом, для умножения и деления чисел, хотя бы одно из которых отрицательное, нужно знать способы вычисления модуля числа и вычисления знака результата.
Знак результата умножения или деления двух чисел будет отрицательным тогда, когда только одно из чисел отрицательное. Пусть знак первого числа записан в старшем бите регистра RO, а знак второго числа -- в старшем бите регистра R1. Тогда знак результата можно записать в бит FO при помощи четырех команд:
Указанный бит удобен для запоминания знака результата потому, что на него не влияют результаты команд, выполняющих арифметические операции.
В i8051 отсутствует операция изменения знака числа, однако она может быть выполнена вычитанием этого числа из 0. Предположим, что нам нужно изменить знак однобайтового числа, записанного в регистр RO. Для этого нужно очистить бит переноса и накопитель, вычесть из него содержимое регистра и переслать результат из накопителя в регистр:
Однако имеется другой алгоритм изменения знака числа, использующий следующую закономерность кодирования отрицательных чисел:
Из приведенного примера видно, что нужно сделать для изменения знака числа на противоположный. Сначала необходимо изменить содержимое всех разрядов исходного числа на обратные значения (обратный код), а затем добавить единицу в младший разряд числа (дополнительный код). Использование этого алгоритма для изменения знака однобайтового числа не дает никакого выигрыша, если подлежащее преобразованию число записано в регистр. Но для изменения знака числа, записанного в накопитель, гораздо проще воспользоваться следующей парой команд.
Для изменения знака двухбайтового числа необходимо сначала получить обратные коды младшего и старшего байтов, а затем добавить 1 к
MOV A, RO ; знака в старший бит накопителя
XRL A, R1 ;вычисление знака результата
RLC А ; старший бит накопителя в флаге переноса
MOV FO, С ; знака результата в бите FO
CLR С ; очистка бита переноса
CLR А ; очистка накопителя
SUBB A, RO ; вычитание
MOV RO, A ; запоминание результата
+0 00000000 -0 00000000 = 11111111+1
+1 00000001 -1 11111111 = 11111110+1
+2
... 00000010
......... —2
... 11111110 = 11111101+1
...........
CPL INC А А ; вычисление обратного кода ; добавление 1
младшему байту. Пусть младший байт числа записан в накопителе, а старший — в регистре В. Число с обратным знаком получается в том же месте, что и исходное.
CPL А ; вычисление обратного кода мл. байта
XRL В, #FFh ; вычисление обратного кода ст. байта
ADD A, #01h ; добавление 1
JNC nc ; переход по отсутствию переноса
INC В ; коррекция ст . байта
nc: NOP ; для записи метки
Обратите внимание на то, как в случае переноса из младшего байта старший байт увеличивается на 1. Для получения переноса из младшего байта нужно использовать в программе не INC, a ADD. Код операции NOP записан для того, чтобы не оставлять пустой строку с меткой. В реальной программе в этой строке должна быть записана первая команда следующего блока.
В качестве примера приведем программу перемножения однобайтовых чисел с произвольным знаком. Исходные числа находятся в регистрах RO, R1, а произведение получается в регистре В (старший байт) и накопителе (младший байт).
MOV A, RO ; множимое
MOV C, A.7 ; знак первого числа в бите С
JNC mlsl ; переход по положительному множимому
CPL A ; вычисление обратного кода множимого
INC A ; вычисление модуля множимого
mls1: MOV B, Rl ; множитель
JNB B,7 mls2 ; переход по положительному множимому
CPL С ; вычисление знака произведения
XRL В, #FFh ; вычисление обратного кода множителя
INC В ; вычисление модуля множителя
mls2: MOV FO, С ; запоминание знака произведения
MUL AB ; вычисление произведения модулей
JNB FO, mls3 ; переход по положительному произведению
CPL A ; вычисление обратного кода мл. байта
XRL B, #FFh ; вычисление обратного кода ст. байта
ADD A, #01h ; добавление 1
JNC mls3 ; переход по отсутствию переноса
INC В ; коррекция ст . байта
mls3: NOP ; для записи метки
Смысл производимых в этой программе действий должен быть понятен по ранее приведенным примерам для вычисления знака произведения и изменения знака числа. Запоминание знака числа необходимо потому, что операция умножения записывает 0 в бит переноса. При умножении и делении чисел, представленных несколькими байтами, изменение знака для вычисления модуля исходных чисел и кода результата требуют выполнения большего количества команд.
Умножение отрицательного числа на целую степень двойки при помощи сдвига ни чем не отличается от умножения положительного числа. При делении отрицательного числа нужно при каждом сдвиге записывать в старший бит старшего байта 1. Для этого после записи старшего байта числа в накопитель перед командой сдвига влево нужно скопировать седьмой бит накопителя в бит переноса.