Иногда при написании программ требуется выполнить простые арифметические операции с многобайтными числами. С однобайтными числами все просто, сложение и вычитание выполняются стандартными командами 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
;Подпрограмма сложения двухбайтного и однобайтного числа ;Двухбайтное число предварительно загружается в регистры varLH (старший байт) и varLL (младший байт) ;Однобайтное число предварительно загружается в регистр tmpLL ;Результат сложения в регистрах varLH и varLL, результат не должен превышать максимального ;значения для двухбайтного числа (65535) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfsc STATUS,C ;проверка переполнения регистра varLL incf varLH,F ;переполнение varLL: инкремент регистра varLH return ;нет переполнения varLL: выход из подпрограммы ;Подпрограмма сложения трехбайтного и однобайтного числа ;Трехбайтное число предварительно загружается в регистры varHL, varLH, varLL ;Однобайтное число предварительно загружается в регистр tmpLL ;Результат сложения в регистрах varHL, varLH, varLL, результат не должен превышать максимального ;значения для трехбайтного числа (16777215) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка переполнения регистра varLL return ;нет переполнения varLL: выход из подпрограммы incfsz varLH,F ;переполнение varLL: инкремент varLH с проверкой на переполнение return ;нет переполнения varLH: выход из подпрограммы incf varHL,F ;переполнение varLH: инкремент varHL return ;выход из подпрограммы ;Подпрограмма сложения двухбайтных чисел ;Первое число предварительно загружается в регистры varLH, varLL ;Второе число предварительно загружается в регистр tmpLH, tmpLL ;Результат сложения в регистрах varLH, varLL, результат не должен превышать максимального ;значения для для двухбайтного числа (65535) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка переполнения регистра varLL goto s1 ;нет переполнения varLL: переход на метку s1 incf varLH,F ;переполнение varLL: инкремент varLH s1 movf tmpLH,W ;прибавление числа из регистра tmpLH к числу addwf varLH,F ;в регистре varLH return ;выход из подпрограммы ;Подпрограмма сложения трехбайтного и двухбайтного числа ;Трехбайтное число предварительно загружается в регистры varHL, varLH, varLL ;Двухбайтное число предварительно загружается в регистры tmpLH, tmpLL ;Результат сложения в регистрах varHL, varLH, varLL, результат не должен превышать максимального ;значения для трехбайтного числа (16777215) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка переполнения регистра varLL goto s1 ;нет переполнения varLL: переход на метку s1 incfsz varLH,F ;переполнение varLL: инкремент varLH с проверкой на переполнение goto s1 ;нет переполнения varLH: переход на метку s1 incf varHL,F ;переполнение varLH: инкремент varHL s1 movf tmpLH,W ;прибавление числа из регистра tmpLH к числу addwf varLH,F ;в регистре varLH btfsc STATUS,C ;проверка переполнения регистра varLH incf varHL,F ;переполнение varLH: инкремент varHL return ;нет переполнения varLH: выход из подпрограммы ;Подпрограмма сложения четырехбайтного и двухбайтного числа ;Четырехбайтное число предварительно загружается в регистры varHH, varHL, varLH, varLL ;Двухбайтное число предварительно загружается в регистры tmpLH, tmpLL ;Результат сложения в регистрах varHH, varHL, varLH, varLL, результат не должен превышать ;максимального значения для четырехбайтного числа (4294967295) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка переполнения регистра varLL goto s1 ;нет переполнения varLL: переход на метку s1 incfsz varLH,F ;переполнение varLL: инкремент varLH с проверкой на переполнение goto s1 ;нет переполнения varLH: переход на метку s1 incfsz varHL,F ;переполнение varLH: инкремент varHL с проверкой на переполнение goto s1 ;нет переполнения varHL: переход на метку s1 incf varHH,F ;переполнение varHL: инкремент varHH s1 movf tmpLH,W ;прибавление числа из регистра tmpLH к числу addwf varLH,F ;в регистре varLH btfss STATUS,C ;проверка переполнения регистра varLH return ;нет переполнения varLH: выход из подпрограммы incfsz varHL,F ;переполнение varLH: инкремент varHL с проверкой на переполнение return ;нет переполнения varHL: выход из подпрограммы incf varHH,F ;переполнение varHL: инкремент varHH return ;выход из подпрограммы ;Подпрограмма сложения трехбайтных чисел ;Первое число предварительно загружается в регистры varHL, varLH, varLL ;Второе число предварительно загружается в регистр tmpHL, tmpLH, tmpLL ;Результат сложения в регистрах varHL, varLH, varLL, результат не должен превышать ;максимального значения для трехбайтного числа (16777215) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка переполнения регистра varLL goto s1 ;нет переполнения varLL: переход на метку s1 incfsz varLH,F ;переполнение varLL: инкремент varLH с проверкой на переполнение goto s1 ;нет переполнения varLH: переход на метку s1 incf varHL,F ;переполнение varLH: инкремент varHL s1 movf tmpLH,W ;прибавление числа из регистра tmpLH к числу addwf varLH,F ;в регистре varLH btfsс STATUS,C ;проверка переполнения регистра varLH incf varHL,F ;переполнение varLH: инкремент varHL movf tmpHL,W ;нет переполнения varLH: прибавление числа из регистра tmpHL addwf varHL,F ;к числу в регистре varHL return ;выход из подпрограммы ;Подпрограмма сложения четырехбайтных чисел ;Первое число предварительно загружается в регистры varHH, varHL, varLH, varLL ;Второе число предварительно загружается в регистр tmpHH, tmpHL, tmpLH, tmpLL ;Результат сложения в регистрах varHH, varHL, varLH, varLL, результат не должен превышать ;максимального значения для четырехбайтного числа (4294967295) sum movf tmpLL,W ;прибавление числа из регистра tmpLL к числу addwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка переполнения регистра varLL goto s1 ;нет переполнения varLL: переход на метку s1 incfsz varLH,F ;переполнение varLL: инкремент varLH с проверкой на переполнение goto s1 ;нет переполнения varLH: переход на метку s1 incfsz varHL,F ;переполнение varLH: инкремент varHL с проверкой на переполнение goto s1 ;нет переполнения varHL: переход на метку s1 incf varHH,F ;переполнение varHL: инкремент varHH s1 movf tmpLH,W ;прибавление числа из регистра tmpLH к числу addwf varLH,F ;в регистре varLH btfss STATUS,C ;проверка переполнения регистра varLH goto s2 ;нет переполнения varLH: переход на метку s2 incfsz varHL,F ;переполнение varLH: инкремент varHL с проверкой на переполнение goto s2 ;нет переполнения varHL: переход на метку s2 incf varHH,F ;переполнение varHL: инкремент varHH s2 movf tmpHL,W ;прибавление числа из регистра tmpHL к числу addwf varHL,F ;в регистре varHL btfsc STATUS,C ;проверка переполнения регистра varHL incf varHH,F ;переполнение varHL: инкремент varHH movf tmpHH,W ;нет переполнения varHL: прибавление числа из регистра tmpHH addwf varHH,F ;к числу в регистре varHH return ;выход из подпрограммы ; |
Для операции вычитания действуют те же правила, как в стандартном вычитании столбиком на бумаге, только здесь вместо переполнения идет проверка на наличие заема, при вычитании большего числа из меньшего. Рассмотрим вычитание однобайтного числа (tmpLL) из двухбайтного (varLH, varLL): сначала вычисляется разность младших регистров обоих чисел, из регистра varLL вычитаем единственный регистр однобайтного числа tmpLL. Выполняем проверку на наличие заема, с помощью того же бита C регистра STATUS. При отсутствии факта заема (C=1) ничего не делаем, выходим из подпрограммы. При наличии заема (C=0), декрементируем старший регистр двухбайтного числа varLH. В случае если число трехбайтное (varHL, varLH, varLL), надо также проверить факт наличия заема после декремента регистра varLH, только в этом случае применяем другую конструкцию, вместо декремента вычитаем единицу и снова проверяем заем с помощью бита C, так как командой декремента с условием (decfsz) проверить заем не получится. При наличии заема, декрементируем самый старший регистр varHL, в противном случае выходим из подпрограммы.
Для многобайтных чисел все также, сначала вычисляется разность младших регистров с проверкой заема и последующим уменьшением значения старших регистров на единицу, если это необходимо (при наличии факта заема). Данная операция производится только для регистров большего числа, из которого вычитаем меньшее число. Далее вычисляется разность старших регистров с такой же проверкой заема, и т.д. Ниже представлены коды подпрограмм для различных вариантов вычитания, где следует соблюдать условие положительного результата, всегда из большего числа вычитаем меньшее число, иначе получим неверный результат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
;Подпрограмма вычитания однобайтного числа из двухбайтного ;Двухбайтное число предварительно загружается в регистры varLH (старший байт) и varLL (младший байт) ;Однобайтное число предварительно загружается в регистр tmpLL ;Результат вычитания в регистрах varLH и varLL, результат не должен быть отрицательным, из ;большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка на факт заема decf varLH,F ;возник факт заема: декремент регистра varLH return ;нет заема: выход из подпрограммы ;Подпрограмма вычитания однобайтного числа из трехбайтного ;Трехбайтное число предварительно загружается в регистры varHL, varLH, varLL ;Однобайтное число предварительно загружается в регистр tmpLL ;Результат вычитания в регистрах varHL, varLH, varLL, результат не должен быть отрицательным, из ;большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfsc STATUS,C ;проверка на факт заема return ;нет заема: выход из подпрограммы movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varLH,F ;в регистре varLH btfss STATUS,C ;проверка на факт заема decf varHL,F ;возник факт заема: декремент регистра varHL return ;нет заема: выход из подпрограммы ;Подпрограмма вычитания двухбайтных чисел ;Первое число предварительно загружается в регистры varLH, varLL ;Второе число предварительно загружается в регистр tmpLH, tmpLL ;Результат вычитания в регистрах varLH, varLL, результат не должен быть отрицательным, из ;большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfss STATUS,C ;проверка на факт заема decf varLH,F ;возник факт заема: декремент регистра varLH movf tmpLH,W ;нет заема: вычитаем число лежащее в регистре tmpLH из subwf varLH,F ;числа в регистре varLH return ;выход из подпрограммы ;Подпрограмма вычитания двухбайтного числа из трехбайтного ;Трехбайтное число предварительно загружается в регистры varHL, varLH, varLL ;Двухбайтное число предварительно загружается в регистры tmpLH и tmpLL ;Результат вычитания в регистрах varHL, varLH, varLL, результат не должен быть отрицательным, из ;большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfsc STATUS,C ;проверка на факт заема goto r1 ;нет заема: переход на метку r1 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varLH,F ;в регистре varLH btfss STATUS,C ;проверка на факт заема decf varHL,F ;возник факт заема: декремент регистра varHL r1 movf tmpLH,W ;нет заема: вычитаем число лежащее в регистре tmpLH из subwf varLH,F ;числа в регистре varLH btfss STATUS,C ;проверка на факт заема decf varHL,F ;возник факт заема: декремент регистра varHL return ;нет заема: выход из подпрограммы ;Подпрограмма вычитания двухбайтного числа из четырехбайтного ;Четырехбайтное число предварительно загружается в регистры varHH, varHL, varLH, varLL ;Двухбайтное число предварительно загружается в регистры tmpLH и tmpLL ;Результат вычитания в регистрах varHH, varHL, varLH, varLL, результат не должен быть отрицательным, ;из большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfsc STATUS,C ;проверка на факт заема goto r1 ;нет заема: переход на метку r1 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varLH,F ;в регистре varLH btfsc STATUS,C ;проверка на факт заема goto r1 ;нет заема: переход на метку r1 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varHL,F ;в регистре varHL btfss STATUS,C ;проверка на факт заема decf varHH,F ;возник факт заема: декремент регистра varHH r1 movf tmpLH,W ;нет заема: вычитаем число лежащее в регистре tmpLH из subwf varLH,F ;числа в регистре varLH btfsc STATUS,C ;проверка на факт заема return ;нет заема: выход из подпрограммы movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varHL,F ;в регистре varHL btfss STATUS,C ;проверка на факт заема decf varHH,F ;возник факт заема: декремент регистра varHH return ;выход из подпрограммы ;Подпрограмма вычитания трехбайтных чисел ;Первое число предварительно загружается в регистры varHL, varLH, varLL ;Второе число предварительно загружается в регистр tmpHL, tmpLH, tmpLL ;Результат вычитания в регистрах varHL, varLH, varLL, результат не должен быть отрицательным, ;из большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfsc STATUS,C ;проверка на факт заема goto r1 ;нет заема: переход на метку r1 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varLH,F ;в регистре varLH btfss STATUS,C ;проверка на факт заема decf varHL,F ;возник факт заема: декремент регистра varHL r1 movf tmpLH,W ;нет заема: вычитаем число лежащее в регистре tmpLH из subwf varLH,F ;числа в регистре varLH btfss STATUS,C ;проверка на факт заема decf varHL,F ;возник факт заема: декремент регистра varHL movf tmpHL,W ;нет заема: вычитаем число лежащее в регистре tmpHL из subwf varHL,F ;числа в регистре varHL return ;выход из подпрограммы ;Подпрограмма вычитания четырехбайтных чисел ;Первое число предварительно загружается в регистры varHH, varHL, varLH, varLL ;Второе число предварительно загружается в регистр tmpHH, tmpHL, tmpLH, tmpLL ;Результат вычитания в регистрах varHH, varHL, varLH, varLL, результат не должен быть отрицательным, ;из большего числа вычитаем меньшее razn movf tmpLL,W ;вычитаем число лежащее в регистре tmpLL из числа subwf varLL,F ;в регистре varLL btfsc STATUS,C ;проверка на факт заема goto r1 ;нет заема: переход на метку r1 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varLH,F ;в регистре varLH btfsc STATUS,C ;проверка на факт заема goto r1 ;нет заема: переход на метку r1 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varHL,F ;в регистре varHL btfss STATUS,C ;проверка на факт заема decf varHH,F ;возник факт заема: декремент регистра varHH r1 movf tmpLH,W ;нет заема: вычитаем число лежащее в регистре tmpLH из subwf varLH,F ;числа в регистре varLH btfsc STATUS,C ;проверка на факт заема goto r2 ;нет заема: переход на метку r2 movlw .1 ;возник факт заема: вычитаем единицу из числа subwf varHL,F ;в регистре varHL btfss STATUS,C ;проверка на факт заема decf varHH,F ;возник факт заема: декремент регистра varHH r2 movf tmpHL,W ;нет заема: вычитаем число лежащее в регистре tmpHL из subwf varHL,F ;числа в регистре varHL btfss STATUS,C ;проверка на факт заема decf varHH,F ;возник факт заема: декремент регистра varHH movf tmpHH,W ;нет заема: вычитаем число лежащее в регистре tmpHH из subwf varHH,F ;числа в регистре varHH return ;выход из подпрограммы ; |
хм.. В AVR для сложения многобайтных чисел есть удобная команда – adc – сложение двух чисел с учетом флага переноса. Если предыдущее сложение закончилось переполнением, то перенос автоматически добавится к двум слагаемым.
Например для четырех байтного сложения выйдет:
add tempLL, varLL
adc tempLH, varLH
adc tempHL, varHL
adc tempHH, varHH
Но при сложении чисел разных размеров это не катит, и делать придется тоже самое что и в пиках ))
По моему в AVR можно складывать числа разных размеров с учетом переноса, например, сложение четырехбайтного и двухбайтного числа:
add varLL, tmpLL
adc varLH, tmpLH
adc var HL, nol
adc varHH, nol
где nol – регистр, куда предварительно записывается число 0
В пиках для сложения есть только 2 команды: addwf – сложение содержимого аккумулятора с каким-либо регистром и addlw – сложение содержимого аккумулятора с константой, то же самое и для вычитания.
Не забываем что в пиках всего 35 инструкций вместо сотни команд у AVR)
Так можно делать, если не жалко выделять лишний регистр под хранение нуля. Все зависит от сложности программы, и иногда каждый регистр бывает на счету.
Красавцы математики, программки классные, для собственного использования то что надо, Спасибо за нужную инфу.
для выделения временного регистра в аврках проблем ваааще нету, их там 32 штуки, и любой рег. можно в стек отправить, если что. пики это шляпа полная.
В микроконтроллерах PIC регистры общего назначения это и есть ОЗУ, здесь нет подразделения, отдельно ОЗУ или отдельно регистры общего назначения. В любом контроллере имеется минимум от 64 байтов регистров общего назначения.
В AVR все действия с ОЗУ и регистрами ввода/вывода необходимо выполнять обязательно через регистры общего назначения, а в PIC все регистры равнозначны, обращайся к любому напрямую и делай что хочешь.
У любого контроллера есть преимущества и недостатки по сравнению с другими контроллерами, и говорить что одно шляпа, а другое круто, неправильно. Если писать на СИ, то это все вообще не имеет значения. И кстати AVR уже давно куплен компанией Microchip, которая как раз и является производителем (шляпных по вашему мнению) PIC контроллеров.