Autor Tema: Multiplicación y División en Assembler  (Leído 20895 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado SavageChicken

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 912
Multiplicación y División en Assembler
« en: 30 de Mayo de 2007, 14:40:31 »
Dado que este es un almacén de assembler, he querido aportar un granito de arena.
He visto que se pregunta repetidamente por esto y si bien hay algunas respuestas dejo un código bien explicado de una rutina de multiplicación y de división, espero les sirva:

Código: Microchip Assembler
  1. ;****** Debemos declarar las variables primero, estas son:
  2. ;***************************************************
  3. ;****** Factor_A
  4. ;****** Factor_B
  5. ;****** Result_H
  6. ;****** Result_L
  7. ;****** Dividendo_H
  8. ;****** Dividendo_L
  9. ;****** Divisor
  10. ;****** Cociente_H
  11. ;****** Cociente_L
  12. ;***************************************************
  13.  
  14. ;Se cargan Previamente en Factor_A y Factor_B los valores a multiplicar
  15. ;El resultado quedará almacenado en Result_H y Result_L
  16.  
  17. Multiplicar
  18.                 clrf    Result_L                        ;borramos para inicializar
  19.                 clrf    Result_H
  20.                 movf    Factor_B,F                      ;Si es cero...
  21.                 btfsc   STATUS,Z                        ;En Caso que el Factor "B" Sea Cero
  22.                 return                                          ;volver con resultado= 0
  23.                 movf    Factor_A,W                      ;cargamos el Factor "A"
  24. Mult_Bucle
  25.                 addwf   Result_L,F                      ;Sumamos "A", Tantas veces como sea "B"
  26.                 btfsc   STATUS,C                        ;Existe Acarreo?...
  27.                 incf    Result_H,F                      ;...Si. Incrementamos el byte Mayor
  28.                 decfsz  Factor_B,F                      ;Terminó la Multiplicación?...
  29.                 goto    Mult_Bucle                      ;... No, Seguimos haciendo sumas.
  30.                 return                                          ;Si. Volvemos a la rutina principal
  31.  
  32.  
  33. ;Se carga previamente en Dividendo_H y Dividendo_L el valor total del Dividendo
  34. ;y en Divisor el valor del divisor. El resultado quedará en Cociente_H y Cociente_L
  35. ;y el resto quedará en Dividendo_L
  36. ;(Nota: Dividendo_H y Dividendo_L serán borrados en el proceso de división)
  37.  
  38. Dividir
  39.                 clrf    Cociente_L                      ;Limpiamos el valor del resultado
  40.                 clrf    Cociente_H
  41. VerifDiv
  42.                 movfw   Dividendo_H                     ;Si la parte alta del Dividendo...
  43.                 btfss   STATUS,Z                        ;contiene algún valor...
  44.                 goto    PuedeDividir                    ;... Podemos dividir
  45.                 movfw   Divisor                         ;Si no es el caso...
  46.                 subwf   Dividendo_L,W                   ;...Verificamos que la parte baja del Dividendo...
  47.                 btfss   STATUS,C                        ;...sea mayor que el divisor
  48.                 return                                          ;En caso de ser menor, la división no se realiza
  49. PuedeDividir
  50.                 incf    Cociente_L,F                    ;Aumentamos el valor del cociente bajo...
  51.                 btfsc   STATUS,Z                        ;...en caso de llegar al máximo...
  52.                 incf    Cociente_H,F                    ;...aumentamos el cociente alto
  53.                 movfw   Divisor                         ;Cargo el valor del Divisor en W...
  54.                 subwf   Dividendo_L,F                   ;...y le resto al dividendo el valor del divisor
  55.                 btfsc   STATUS,C                        ;Si el valor es positivo...
  56.                 goto    VerifDiv                        ;...continuo con la división...
  57.                 decf    Dividendo_H,F                   ;...Si fue negativo decremento la parte alta del dividendo y...
  58.                 goto    VerifDiv                        ;...continuo con la división.
  59.  
« Última modificación: 30 de Mayo de 2007, 21:15:42 por SavageChicken »
No hay preguntas tontas...
Solo hay tontos que no preguntan.

Desconectado nietzche

  • PIC10
  • *
  • Mensajes: 14
Re: Multiplicación y División en Assembler
« Respuesta #1 en: 05 de Agosto de 2010, 03:24:12 »
excelentisimo, gracias man

Desconectado tapi8

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1506
Re: Multiplicación y División en Assembler
« Respuesta #2 en: 05 de Agosto de 2010, 12:54:54 »
Pues muchas gracias.

Desconectado agauss

  • PIC16
  • ***
  • Mensajes: 147
Re: Multiplicación y División en Assembler
« Respuesta #3 en: 24 de Agosto de 2010, 15:55:29 »
Y tendras algo en coma flotante...???
No es el conocimiento, sino el acto de aprendizaje, y no la posesión, sino el acto de llegar allí, que concede el mayor disfrute.
"Carl Friedrich Gauss"

El tacto es el arte de hacer un punto sin hacer un enemigo.
"Isaac Newton"

Desconectado TrebolTronik

  • PIC10
  • *
  • Mensajes: 1
Re:Multiplicación y División en Assembler
« Respuesta #4 en: 15 de Abril de 2017, 22:56:54 »
Muy bueno, se agradece.

Desconectado misterweb

  • PIC12
  • **
  • Mensajes: 90
Re:Multiplicación y División en Assembler
« Respuesta #5 en: 19 de Diciembre de 2017, 09:28:06 »
Repasando esta subrutina, veo que tiene algunos fallos, aparte de que no funciona por tener rutinas mal escritas como:

movfw   en vez de movwf

Una vez reparado todos esos fallos, a la hora de hacer la division no me sale de forma correcta.

Luego tiene cosas como esta rutina que son a la contra:

 movwf   Divisor                         ;Cargo el valor del Divisor en W..

Cuando en realidad lo que esta haciendo es cargar en Divisor lo que se encuentra en work.

Empieza la subrutina con

      movwf   Dividendo_H                     ;Si la parte alta del Dividendo...
      btfss   STATUS,Z       

Chequeando el STATUS sin previamente no haber echo nada.

Por mucho que la miro no entiendo muy bien el funcionamiento.


Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 6539
Re:Multiplicación y División en Assembler
« Respuesta #6 en: 19 de Diciembre de 2017, 10:29:50 »
No voy a opinar del codigo porque no lo use ni tampoco lo vi, pero voy a opinar sobre lo que pusiste

Citar
Repasando esta subrutina, veo que tiene algunos fallos, aparte de que no funciona por tener rutinas mal escritas como:

movfw   en vez de movwf

MOVFW xxx es una pseudo-instruccion soportada por MPASM lo que hace es un MOVF xxx, W

http://ww1.microchip.com/downloads/en/DeviceDoc/33014L.pdf
Pag. 320

El significado es muy distinto al de MOVWF, el cual es el inverso Uno de registro a W, y otro de W a Registro.
Personalmente, no me gustan las pseudo-instrucciones, pero cada uno programa como desea.

Citar
Luego tiene cosas como esta rutina que son a la contra:

 movwf   Divisor                         ;Cargo el valor del Divisor en W..

Cuando en realidad lo que esta haciendo es cargar en Divisor lo que se encuentra en work.

Empieza la subrutina con

      movwf   Dividendo_H                     ;Si la parte alta del Dividendo...
      btfss   STATUS,Z       

Chequeando el STATUS sin previamente no haber echo nada.

Mirando el codigo veo que lo cambiaste a MOVFW por MOVWF en ambas situaciones
Como ya te dije el MOVFW es un MOVF, si miras la tabla de instrucciones el MOVF actualiza la bandera Z cuando es ejecutada, es una forma simple de saber si el registro es 0 o no.

Tu error de creer que esta mal el MOVFW y corregirlo por otra cosa distinta hace que no funcione seguramente.
Cambia los MOVFW xxx por MOVF xxx, w y te va a funcionar.

EDITO:

Acabo de ver el codigo asi nomas por arriba, y con solo ver que se usa un ADDWF y un SUBWF, puedo decir con certeza que:

- La multiplicacion se realiza con sumas sucesivas, sumas segun la cantidad de veces que necesitas
- La division se realiza con restas sucesivas, restas hasta que no puedas restar mas.

La multiplicacion tiene una ventaja frente a factores pequeños, pero si un factor es grande se hace consumidor de tiempo, hay otras formas de multiplicar mediante rotaciones que hacen que sea fijo el tiempo de procesado de la multiplicacion

https://en.wikipedia.org/wiki/Binary_multiplier

Y sobre la division realmente es mas complicado. La otra es tener una tabla de 255 valores de ( 1/x ) y luego multiplicas. Otras formas:

https://en.wikipedia.org/wiki/Division_algorithm
« Última modificación: 19 de Diciembre de 2017, 10:47:06 por KILLERJC »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 6539
Re:Multiplicación y División en Assembler
« Respuesta #7 en: 19 de Diciembre de 2017, 12:00:16 »
Multiplicacion por shift. Seguramente se pueda optimizar mas, solo lo hice por hacerlo, ya que el trabajo esta pobre, entonces tengo tiempo.

A diferencia del codigo de SavageChicken este codigo destruye ambos valores en los registros FACTOR_A y B
Y usa un registro mas que es TEMP, pero que puede ser reutilizado en otro lado tranquilamente como un registro temporal.

Código: ASM
  1. MULTIPLICAR
  2.     CLRF    RES_H
  3.     CLRF    RES_L
  4.     CLRF    TEMP
  5.     MOVF    FACTOR_A, F
  6.     BTFSC   STATUS, Z
  7.     RETURN
  8.     MOVF    FACTOR_B, F
  9.     BTFSC   STATUS, Z
  10.     RETURN
  11.     RRF     FACTOR_A, F
  12.     GOTO    LOOP_M
  13. LOOP_A:
  14.     BCF     STATUS, C
  15.     RLF     FACTOR_B, F
  16.     RLF     TEMP, F
  17.     MOVF    FACTOR_A, F
  18.     BTFSC   STATUS, Z
  19.     RETURN
  20.     RRF     FACTOR_A, F
  21. LOOP_M:
  22.     BTFSS   STATUS, C
  23.     GOTO    LOOP_A
  24.     MOVF    FACTOR_B, W
  25.     ADDWF   RES_L, F
  26.     BTFSC   STATUS, C
  27.     INCF    RES_H, F
  28.     MOVF    TEMP, W
  29.     ADDWF   RES_H, F
  30.     GOTO    LOOP_A

Le toma unos 150 ciclos con el CALL/RETURN incluido en el peor caso que seria cuando FACTOR_A es 0xFF. Y 31 ciclos cuando FACTOR_A es 0x01

El programa de SavageChicken ocupa 15 ciclos para cuando FACTOR_B es 0x01 y 1539 ciclos cuando FACTOR_B es 0xFF. Por esto decia que aumenta bastante cuando los factores son grandes pero es bueno con factores pequeños.

Tambien en consideracion que el codigo de SavageChicken es mas corto, ocupa menos ROM

Se puede mejorar eligiendo el termino menor en uno de los factores, de esa forma siempre utiliza la menor cantidad de ciclos posibles. A partir de 0xA aproximadamente el codigo de multiplicacion por shift termina siendo mas rapido que el de sumas sucesivas
« Última modificación: 19 de Diciembre de 2017, 12:08:57 por KILLERJC »

Desconectado misterweb

  • PIC12
  • **
  • Mensajes: 90
Re:Multiplicación y División en Assembler
« Respuesta #8 en: 19 de Diciembre de 2017, 17:38:37 »
 No se con que compilara SavageChicken la subrutina pero a mi con con MPLABX V 3.61 estas rutinas me dan lo siguiente:


Warning[207] /mnt/sda1/PROYECTOS_1_3_2015/00_SUBRUTINAS_PIC/MATEMATICAS/DIVISOR.X/DVISION1.asm 136 : Found label after column 1. (movfw)

Con lo cual lo considera como una label y no como una funcion.

Y lo mismo para las 3 instrucciones movfw dentro de esta subrutina.

Pero si lo compilo con MPAB antiguo para Xp 8.63   lo mismo.

Muchas veces el lenguaje ensamblador de los PICs es un poco critico como para andar con tonterias.

Particularmente prefiero una subrutina que sea legible a una no legible hoy en dia con pics con 16 K de programa que una rutina ocupe 80 bytes a 200 byte pues no lo veo tan critico.
De cara a la velocidad si esta corriendo por ejemplo a una velocidad modesta de 1millon de instrucciones  de que tenga que ejecutar 200 o 1500 ciclos no lo veo tan critico, aunque pueda para algunas aplicaciones que haga un uso intensivo de ello puede que si.

Quiza es que este viviendo una epoca critica con los Pics en este momento hay otra cosa que me esta rayando..... que proximamente pondre en el foro....  En fin tengo que hacer mas cayo con ellos.

Un abrazo... KILLERJC .

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 6539
Re:Multiplicación y División en Assembler
« Respuesta #9 en: 19 de Diciembre de 2017, 20:37:37 »
Citar
Warning[207] /mnt/sda1/PROYECTOS_1_3_2015/00_SUBRUTINAS_PIC/MATEMATICAS/DIVISOR.X/DVISION1.asm 136 : Found label after column 1. (movfw)

Pasa el codigo que tenes... porque al menos a mi con el codigo copiado directamente:

Código: ASM
  1. #INCLUDE <P16F628A.INC>
  2.  
  3.     CBLOCK 0x70
  4.     Dividendo_H
  5.     Dividendo_L
  6.     Divisor
  7.     Cociente_L
  8.     Cociente_H
  9.     ENDC
  10.  
  11.     ORG   0000H
  12.  
  13. LOOP:
  14.     MOVLW 0x00
  15.     MOVWF Dividendo_H
  16.     MOVLW 0x0C
  17.     MOVWF Dividendo_L
  18.     MOVLW 0x4
  19.     MOVWF Divisor
  20.     CALL Dividir
  21.     GOTO    LOOP
  22.            
  23. FINISH:
  24.     NOP
  25.     GOTO FINISH
  26.  
  27. Dividir
  28.     clrf    Cociente_L                      ;Limpiamos el valor del resultado
  29.     clrf    Cociente_H
  30. VerifDiv
  31.     movfw   Dividendo_H                     ;Si la parte alta del Dividendo...
  32.     btfss   STATUS,Z                        ;contiene alg&#38;#250;n valor...
  33.     goto    PuedeDividir                    ;... Podemos dividir
  34.     movfw   Divisor                         ;Si no es el caso...
  35.     subwf   Dividendo_L,W                   ;...Verificamos que la parte baja del Dividendo...
  36.     btfss   STATUS,C                        ;...sea mayor que el divisor
  37.     return                                          ;En caso de ser menor, la divisi&#38;#243;n no se realiza
  38. PuedeDividir
  39.     incf    Cociente_L,F                    ;Aumentamos el valor del cociente bajo...
  40.     btfsc   STATUS,Z                        ;...en caso de llegar al m&#38;#225;ximo...
  41.     incf    Cociente_H,F                    ;...aumentamos el cociente alto
  42.     movfw   Divisor                         ;Cargo el valor del Divisor en W...
  43.     subwf   Dividendo_L,F                   ;...y le resto al dividendo el valor del divisor
  44.     btfsc   STATUS,C                        ;Si el valor es positivo...
  45.     goto    VerifDiv                        ;...continuo con la divisi&#38;#243;n...
  46.     decf    Dividendo_H,F                   ;...Si fue negativo decremento la parte alta del dividendo y...
  47.     goto    VerifDiv        
  48.    
  49.     END

El resultado es este:
Citar
CLEAN SUCCESSFUL (total time: 393ms)
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'F:/CCS/MPLABx/MPASM.X'
make  -f nbproject/Makefile-default.mk dist/default/production/MPASM.X.production.hex
make[2]: Entering directory 'F:/CCS/MPLABx/MPASM.X'
"C:\Program Files (x86)\Microchip\MPLABX\v4.01\mpasmx\mpasmx.exe" -q -p16f628a -l"build/default/production/main.lst" -e"build/default/production/main.err" -o"build/default/production/main.o" "main.asm"
"C:\Program Files (x86)\Microchip\MPLABX\v4.01\mpasmx\mplink.exe"    -p16f628a  -w  -m"dist/default/production/MPASM.X.production.map"   -z__MPLAB_BUILD=1  -odist/default/production/MPASM.X.production.cof  build/default/production/main.o     
MPLINK 5.08, LINKER
Device Database Version 1.38
Copyright (c) 1998-2011 Microchip Technology Inc.
Errors    : 0

MP2HEX 5.08, COFF to HEX File Converter
Copyright (c) 1998-2011 Microchip Technology Inc.
Errors    : 0

make[2]: Leaving directory 'F:/CCS/MPLABx/MPASM.X'
make[1]: Leaving directory 'F:/CCS/MPLABx/MPASM.X'

BUILD SUCCESSFUL (total time: 2s)
Loading code from F:/CCS/MPLABx/MPASM.X/dist/default/production/MPASM.X.production.hex...
Loading completed

MPLAB X v4.01 / MPASM v5.75

Seguramente algo mal estas escribiendo.

Citar
Particularmente prefiero una subrutina que sea legible a una no legible hoy en dia con pics con 16 K de programa que una rutina ocupe 80 bytes a 200 byte pues no lo veo tan critico.
De cara a la velocidad si esta corriendo por ejemplo a una velocidad modesta de 1millon de instrucciones  de que tenga que ejecutar 200 o 1500 ciclos no lo veo tan critico, aunque pueda para algunas aplicaciones que haga un uso intensivo de ello puede que si.

Entonces el ASM no es para lo que buscas, si no vas a buscar un punto de optimizacion grande, entonces no tiene sentido ir al ASM, es mejor ir a C. y hacer una multiplicacion con un simple asterisco, o una division con una simple barra. Y queda bien legible. Para lo unico que veo util el ASM hoy en dia es para:

- Conocer el funcionamiento del micro mas a fondo
- Optimizar alguna situacion en la que se requiera gran velocidad/espacio
- Debug cuando programas en otro lenguaje de alto nivel, y en caso muy extremo para llegar a este debug.

Vos mismo lo decis, los espacios de los micros, la velocidad de los mismos hace que sea insensato hoy en dia ponerse con ASM si no es para los puntos anteriores.

EDIT: En mi otro post que sigue, se cual es tu problema.
« Última modificación: 19 de Diciembre de 2017, 21:24:55 por KILLERJC »

Desconectado tsk

  • PIC16
  • ***
  • Mensajes: 241
Re:Multiplicación y División en Assembler
« Respuesta #10 en: 19 de Diciembre de 2017, 20:47:30 »
No veo porque ha de ser una tontería usar movfw. Si te está dando ese error con respecto que lo reconoce como etiqueta probablemente tengas algún caracter no imprimible, algo relacionado con la codificación de caracteres o incluso algún error con el IDE o con la inclusión de las bibliotecas del microcontrolador, si tener el código es algo difícil de adivinar.

Tampoco se con que está compilando SavageChicken sus rutinas, pero te puedo decir que, en mi caso particular (copiando y pegando el código) no me produce ningún error y usé tanto mpasmx y gpasm sin errores.




PD: KILLERJC tendremos una manía con el pic16f628(a) o cual fue la razón para que ambos lo elegimos para hacer la prueba  :lol:

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 6539
Re:Multiplicación y División en Assembler
« Respuesta #11 en: 19 de Diciembre de 2017, 21:18:22 »
PD: KILLERJC tendremos una manía con el pic16f628(a) o cual fue la razón para que ambos lo elegimos para hacer la prueba  :lol:

Es que tengo un proyecto para estas cosas, unicamente para probar codigos, y por ahi voy cambiando de PICs para amoldarme a los codigos, pero resulto que estaba ya el .inc del 16F628A entonces me fui con ese.

Seguramente y ahora me juego la cabeza el pueda tener problemas por tratar de usarlo con un PIC18, el cual no comento nada de en que micro se usaba, me di cuenta porque en otro post puso un codigo con instrucciones de PIC18. Usando un PIC18F4550 me da el warning que el comenta:

Citar
Warning[207] F:\CCS\MPLABX\MPASM.X\MAIN.ASM 32 : Found label after column 1. (movfw)

Ya que parece que no vio que en el PDF del MPASM, esas pseudo-instrucciones son para micros de instrucciones de 12/14-bits  ( 12-BIT/14-BIT INSTRUCTION WIDTH PSEUDO-INSTRUCTIONS ), mientras que el PIC18 las instrucciones son de 16-bits. Nunca pense que estuviera usando un PIC18, por eso no toque ese tema. Ya que el tema que abordo es que directamente no funcionaba. Y menos pensando que los PIC18 suelen tener multiplicadores por hardware.

En fin, todo esto me parecio que todo esto fue por pecar y no saber de las pseudo-instrucciones, y tampoco por no haber indentificado el PIC que estaba usando lo cual es muy importante para cuando uno habla de ASM, ya que las arquitecturas e incluso las instrucciones varian dentro de las mismas familias de micros.

« Última modificación: 19 de Diciembre de 2017, 21:26:48 por KILLERJC »

Desconectado misterweb

  • PIC12
  • **
  • Mensajes: 90
Re:Multiplicación y División en Assembler
« Respuesta #12 en: 20 de Diciembre de 2017, 16:55:53 »
Pues si efectivamente KILLERJC esta compilado para el pic18F4550, sera ese el problema, pero vamos tampoco me preocupa mucho, por que  no me interesa mucho utilizar pseudo-instrucciones.

En cualquier caso estoy en tratos con un brujo para que me pase unas llervas por encima...jejeje.

No la verdad es que no me he leido el manual MPASM en lo que ser refiere a los pseudo-instrucciones, pero vamos entre utilizar movfw  o utilizar movf xxx,w pa que me voy a complicar la vida, siempre voy a utilizar un codigo que pueda trasladar de forma lo mas facil posible de un microcontrolador a otro, pero entiendo que esto es personal y va en gustos de cada uno.

Muchas gracias por vuestras enseñanzas ..... un placer aprender con vosotros.




« Última modificación: 20 de Diciembre de 2017, 17:00:07 por misterweb »