
;********************************************************************************************************
;*ORGANO POLIFONICO************************************************************************************************
;********************************************************************************************************
      LIST P=16F874
      INCLUDE "P16f874.INC"
;***************ESTA EN CD**************************************
; Puertos de teclado

PKYBD   EQU   PORTB      ; El teclado ocupa el puerto B de 8 bits
            ; el MSN debe ser configurado como entrada
            ; el LSN debe ser configurado como salida
            ; con ello será posible aprovechar la caracteristica
            ; de interrupción por cambios del PIC
            ; si se utilizara otro puerto se perdería la 
            ; capacidad de interrupción (para equipos que pasan a modo
            ; de bajo consumo) y los Pull-up
            
;*************************CTE BY NOS**********************
;CONT1      EQU      20   ;
;CONT2      EQU      21   ;
TMP1      EQU      22;
TMP2      EQU      23;
;PAUS1      EQU      24;
;PAUS2      EQU      25;
TEC_numv   EQU      26;
TEC_flg      EQU      27;
TEC_ant      EQU      28;
;MAQ_ESTADOS1   EQU      29;
;MAQ_EVENTOS1   EQU      30;
;MAQ_EVENTOS2   EQU      31;
;MAQ_ESTADOS2      EQU      36;
;MAQ_ESTADOS      EQU      41;
F_rep      EQU      4;
;CAMBIO_OCTAVA   EQU   32;
;TONO      EQU      33;
;EVENTO_TONO      EQU   34;
KBINIT      EQU      35;
;DESPLAZAMIENTO1   EQU   37;
;DESPLAZAMIENTO0   EQU   38
;PUNTERO0   EQU      39;
;CAMBIAR_OCTAVA   EQU   40;
;ELEGIR_INSTRUMENTO   EQU   41;
;ESTADO      EQU      42;
cur_12      EQU      43;
EVENTO_TIPO   EQU   44;
CODIG_TECL   EQU   45
;**************************************************************
; Puertos LCD

P_LCDen   EQU   PORTD      ; Puerto de bit de enable(strobe) LCD
b_LCDen   EQU    4      ;  bit de enable(strobe) LCD
P_LCDrw   EQU   PORTD      ; Puerto de bit de read/write LCD
b_LCDrw   EQU    5      ;  bit de read/write LCD
P_LCDdi   EQU   PORTD      ; Puerto de bit de dato/instruccion LCD
b_LCDdi   EQU    6      ;  bit de dato/instruccion LCD
P_LCDDA   EQU   PORTD      ; Puerto de dato de LCD (los 4 bits menos significativos!)
TMP0   EQU   7
;*****************************************************
; Constantes de tiempo (pueden variar segun frecuencia
;                       de operacion del microcontrolador)

klcdw   EQU   0FFh      ; Constante para pausa 

;*****************************************************
; Configuración LCD

lcd_set   EQU   b'00101000'   ; LCD: 4 bits, 2 línea, 5x7 puntos
lcd_mod   EQU   b'00000110'   ;
lcd_on   EQU   b'00001100'   ; Display on, cursor off, blink off
lcd_off   EQU   b'00001000'   ;
lcd_to   EQU   d'200'      ; Time-out para display ocupado

;*****************************************************
; Comandos LCD

lcd_clr   EQU   b'00000001'   ; LCD clear
cur_hm   EQU   b'00000010'   ; Cursor to home
cur_on   EQU   b'00001010'   ; Cursor on
cur_off EQU   b'00001000'   ; Cursor off
cur_rm   EQU   b'00010100'   ; Cursor right move
cur_lm   EQU   b'00010000'   ; Cursor left move

;********************ESTA EN CD*********************************
; Constantes de teclado

self1   EQU   B'00000111'   ; Selección de fila 1
self2   EQU   B'00001011'   ; Selección de fila 2
self3   EQU   B'00001101'   ; Selección de fila 3
self4   EQU   B'00001110'   ; Selección de fila 4
selfa   EQU   B'00000000'   ; Selección de todas las filas
nminv   EQU   10      ; Numero minimo de veces para detectar estabilidad

;********************ESTA N CD*********************************
      ORG    003   ;
      GOTO   PROGPPAL;

PROGPPAL:
      CALL LCDINIT   ;
      CALL KBINIT      ;
      CALL INITMAQ_EVENT
   
LAZOPPAL:
      CALL LEETECLADO ;
      CALL    ELIMREB
      CALL   REPORTT   
      MOVWF   CODIG_TECL
      MOVF   CODIG_TECL,W
       GOTO TRAINSTRUMENTO   ;
      MOVWF   EVENTO_TIPO   ;
      CALL MAQ_EVENTOS1   ;
      GOTO LAZOPPAL   ;


   ORG   100            ;
;******************************************
; INSTRUMENTO
; Traduce código de tecla
; Recibe:  W: código de tecla (0-16)
; Retorna: W: código de tecla traducido
; Nota: está preparado para trabajar en la página 100-1FF

   
;*********************************************************
;elegir instrumento en teclado
TRAINSTRUMENTO:

   CLRF   PCLATH      ; Funciona en direcciones 100-1FF
   BSF      PCLATH,0   ;
   BCF      PCLATH,1   ;

   ADDWF   PCL,F      ;
   RETLW   0      ; No instrumento
   RETLW   1      ; guitarra
   RETLW   2      ; violin
   RETLW   3      ; flauta
   RETLW   4      ; saxofon
   RETLW   0      ; maquina de estados
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; 
   RETLW   0      ; Tecla 16 (para cambiar de evento)



   ORG 200

;******************MOSTRAR*********************


   
;***************MAQUINA DE EVENTOS1****************


MAQ_EVENTOS1:

   CLRF   PCLATH      ; Funciona en direcciones 200-2FF
   BSF   PCLATH,1   ;
   BCF   PCLATH,0   ;

                  ; Recibe el dato desde w
   MOVF   EVENTO_TIPO,W            
   ADDWF   PCL,F      ; Salta a la rutina de tratamiento
   RETURN         ; Evento 0
   GOTO   GUITARRA      ; cambiar octava (EVENTO1)
   GOTO   VIOLIN   ; elegir instrumento (EVENTO2)
   GOTO   FLAUTA   ; Maquina de estados
   GOTO   SAXOFON   ;maquina de estados2
   RETURN
;*********************CAMBIAR OCTAVA (EVENTO 1)*********************


;**********************ELEGIR INSTRUMENTO (EVENTO 2)****************


   
   ORG 300
            



   
;******************SACAR POR PANTALLA NOMBRE DE INSTRUMENTOS*****************
GUITARRA:
   
   MOVLW   lcd_clr
   CALL   LCDIWR

   MOVLW   A'G'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'U'      ;   
   CALL   LCDDWR      ;
   
   MOVLW   A'I'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'T'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'A'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'R'      ;
   CALL   LCDDWR               

   MOVLW   A'R'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'A'      ;
   CALL   LCDDWR
   
   GOTO    PAUS1
   
   
VIOLIN:
   
   MOVLW   lcd_clr
   CALL   LCDIWR

   MOVLW   A'V'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'I'      ;   
   CALL   LCDDWR      ;
   
   MOVLW   A'O'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'L'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'I'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'N'      ;
   CALL   LCDDWR               
   
   GOTO    PAUS1
   
   
FLAUTA:
   
   MOVLW   lcd_clr
   CALL   LCDIWR

   MOVLW   A'F'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'L'      ;   
   CALL   LCDDWR      ;
   
   MOVLW   A'A'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'U'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'T'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'A'      ;
   CALL   LCDDWR      
            
   
   GOTO    PAUS1
   
   
SAXOFON:
   

   MOVLW   lcd_clr
   CALL   LCDIWR

   MOVLW   A'S'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'A'      ;   
   CALL   LCDDWR      ;
   
   MOVLW   A'X'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'O'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'F'      ;
   CALL   LCDDWR      ;
   
   MOVLW   A'O'      ;
   CALL   LCDDWR               

   MOVLW   A'N'      ;
   CALL   LCDDWR      ;
   
   
   GOTO    PAUS1   
PAUS1:
   CALL    LEETECLADO   
   CALL   ELIMREB
   CALL   REPORTT
   CALL   TRAINSTRUMENTO   ; sakatutako teklari dagokion gertaera mota 
                  ;itzuliko du
   MOVWF   EVENTO_TIPO
   CALL   MAQ_EVENTOS1

   RETURN
   
;***********************ESTA EN CD******************************
; KYBINIT
; Inicializa puerto de teclado

KYBINIT:
   BSF   STATUS,RP0      ; Banco 1
   MOVLW   B'11110000'      ; Configuración de puerto B:
   MOVWF   PKYBD         ;  RB7-RB4 entrada, RB3-RB0 salida
   BCF   OPTION_REG&7F,NOT_RBPU   ;  Pull ups activados
   BCF   STATUS,RP0      ; Banco 0
   MOVLW   selfa         ; Se seleccionan todas las filas simultáneamente
   MOVWF   PKYBD         ;
   CLRF   TEC_ant         ; Se inicializan variables
   CLRF   TEC_flg         ;
   CLRF   TEC_numv      ;
   RETURN   
         ;
INITMAQ_EVENT
   MOVLW   0
   MOVWF    CODIG_TECL
   MOVWF   EVENTO_TIPO   
         
   RETURN
;********************ESTA EN CD*********************************
; LEETECLADO
; Lee teclado
; Retorna: W: 0 si no hay tecla presionada, de lo contrario código de tecla

LEETECLADO:
            ; Se hace una verificación rápida de si
            ; hay tecla presionada
   CLRF   TMP2      ; Se inicializa registro de código COD=0
   MOVLW   selfa      ; Se seleccionan todas las filas simultáneamente
   MOVWF   PKYBD      ;
   MOVLW   0F0h
   XORWF   PKYBD,W      ; Se leen las columnas y si alguna tecla esta
            ; presionada el resultado será distinto de 0
   BTFSC   STATUS,Z   ;
   GOTO   LEETEC_RET   ; Retorna, no hay teclas presionadas

   CLRF   TMP1      ; Se inicializa registro de barrido BAR=1
   INCF   TMP1,F      ; 

   MOVLW   self1      ; Se selecciona fila 1
   CALL   LEETEC1      ;
   MOVLW   self2      ; Se selecciona fila 2
   CALL   LEETEC1      ;
   MOVLW   self3      ; Se selecciona fila 3
   CALL   LEETEC1      ;
   MOVLW   self4      ; Se selecciona fila 4
   CALL   LEETEC1      ;
LEETEC_RET
   MOVLW   selfa      ; Se dejan seleccionadas todas las filas
   MOVWF   PKYBD      ;
   MOVF   TMP2,W      ; Se retorna código en W
   RETURN         ;

LEETEC1:
   MOVWF   PKYBD      ; Se pone seleccionador de fila en el puerto
   MOVF   TMP2,F      ; Se verifica que no ha sido recibida tecla
   BTFSS   STATUS,Z   ;
   RETURN         ; Tecla recibida previamente
   BTFSS   PKYBD,4      ; Se verifica columna 1
   GOTO   LEETEC2      ;
   INCF   TMP1,F      ;
   BTFSS   PKYBD,5      ; Se verifica columna 2
   GOTO   LEETEC2      ;
   INCF   TMP1,F      ;
   BTFSS   PKYBD,6      ; Se verifica columna 3 
   GOTO   LEETEC2      ;
   INCF   TMP1,F      ;
   BTFSS   PKYBD,7      ; Se verifica columna 4
   GOTO   LEETEC2      ;
   INCF   TMP1,F      ;
   RETURN         ; No se encuentra ninguna columna presionada

LEETEC2:
   MOVF   TMP1,W      ; Se ha descubierto una columna presionada y
   MOVWF   TMP2      ; se guarda su código
   RETURN         ; Se retorna con el código de columna en TMP2


;************************ESTA*****************************
; ELIMREB
; Elimina rebote de teclado
; Recibe:  W: código leido de tecla, 0 no hay tecla
; Retorna: W: código de tecla ya estable, 0 no hay tecla


ELIMREB:
   MOVWF   TMP1      ; Se almacena temporalmente la tecla recibida
   XORWF   TEC_ant,W   ; Se verifica si es igual a la tecla anterior
   BTFSC   STATUS,Z   ;
   GOTO   ELIM_IG      ;
            ; Caso de tecla distinta
   MOVF   TMP1,W      ; Se actualiza tecla anterior
   MOVWF   TEC_ant      ;
   CLRF   TEC_numv   ; Se inicializa contador de numero de veces
   RETLW   0      ; Se indica 'no tecla'
ELIM_IG:         ; Caso de tecla igual
   MOVLW   nminv      ; Se compara si llegó al número mínimo de veces, estable
   SUBWF   TEC_numv,W   ;
   BTFSC   STATUS,Z   ;
   GOTO   ELIM_EST   ;
            ; Caso aún no estable
   INCF   TEC_numv,F   ;
   RETLW   0      ;
ELIM_EST:         ; Caso estable
   MOVF   TMP1,W      ;
   RETURN         ;
   

;***************************ESTA**************************
; REPORTT
; Reporta una vez una tecla cuando se presiona
; Recibe:  W: código leido y filtrado de tecla, 0 no hay tecla
; Retorna: W: código de tecla, 0 no hay tecla
;          STATUS: flag Zero debidamente ajustado

REPORTT:
   IORLW   0      ; Se verifica si hay tecla
   BTFSC   STATUS,Z   ;
   GOTO   RE_NTEC      ;
RE_TEC:
   BSF   STATUS,Z   ; Se activa flag Zero, por si hay que indicar "no tecla"  
   BTFSC   TEC_flg,F_rep   ; Tecla presionada, se verifica si fue reportada
   RETLW   0      ; (RETLW 0 no ajusta el flag de Zero)

   BSF   TEC_flg,F_rep   ; Se reporta tecla, marcando tecla reportada
   BCF   STATUS,Z   ; Se ajusta el flag de Zero indicando que no es cero
   RETURN         ; Se retorna codigo de tecla
RE_NTEC:
   BCF   TEC_flg,F_rep   ; No hay tecla, se marca no reportado
   ANDLW   0      ; Se retorna 0: tecla ya reportada (flag Z ajustado)
   RETURN         ; 




;********************************************************************************************************
;*LCD****************************************************************************************************
;********************************************************************************************************



;*************ESTA EN CD****************
; LCDINIT
; Inicializa pantalla y puerto de pantalla

LCDINIT:

   BSF   STATUS, RP0   ; Se reconfigura puerto de datos LCD
   MOVLW   0F0h      ; 4 bits de datos = Salida
   ANDWF   P_LCDDA,F   ;
   BCF   P_LCDdi,b_LCDdi   ; Dato/instrucción = Salida
   BCF   P_LCDrw,b_LCDrw   ; Read/Write = Salida
   BCF   P_LCDen,b_LCDen   ; Strobe = Salida
   BCF   STATUS, RP0   ;

   BCF   P_LCDdi,b_LCDdi   ; Dato/instrucción = Instrucción
   BCF   P_LCDrw,b_LCDrw   ; Read/Write = Write
   BCF   P_LCDen,b_LCDen   ; desactiva el strobe

   MOVLW   0F0h      ; Se prepara primer dato de inicialización de 4 bits
   ANDWF   P_LCDDA,F   ; (que hay que enviar 3 veces)
   MOVLW   B'0011'      ; 
   IORWF   P_LCDDA,F   ;

   MOVLW   D'20'      ; Se esperan 20ms
   CALL   LCDWAIT   ;

   BSF   P_LCDen,b_LCDen   ; Primer envio
   BCF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe

   MOVLW   D'5'      ; Se esperan 5ms
   CALL   LCDWAIT      ;

   BSF   P_LCDen,b_LCDen   ; Segundo envio
   BCF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe

   MOVLW   D'1'      ; Se espera 1ms
   CALL   LCDWAIT      ;

   BSF   P_LCDen,b_LCDen   ; Tercer envio
   BCF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe

   MOVLW   D'5'      ; Se esperan 5ms
   CALL   LCDWAIT      ;

   MOVLW   0F0h      ; Se prepara segundo dato de inicialización de 4 bits
   ANDWF   P_LCDDA,F   ;
   MOVLW   B'0010'      ;
   IORWF   P_LCDDA,F   ;

   BSF   P_LCDen,b_LCDen   ; Envio
   BCF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe

   MOVLW   D'5'      ; Se esperan 5ms
   CALL   LCDWAIT      ;

   MOVLW   lcd_set      ; Se envia instrucción para fijar tipo de pantalla
   CALL   LCDIWR      ;

   MOVLW   D'5'      ; Se esperan 5ms
   CALL   LCDWAIT      ;

   MOVLW   lcd_off      ; Se envia instrucción para apagar pantalla
   CALL   LCDIWR      ;

   MOVLW   lcd_on      ; Se envia instrucción para encender pantalla
   CALL   LCDIWR      ;

   MOVLW   lcd_mod      ; Se envia instrucción para forma de operar pantalla
   CALL   LCDIWR      ;

   MOVLW   lcd_clr      ;
   CALL   LCDIWR      ;

   MOVLW   D'100'      ; Se esperan 100 ms
   CALL   LCDWAIT      ;

   RETURN

;****************ESTABA EN CD*************************************
; LCDDWR
; LCD Data write
; Recibe: W=Dato a escribir

LCDDWR:
   BSF   P_LCDdi,b_LCDdi   ; Dato/instrucción = Dato
   GOTO   LCDWRAUX   ;


;*********************ESTA EN CD********************************
; LCDIWR
; LCD Instruction write
; Recibe: W=Dato a escribir

LCDIWR:
   BCF   P_LCDdi,b_LCDdi   ; Dato/instrucción = Dato
   GOTO   LCDWRAUX   ;

LCDWRAUX:
   BCF   P_LCDrw,b_LCDrw   ; Read/Write = Write
   MOVWF   TMP1      ; Se guarda dato para posterior uso

   MOVLW   0F0h      ; Se borran bits lsb del puerto
   ANDWF   P_LCDDA,F   ;
   SWAPF   TMP1,W      ; Se escribe el nible superior
   ANDLW   00Fh      ;
   IORWF   P_LCDDA,F   ;
   BSF   P_LCDen,b_LCDen   ;  activa el strobe
   BCF   P_LCDen,b_LCDen   ;  desactiva el strobe

   MOVLW   0F0h      ; Se borran bits lsb del puerto
   ANDWF   P_LCDDA,F   ;
   MOVF   TMP1,W      ; Se escribe nible inferior   
   ANDLW   00Fh      ;
   IORWF   P_LCDDA,F   ;
   BSF   P_LCDen,b_LCDen   ;  activa el strobe
   BCF   P_LCDen,b_LCDen   ;  desactiva el strobe

   CALL   LCDBSY      ;
   
   RETURN

;*****************************************************
; LCDWAIT
; Recibe:  W=Número de milisegundos a esperar

LCDWAIT:
WAIT:
   MOVWF   TMP1      ; Se almacena el número de milisegundos en CONT1
LCDW1:   
   MOVLW   klcdw   ; Se almacena contador básico
   MOVWF   TMP2      ;
LCDW2:   
   NOP         ; Ajustando al milisegundo
   NOP         ;
   DECFSZ   TMP2,F   ; Se decrementa contador básico
   GOTO   LCDW2      ; hasta llegar a cero
   DECFSZ   TMP1,F   ; Se repite el contador básico tantas
   GOTO   LCDW1      ; veces como número de milisegundos
   RETURN   

;*************************ESTA EN CD****************************
; LCDBSY
; LCD Busy: espera desocupación de LCD (con time-out)
; Retorna: W=0 desocupado / W=1 error time-out superado

LCDBSY:
   MOVLW   lcd_to      ; Se prepara contador de time-out
   MOVWF   TMP1      ;
   CALL   LCDDIN      ; Se pone el bus como entrada
   BCF   P_LCDdi,b_LCDdi   ; Dato/instrucción = Instruccion
   BSF   P_LCDrw,b_LCDrw   ; Read/Write = Write
LCDBS1:
   BSF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe
   BCF   P_LCDen,b_LCDen   ;
   MOVF   P_LCDDA,W   ; Se lee nibble MSN con busy flag BF
   BSF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe
   BCF   P_LCDen,b_LCDen   ;
   ANDLW   08h      ;
   BTFSC   STATUS,Z   ; Si BF=0 el LCD esta desocupado
   GOTO   LCDBS2      ;

   DECFSZ   TMP1,F      ; Se verifica si hay time out
   GOTO   LCDBS1      ;
   
   CLRF   TMP1      ; Hay time-out
   CALL   LCDDOUT      ;
   RETLW   01      ; Retorna error por time-out

LCDBS2:            ; Desocupado
   CALL   LCDDOUT      ;
   RETLW   00      ; Retorna desocupado sin errores

;**********************ESTA EN CD*******************************
; CURADD
; Lee dirección del cursor
; Retorna: W=dirección del cursor (0-79d)

CURADD:
   CALL   LCDDIN      ; Se reconfigura puerto de datos LCD como entrada

   BCF   P_LCDdi,b_LCDdi   ; Dato/instrucción = Instruccion
   BSF   P_LCDrw,b_LCDrw   ; Read/Write = Write

   BSF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe
   BCF   P_LCDen,b_LCDen   ;
   MOVF   P_LCDDA,W   ; Se lee nibble MSN
   ANDLW   07h      ;
   MOVWF   TMP1      ;
   SWAPF   TMP1,F      ;
   BSF   P_LCDen,b_LCDen   ;  activar strobe / desactivar strobe
   BCF   P_LCDen,b_LCDen   ;
   MOVF   P_LCDDA,W   ; Se lee nibble LSN
   ANDLW   0Fh      ;
   IORWF   TMP1,W      ;
   CALL   LCDDOUT      ;
   RETURN         ;



;**********************ESTA EN CD*******************************

LCDDIN:            ; Bus de datos LCD como entrada
   BSF   STATUS, RP0   ; Se reconfigura puerto de datos LCD
   MOVLW   00Fh      ; como entrada
   IORWF   P_LCDDA,W   ;
   MOVWF   P_LCDDA      ;
   BCF   STATUS, RP0   ;
   RETURN         ;

LCDDOUT:         ; Bus de datos LCD como salida
   BSF   STATUS, RP0   ; Se reconfigura puerto de datos LCD
   MOVLW   0F0h      ; como salida
   ANDWF   P_LCDDA,W   ;
   MOVWF   P_LCDDA      ;
   BCF   STATUS, RP0   ;
   MOVF   TMP1,W      ;
   RETURN

   END
