Арифметические операции. Сложение и вычитание

Сложение и вычитание
Иногда при написании программ требуется выполнить простые арифметические операции с многобайтными числами. С однобайтными числами все просто, сложение и вычитание выполняются стандартными командами addwf, addlw, subwf, sublw, для многобайтных чисел придется писать небольшие подпрограммы. Я буду рассказывать только про целочисленные операции, без дробных частей.
В тексте статьи и в коде я буду использовать приставки к названиям регистров, чтобы легче разобрать старшие и младшие регистры чисел. Ограничусь четырехбайтными числами, соответствующие приставки, начиная от старшего регистра до младшего: HH, HL, LH, LL.

Рассмотрим простейший вариант сложения двухбайтного и однобайтного числа. Двухбайтное число представлено регистрами varLH (старший байт) и varLL (младший байт), однобайтное число в регистре tmpLL. Сначала складываются младшие регистры обоих чисел, это varLL и единственный регистр tmpLL в случае однобайтного числа. Далее выполняется проверка на возможное переполнение регистра varLL в результате сложения (перенос старшего бита), с помощью бита C регистра STATUS. Если было переполнение (C=1), инкрементируем старший регистр varLH, в противном случае (C=0) ничего не делаем. В случае трехбайтного числа (varHL, varLH, varLL), после инкремента регистра varLH, также необходимо выполнить проверку на его переполнение с помощью команды incfsz. При возникновении переполнения, соответственно инкрементируем самый старший регистр varHL.

При сложении многобайтных чисел, например двухбайтных (varLH, varLL)+(tmpLH, tmpLL), как было сказано, сначала складываются младшие регистры varLL и tmpLL, и проверяется переполнение регистра varLL в результате сложения, при наличии которого инкрементируют старший регистр varLH. Далее складываются старшие регистры varLH и tmpLH, здесь проверку не производим, в случае если заранее известно, что результат сложения не превысит двухбайтного числа (значения 65535). Проверку переполнения регистра varLH, необходимо сделать, если первое число трехбайтное (varHL, varLH, varLL) и результат сложения превышает число 65535. В принципе все также, что и при сложении десятичных чисел столбиком на бумаге.

Ниже представлены коды подпрограмм для различных вариантов сложения. Числа необходимо предварительно загрузить в соответствующие регистры до вызова подпрограммы, также следует учесть, что результат сложения не должен превышать максимального значения для наибольшего слагаемого, иначе мы получим неправильный результат. Например, при сложении двухбайтного и однобайтного числа, результат не должен превышать максимального значения для двухбайтного числа – 65535:

Для операции вычитания действуют те же правила, как в стандартном вычитании столбиком на бумаге, только здесь вместо переполнения идет проверка на наличие заема, при вычитании большего числа из меньшего. Рассмотрим вычитание однобайтного числа (tmpLL) из двухбайтного (varLH, varLL): сначала вычисляется разность младших регистров обоих чисел, из регистра varLL вычитаем единственный регистр однобайтного числа tmpLL. Выполняем проверку на наличие заема, с помощью того же бита C регистра STATUS. При отсутствии факта заема (C=1) ничего не делаем, выходим из подпрограммы. При наличии заема (C=0), декрементируем старший регистр двухбайтного числа varLH. В случае если число трехбайтное (varHL, varLH, varLL), надо также проверить факт наличия заема после декремента регистра varLH, только в этом случае применяем другую конструкцию, вместо декремента вычитаем единицу и снова проверяем заем с помощью бита C, так как командой декремента с условием (decfsz) проверить заем не получится. При наличии заема, декрементируем самый старший регистр varHL, в противном случае выходим из подпрограммы.

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

У этой записи 6 комментариев

  1. хм.. В AVR для сложения многобайтных чисел есть удобная команда – adc – сложение двух чисел с учетом флага переноса. Если предыдущее сложение закончилось переполнением, то перенос автоматически добавится к двум слагаемым.
    Например для четырех байтного сложения выйдет:
    add tempLL, varLL
    adc tempLH, varLH
    adc tempHL, varHL
    adc tempHH, varHH
    Но при сложении чисел разных размеров это не катит, и делать придется тоже самое что и в пиках ))

    1. По моему в AVR можно складывать числа разных размеров с учетом переноса, например, сложение четырехбайтного и двухбайтного числа:
      add varLL, tmpLL
      adc varLH, tmpLH
      adc var HL, nol
      adc varHH, nol
      где nol – регистр, куда предварительно записывается число 0

      В пиках для сложения есть только 2 команды: addwf – сложение содержимого аккумулятора с каким-либо регистром и addlw – сложение содержимого аккумулятора с константой, то же самое и для вычитания.
      Не забываем что в пиках всего 35 инструкций вместо сотни команд у AVR)

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

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

  3. для выделения временного регистра в аврках проблем ваааще нету, их там 32 штуки, и любой рег. можно в стек отправить, если что. пики это шляпа полная.

    1. В микроконтроллерах PIC регистры общего назначения это и есть ОЗУ, здесь нет подразделения, отдельно ОЗУ или отдельно регистры общего назначения. В любом контроллере имеется минимум от 64 байтов регистров общего назначения.

      В AVR все действия с ОЗУ и регистрами ввода/вывода необходимо выполнять обязательно через регистры общего назначения, а в PIC все регистры равнозначны, обращайся к любому напрямую и делай что хочешь.

      У любого контроллера есть преимущества и недостатки по сравнению с другими контроллерами, и говорить что одно шляпа, а другое круто, неправильно. Если писать на СИ, то это все вообще не имеет значения. И кстати AVR уже давно куплен компанией Microchip, которая как раз и является производителем (шляпных по вашему мнению) PIC контроллеров.

Имя (обязательно)Email (обязательно)Веб-сайт

Добавить комментарий