Какой подход к умножению двух байтов лучше, используя только сдвиг битов и сложение?

Исходный вопрос:

Группа из нас (студенты-электронщики – Великобритания) недавно в свое время освоили программирование микроконтроллера PIC16F84A. Возникла необходимость перемножить два 8-битных числа, не зная минимального/максимального значения для каждого из них. Однокурсник предложил следующую идею.

multiply_numbers:
; Takes numbers in Num1 and Num2, and returns product in OutH:OutL
    clrf    OutH            ; clear all non-input variables
    clrf    OutL
mult_loop
    bcf     STATUS,c        ; clear carry bit
    movfw   Num2
    addwf   OutL            ; add Num2 to OutL
    btfsc   STATUS,c        ; check carry bit
    incf    OutH            ; if set, increment OutH
    decfsz  Num1            ; decrement Num1
    goto    mult_loop       ; if Num1 is not zero, repeat loop
    return                  ; else return

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

N = a_0 + a_1*2 + a_2* 2^2 + a_3*2^3 + ... + a_7*2^7

Исходя из этого, я придумал метод умножения двух 8-битных чисел для получения 16-битного результата (хранящегося в два 8-битных регистра).

multiply_numbers:
; Takes numbers in Num1 and Num2L, and returns product in OutH:OutL
    clrf    Num2H           ; clear all non-input variables
    clrf    OutL
    clrf    OutH
mult_loop
    btfsc   Num1,0          ; test LSB of Num1
    call    add_num16       ; if set, add Num2H:Num2L to OutH:OutL
    call    shift_left      ; shift Num2H:Num2L left (multiply by 2)
    rrf     Num1,f          ; shift Num1 right
    clrw                    ; clear working register (0x00)
    bcf     STATUS,z        ; clear zero bit (3) of the STATUS register
    addwf   Num1,w          ; add 0x00 to Num1
    btfss   STATUS,z        ; if Num1 is zero, then exit loop
    goto    mult_loop       ; else, continue with another iteration
    return
add_num16
    movfw   Num2H
    addwf   OutH,f          ; add Num2H to OutH
    bcf     STATUS,c        ; clear carry bit (0) of the STATUS register
    movfw   Num2L
    addwf   OutL,f          ; add Num2L to OutL
    btfsc   STATUS,c        ; check carry bit
    incf    OutH,f          ; increment OutH if set (OutL overflowed)
    return
shift_left
    bcf     STATUS,c        ; clear carry bit
    rlf     Num2L,f         ; rotate Num2L left (carry -> LSB, MSB -> carry)
    rlf     Num2H,f         ; rotate Num2H left, using carry bit from Num2L
    return

Я думаю, что этот второй пример в большинстве случаев быстрее, просто потому, что цикл будет повторяться только до 8 раз, а не до 256 раз.

Правильно ли я предполагаю их относительную скорость/эффективность? И действительно ли второй блок кода работает так, как я задумал (есть ли какие-либо потенциальные проблемы с ним, которые я пропустил)? Наконец, можно ли дополнительно оптимизировать это умножение с помощью еще не использовавшихся методов?

Заранее благодарю.

П.С. Все переменные/регистры были правильно определены со своими собственными адресами. Обширное комментирование кода связано с тем, что мы пытаемся скомпилировать набор подпрограмм, к которым мы можем обращаться в будущем и по-прежнему с первого взгляда знать, что происходит и почему.

П.П.С. Этот вопрос связан с личным / хобби интересом к программированию этой картинки и не имеет ничего общего с какой-либо текущей курсовой работой и т. Д.Просто чтобы рассеять любые подозрения, которые у вас могли быть!


Микроконтроллер: PIC16F84A
Среда разработки: MPLABX IDE v1.10
Компилятор: mpasm (v5.43)


Правка №1:

  • Вместо проверки младшего бита числа Num1 и добавления сдвинутого влево числа Num2H:Num2L к OutH:OutL проверьте старший бит числа Num1 и добавьте число2 слева -смещено OutH:OutL.
  • Сделать 'shift_left' встроенным, а не вызываемой подфункцией.
  • Разверните 'mult_loop' для оптимизации 8-й итерации.

Метод 2 улучшен:

multiply_numbers:
; Takes numbers in Num1 and Num2, and returns product in OutH:OutL
    clrf    OutL            ; clear all non-input variables
    clrf    OutH
; 1st iteration
    btfsc   Num1,7          ; test MSB of Num1
    call    add_num8        ; if set, add Num2 to OutH:OutL
    bcf     STATUS,c        ; clear carry bit
    rlf     OutL,f          ; rotate OutL left (carry -> LSB, MSB -> carry)
    rlf     OutH,f          ; rotate OutH left, using carry bit from OutL
    rlf     Num1,f          ; shift Num1 left
; 2nd iteration
    btfsc   Num1,7
    call    add_num8
    bcf     STATUS,c
    rlf     OutL,f
    rlf     OutH,f
    rlf     Num1,f
; 3rd iteration
    btfsc   Num1,7
    call    add_num8
    bcf     STATUS,c
    rlf     OutL,f
    rlf     OutH,f
    rlf     Num1,f
; 4th iteration
    btfsc   Num1,7
    call    add_num8
    bcf     STATUS,c
    rlf     OutL,f
    rlf     OutH,f
    rlf     Num1,f
; 5th iteration
    btfsc   Num1,7
    call    add_num8
    bcf     STATUS,c
    rlf     OutL,f
    rlf     OutH,f
    rlf     Num1,f
; 6th iteration
    btfsc   Num1,7
    call    add_num8
    bcf     STATUS,c
    rlf     OutL,f
    rlf     OutH,f
    rlf     Num1,f
; 7th iteration
    btfsc   Num1,7
    call    add_num8
    bcf     STATUS,c
    rlf     OutL,f
    rlf     OutH,f
    rlf     Num1,f
; 8th iteration
    btfss   Num1,7          ; test MSB of Num1
    return                  ; if not set, then return. else...
add_num8
    bcf     STATUS,c        ; clear carry bit (0) of the STATUS register
    movfw   Num2
    addwf   OutL,f          ; add Num2L to OutL
    btfsc   STATUS,c        ; check carry bit
    incf    OutH,f          ; increment OutH if set (OutL overflowed)
    return
6
задан Community 23 May 2017 в 12:27
поделиться