;**********************************************************************
; *
; Filename: Multiplicar.asm *
; Date: 30/10/2010 *
; File Version: 1.1 *
; *
; Author: Ruben Arranz *
; Company: Electronics Design *
; *
; *
;**********************************************************************
; *
; Files required: *
; 16F648A.lkr *
; *
; *
;**********************************************************************
; *
; Notes: *
; *
; *
; *
; *
;**********************************************************************
list p=16F648A ; list directive to define processor
#include <p16F648A.inc> ; processor specific variable definitions
errorlevel -302 ; suppress message 302 from list file
__CONFIG _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT
; Para numeros de 32 bits, necesitamos colocar el resultado en un numero de 64 bits es decir 8 Registros de Memoria
Resultado_B7 equ 0x20 ; Byte mas significativo del Resultado
Resultado_B6 equ 0x21
Resultado_B5 equ 0x22
Resultado_B4 equ 0x23
Resultado_B3 equ 0x24
Resultado_B2 equ 0x25
Resultado_B1 equ 0x26
Resultado_B0 equ 0x27 ; Byte menos significativo del Resultado
ValInt_B7 equ 0x28 ; Byte mas significativo del Valor Intermedio
ValInt_B6 equ 0x29
ValInt_B5 equ 0x2A
ValInt_B4 equ 0x2B
ValInt_B3 equ 0x2C
ValInt_B2 equ 0x2D
ValInt_B1 equ 0x2E
ValInt_B0 equ 0x2F ; Byte menos significativo del Valor Intermedio
Factor_A_B3 equ 0x30 ; Byte mas significativo del Factor A
Factor_A_B2 equ 0x31
Factor_A_B1 equ 0x32
Factor_A_B0 equ 0x33 ; Byte menos significativo del Factor A
Factor_B_B3 equ 0x34 ; Byte mas significativo del Factor B
Factor_B_B2 equ 0x35
Factor_B_B1 equ 0x36
Factor_B_B0 equ 0x37 ; Byte menos significativo del Factor B
Coma_R equ 0x38 ; Posicion de la coma del Resultado
Coma_A equ 0x39 ; Posicion de la coma del Factor A
Coma_B equ 0x3A ; Posicion de la coma del Factor B
Contador equ 0x3B ; Esta variable sera el contador de los desplazamientos que debemos hacer
Auxiliar_A equ 0x03C ; Variable Auxiliar para propositos generales
Auxiliar_B equ 0x03D ; Variable Auxiliar para propositos generales
Auxiliar_C equ 0x03E ; Variable Auxiliar para propositos generales
Auxiliar_D equ 0x03F ; Variable Auxiliar para propositos generales
ResDec_9 equ 0x040
ResDec_8 equ 0x041
ResDec_7 equ 0x042
ResDec_6 equ 0x043
ResDec_5 equ 0x044
ResDec_4 equ 0x045
ResDec_3 equ 0x046
ResDec_2 equ 0x047
ResDec_1 equ 0x048
ResDec_0 equ 0x049
ResASC_20 equ 0x050
ResASC_19 equ 0x051
ResASC_18 equ 0x052
ResASC_17 equ 0x053
ResASC_16 equ 0x054
ResASC_15 equ 0x055
ResASC_14 equ 0x056
ResASC_13 equ 0x057
ResASC_12 equ 0x058
ResASC_11 equ 0x059
ResASC_10 equ 0x05A
ResASC_9 equ 0x05B
ResASC_8 equ 0x05C
ResASC_7 equ 0x05D
ResASC_6 equ 0x05E
ResASC_5 equ 0x05F
ResASC_4 equ 0x060
ResASC_3 equ 0x061
ResASC_2 equ 0x062
ResASC_1 equ 0x063
ResASC_0 equ 0x064
Espacio equ 0x065
;**********************************************************************
RESET_VECTOR CODE 0x000 ;Vector de Reset del Procesador
goto Multiplicar
VECT_INT CODE 0x004 ;Vector de Interrupcion del Procesador
goto Multiplicar
MAIN CODE 0x005
Multiplicar
; Me gusta partir explicandome a mi mismo lo que quiero hacer, y luego realizar las lineas de comando
; En pocas palabras vamos a tomar el valor almacenado en Factor A y lo desplazaremos a la izquierda tantas veces...
; ...como digitos binarios tenga el Factor B, este desplazamiento quedara almacenado en Valor Intermedio, en cada...
; ..ocacion que el digito binario analizado del factor B sea "1" sumaremos el valor intermedio al Resultado
; No tendremos en consideracion cual de los dos factores es mayor, siempre se tomara el valor del factor A...
; ... para hacer el desplazamiento y se analizara el factor B para hacer las sumas.
; Comenzamos limpiando las variables que usaremos
clrf Resultado_B0
clrf Resultado_B1
clrf Resultado_B2
clrf Resultado_B3
clrf Resultado_B4
clrf Resultado_B5
clrf Resultado_B6
clrf Resultado_B7
clrf ValInt_B0
clrf ValInt_B1
clrf ValInt_B2
clrf ValInt_B3
clrf ValInt_B4
clrf ValInt_B5
clrf ValInt_B6
clrf ValInt_B7
clrf Contador
clrf Auxiliar_A
clrf Auxiliar_B
movlw 0x30
movwf Espacio
; Esta rutina considera que las variables Factor_A y Factor_B fueron previamente cargadas
; Siendo que en virtud del valor de B se realizaran las sumas sucesivas, intentaremos ver cual es el bit mas significativo...
; ...de este factor para no hacer loops innecesarios.
; Esto evitara que hagamos por ejemplo 32 loops cuando estamos multiplicando numeros pequeños.
movfw Factor_B_B3 ; Me fijo si hay algun bit en "1" en el byte mas significativo
movwf Auxiliar_A ; Aprovecho la pasada para guardar este dato en la Variable Auxiliar
btfsc STATUS,Z ; La existencia de algun bit en "1" la puedo detectar en el valor Z del Status
goto TestByte2 ; No habiendo ninguno bit en "1" sigo con el siguiente byte
movlw 0x18 ; Habiendo algun bit en "1" el "0x18" corresponde al valor minimo de loops que debere hacer
movwf Contador ; Almaceno este dato en el Contador
goto DeterminaLoops ; Vamos a ver en forma mas detallada cuantos loops deberemos hacer
TestByte2 movfw Factor_B_B2 ; Me fijo si hay algun bit en "1" en el byte siguiente
movwf Auxiliar_A ; Aprovecho la pasada para guardar este dato en la Variable Auxiliar
btfsc STATUS,Z ; La existencia de algun bit en "1" la puedo detectar en el valor Z del Status
goto TestByte1 ; No habiendo ninguno bit en "1" sigo con el siguiente byte
movlw 0x10 ; Habiendo algun bit en "1" el "0x10" corresponde al valor minimo de loops que debere hacer
movwf Contador ; Almaceno este dato en el Contador
goto DeterminaLoops ; Vamos a ver en forma mas detallada cuantos loops deberemos hacer
TestByte1 movfw Factor_B_B1 ; Me fijo si hay algun bit en "1" en el byte siguiente
movwf Auxiliar_A ; Aprovecho la pasada para guardar este dato en la Variable Auxiliar
btfsc STATUS,Z ; La existencia de algun bit en "1" la puedo detectar en el valor Z del Status
goto TestByte0 ; No habiendo ninguno bit en "1" sigo con el siguiente byte
movlw 0x08 ; Habiendo algun bit en "1" el "0x08" corresponde al valor minimo de loops que debere hacer
movwf Contador ; Almaceno este dato en el Contador
goto DeterminaLoops ; Vamos a ver en forma mas detallada cuantos loops deberemos hacer
TestByte0 movfw Factor_B_B0 ; En este caso veremos solo el detalle del byte
movwf Auxiliar_A ; Guardamos este dato en la Variable Auxiliar
DeterminaLoops movlw 0x08 ; En la ultima etapa, del valor maximo de loops probables ("0x08")...
movwf Auxiliar_B ; ...deberemos descontarle al contador de loops una vuelta por cada bit en "0"...
DetLoop_A rlf Auxiliar_A,f ; ...y deteniendonos en el primer bit en "1", esto visto desde el bit de mayor peso hacia abajo
btfsc STATUS,C ; Por eso vamos sacando los bit de mayor peso y chequeamos si estan en "1"
goto SumaLoop ; Si esta en "1" paramos de decrementar
decfsz Auxiliar_B,f ; Por el contrario si tenemos un "0" decrementamos el valor que se le sumara al contador...
goto DetLoop_A ; ...y volvemos para chequear el siguiente bit
SumaLoop movfw Auxiliar_B ; Finalmente ponemos en el contador el valor de loops...
addwf Contador,f ; Que deberan realizarse para efectuar el producto
; Si lo preferimos podemos cargar el contador con los 32 loops, esto ahorrara memoria en el PIC...
; ...Pero tomara mas ciclos la ejecucion total de la multiplicacion, la decicion es nuestra.
; movlw 0x20 ; Cargo el Contador de loops
; movwf Contador
; Bien llego el momento de multiplicar
; En primera instancia, pasamos el Factor A a la variable ValInt
movfw Factor_A_B0
movwf ValInt_B0
movfw Factor_A_B1
movwf ValInt_B1
movfw Factor_A_B2
movwf ValInt_B2
movfw Factor_A_B3
movwf ValInt_B3
; Luego generamos el loop
; La idea es ir testeando uno a uno los bits del Factor B, para descubrir si son "1" o "0"
; Del bit menos significativo al mas significativo
LoopMultip bcf STATUS,C ; Utilizamos el bit de acarreo para testear esto, por eso primero lo ponemos a"0"
rrf Factor_B_B3,f ; Rotamos ahora los 4 bytes
rrf Factor_B_B2,f
rrf Factor_B_B1,f
rrf Factor_B_B0,f
btfsc STATUS,C ; No fijamos que salio en el ultimo bit
call SumarFactor_A ; Si hay un uno llamamos a la rutina de Suma...
call RotaFactor_A ; ... mientras que en cualquier caso "1" o "0" debemos rotar el factor A
decfsz Contador,f ; Finalmente decrementamos el contador hasta llegar a "0"
goto LoopMultip ; Si todavia no llegamos a "0" nos mantenemos en este loop
; Si se requiere convertimos la cifra a un valor decimal
; ATENCION: La convercion del valor Hexadecimal a Decimal destruye el resultado en Hexadecimal
call Hexadec_BDC
;Finalmente lo mas sencillo, decidir que posicion ocupa la coma en el resultado final
clrf Coma_R
movfw Coma_A
addwf Coma_B,w
movwf Coma_R
; Si se requiere acomodar el resultado para mostrarlo en un display o algo similar
call BCD_ASCII
goto Finalizar ; Terminada la multiplicacion, salimos de la subrutina
; Simplemente rotamos todos los bytes para la proxima etapa
RotaFactor_A bcf STATUS,C
rlf ValInt_B0,f
rlf ValInt_B1,f
rlf ValInt_B2,f
rlf ValInt_B3,f
rlf ValInt_B4,f
rlf ValInt_B5,f
rlf ValInt_B6,f
rlf ValInt_B7,f
return
; Vamos sumando en la variable resultado los valores intermedios cuando corresponde
; Esta suma se hace byte a byte y en caso de suceder un acarreo se llama a una subrutina que incrementa el siguiente byte
SumarFactor_A bcf STATUS,C
movfw ValInt_B0
addwf Resultado_B0,f
btfsc STATUS,C
call Incremento_B1
bcf STATUS,C
movfw ValInt_B1
addwf Resultado_B1,f
btfsc STATUS,C
call Incremento_B2
bcf STATUS,C
movfw ValInt_B2
addwf Resultado_B2,f
btfsc STATUS,C
call Incremento_B3
bcf STATUS,C
movfw ValInt_B3
addwf Resultado_B3,f
btfsc STATUS,C
call Incremento_B4
bcf STATUS,C
movfw ValInt_B4
addwf Resultado_B4,f
btfsc STATUS,C
call Incremento_B5
bcf STATUS,C
movfw ValInt_B5
addwf Resultado_B5,f
btfsc STATUS,C
call Incremento_B6
bcf STATUS,C
movfw ValInt_B6
addwf Resultado_B6,f
btfsc STATUS,C
call Incremento_B7
bcf STATUS,C
movfw ValInt_B7
addwf Resultado_B7,f
return
; Esta seccion es llamada cuando en la suma ocurre un acarreo, este acarreo se traslada al siguiente byte...
; ...incrementandolo, en caso de que este incremento genere otro acarreo, se sigue trasladando el acarreo al byte siguiente
Incremento_B1 incf Resultado_B1,f
btfss STATUS,Z
return
Incremento_B2 incf Resultado_B2,f
btfss STATUS,Z
return
Incremento_B3 incf Resultado_B3,f
btfss STATUS,Z
return
Incremento_B4 incf Resultado_B4,f
btfss STATUS,Z
return
Incremento_B5 incf Resultado_B5,f
btfss STATUS,Z
return
Incremento_B6 incf Resultado_B6,f
btfss STATUS,Z
return
Incremento_B7 incf Resultado_B7,f
return
; Rutina de Conversion de Hexadecima a Decimal (Esta rutina no es mia, solo la adapte)
; Hexadecimal (Resultado_B7 a Resultado_B0) -> Decimal (ResDec_9-ResDec_0)
; Para una Multiplicacion de FFFFFFFF x FFFFFFFF (Que es el maximo numero a multiplicar por nosotros)
; obtenemos FFFFFFFE00000001 y en decimal 18446744065119617025 para representar este numero utilizamos u nibble por digito
; por lo tanto necesitaremos 10 bytes para almacenarlo
Hexadec_BDC bcf STATUS,C
movlw 0x40 ; este valor es la cantidad de bits del numero hexadecimal a convertir (64 bits)
movwf Contador
clrf ResDec_9
clrf ResDec_8
clrf ResDec_7
clrf ResDec_6
clrf ResDec_5
clrf ResDec_4
clrf ResDec_3
clrf ResDec_2
clrf ResDec_1
clrf ResDec_0
Hex_BCD_1 rlf Resultado_B0,f
rlf Resultado_B1,f
rlf Resultado_B2,f
rlf Resultado_B3,f
rlf Resultado_B4,f
rlf Resultado_B5,f
rlf Resultado_B6,f
rlf Resultado_B7,f
rlf ResDec_0,f
rlf ResDec_1,f
rlf ResDec_2,f
rlf ResDec_3,f
rlf ResDec_4,f
rlf ResDec_5,f
rlf ResDec_6,f
rlf ResDec_7,f
rlf ResDec_8,f
rlf ResDec_9,f
decfsz Contador,f
goto Hex_BCD_2
return
Hex_BCD_2 movlw ResDec_0
call Hex_BCD_3
movlw ResDec_1
call Hex_BCD_3
movlw ResDec_2
call Hex_BCD_3
movlw ResDec_3
call Hex_BCD_3
movlw ResDec_4
call Hex_BCD_3
movlw ResDec_5
call Hex_BCD_3
movlw ResDec_6
call Hex_BCD_3
movlw ResDec_7
call Hex_BCD_3
movlw ResDec_8
call Hex_BCD_3
movlw ResDec_9
call Hex_BCD_3
goto Hex_BCD_1
Hex_BCD_3 movwf FSR
movlw 0x03
addwf INDF,f
btfss INDF,3
subwf INDF,f
movlw 30H
addwf INDF,f
btfss INDF,7
subwf INDF,f
return
; Esta seccion convierte el número de decimal a caracteres ASCII para mostrar en un display (Seccion Nueva)
BCD_ASCII movlw 0x15 ;Limpiamos la conversion ASCII anterior, llenando de "0" todos los dígitos"
movwf Auxiliar_A
movlw ResASC_0
movwf FSR
LimpiaASCII movlw 0x30
movwf INDF
decf FSR,f
decfsz Auxiliar_A,f
goto LimpiaASCII
movlw 0x0A ; Meto en elcontador la cantidad de BCD´s a transformar
movwf Contador
movlw ResASC_20 ; Cargo en Auxiliar_B la direccion con valores de más peso de los ASCII
movwf Auxiliar_B
movlw ResDec_9 ; Hago lo mismo pero ahora con las direccion de los valores de más peso de los BCD en Auxiliar_A
movwf Auxiliar_A
ASCII_Loop_1 movwf FSR ; Direcciono indirectamente al resultado en BCD
movfw INDF ; Cargo el dato en BCD
swapf INDF,w ; Ttrabajamos con la parte alta de este BCD
andlw 0x0F ; limpio la parte que no me sirve...
addlw 0x30 ; ...y lo convierto en un valos ASCII
movwf Auxiliar_C ; Guardo la conversion en Auxiliar_C
call VerifComa ; Rutina que chequéa si corresponde colocar la coma
movfw Auxiliar_B ; Direcciono indirectamente la actual posición ASCII
movwf FSR
movfw Auxiliar_C ; Meto el valor convertido en la dirección correspondiente
movwf INDF
incf Auxiliar_B,f ; Apunto a donde se dejará el próximo valor ASCII
movfw Auxiliar_A ; Direcciono indirectamente al resultado en BCD de nuevo
movwf FSR ; Cargo el dato en BCD, Ahora trabajaremos con la parte baja
movfw INDF
andlw 0x0F ; limpio la parte que no me sirve...
addlw 0x30 ; ...y lo convierto en un valos ASCII
movwf Auxiliar_C ; Guardo la conversion en Auxiliar_C
call VerifComa ; Rutina que chequéa si corresponde colocar la coma
movfw Auxiliar_B ; Direcciono indirectamente la actual posición ASCII
movwf FSR
movfw Auxiliar_C ; Meto el valor convertido en la dirección correspondiente
movwf INDF
incf Auxiliar_A,f ; Apunto a donde se tomaran los dos siguientes valores BCD
incf Auxiliar_B,f ; Apunto a donde se dejara el proximo valor ASCII
movfw Auxiliar_A ; Dejo en w el valor de la proxima dirección BCD
decfsz Contador,f ; Verifico si hemos llegado al final del loop
goto ASCII_Loop_1
; En esta seccion eliminamos los ceros antes del primer valor significativo o antes de la coma y los ceros despues del...
; ...último valor significativo.
movlw ResASC_20 ; Direcciono indirectamente la actual posición ASCII
movwf FSR
movlw 0x00 ; Utilizo Auxiliar_A como un contador para saber desde donde debo desplazar los dígitos más significativos
movwf Auxiliar_A
ASCII_Loop_2 movfw INDF
xorlw 0x30 ; Comparo dígito a dígito de izquierda a derecha...
btfss STATUS,Z ; ...si los valores son "0"
goto ASCII_Loop_3 ; si llego a un dígito que no es "0" salgo del loop
incf FSR,f ; si es "0" direcciono el siguiente digito...
incf Auxiliar_A,f ;... e incremento el contador que indica cuantos digitos hay en "0"
goto ASCII_Loop_2 ; y seguimos testeando
ASCII_Loop_3 movfw INDF ; Antes de terminar esta seccion
xorlw 0x2C ; comparo el último dígito que no fue "0" para ver...
btfsc STATUS,Z ; si el dígito tenia una ","
decf Auxiliar_A,f ; tomo el dígito anterior para que el resultado quede "0,XXXX"
call Corre_ASCII ; Llamamos a la rutina que desplaza los dígitos
movlw ResASC_0
movwf FSR
ASCII_Loop_4 movfw INDF
xorlw 0x30
btfss STATUS,Z
goto ASCII_Loop_5
movlw 0x20
movwf INDF
decf FSR,f
goto ASCII_Loop_4
ASCII_Loop_5 movfw INDF
xorlw 0x2C
btfss STATUS,Z
return
movlw 0x20
movwf INDF
return
VerifComa movfw Coma_R
sublw ResASC_0
xorwf Auxiliar_B,w
btfss STATUS,Z
return
movfw Auxiliar_B ; Direcciono indirectamente la actual posición ASCII
movwf FSR
movlw 0x2C ; Meto una coma en la posición adecuada
movwf INDF
incf Auxiliar_B,f
return
Corre_ASCII movlw 0x15
movwf Auxiliar_B
movlw ResASC_19
movwf FSR
Loop_C_ASC_2 movfw INDF
decf FSR,f
movwf INDF
incf FSR,f
incf FSR,f
decfsz Auxiliar_B,f
goto Loop_C_ASC_2
decfsz Auxiliar_A,f
goto Corre_ASCII
return
Finalizar
end
; Si la estamos ocupando como una subrutina en lugar del end colocamos un return