Olá, para os que, como eu, sofrem com o assembly.
Programar em assembly não é o que podemos chamar de um passeio no parque. Mas para os MCU, principalmente quando espaço x custo x desempenho é crítico, um código asm bem feito ainda é, penso, importante. Acho que o assunto não é necessariamente novidade mas há algum tempo comecei a utilizar algumas macros que possibilitaram tornar o meu trabalho em assembly um pouco mais "elegante", eu diria, e sem inchar muito o código. Elas foram desenvolvidas pelo Mike Predko, nos idos de 1999, e venho adaptando alguns includes importantes para o meu uso a partir delas. São umas tais de "varstart" junto com a principal que é a "varadd". Elas permitem que se criem includes do tipo "usart.inc", "ht1380.inc", "lcd2x16.inc" e muitos outros módulos que se tornam razoavelmente portáveis ou, pelo menos, diminuem um pouco o trabalho com novos códigos. Claro que inicialmente é tudo "na unha". Mas o programa principal fica, a partir desses includes, bastante mais "legível" e de melhor manutenção. Seguem alguns trechos de código interessantes:
;*****************************************************
;*****************************************************
; VARS.inc
;*****************************************************
; VARSTART (BEGIN/END ADDRESSES) PLACES ON MCU REGS
; VARADD (LABEL, SIZE) SPECIFY VARIABLE AND ITS SIZE
;
VARSTART MACRO MSTART, MEND
IFDEF VARSTARTVAR
ERROR "'VARSTART' INVOCATION REPEATED MORE THAN ONCE!"
ELSE
#DEFINE VARSTARTVAR
ENDIF
VARCURRENT SET MSTART
VAREND SET MEND
ENDM
VARADD MACRO VARNAME, SIZE
IF (VARCURRENT>VAREND)
ERROR "TOO MANY VARIABLES FOR ALLOCATED FILE REGISTERS!"
ENDIF
VARNAME EQU VARCURRENT
VARCURRENT SET VARCURRENT+SIZE
ENDM
;*****************************************************
Então, você poderia iniciar seu código principal assim:
;*****************************************************
;*****************************************************
INCLUDE "p16f877A.inc"
INCLUDE "BANK_MACROS.inc"
; DECLARE PROCESSOR SPEED (Hz) -> USADO PARA "usart.inc" e "delayms.inc"
CONSTANT SPEED = 8000000
ERRORLEVEL -302
__CONFIG _BODEN_OFF & _CP_OFF & _CPD_OFF & _WDT_OFF e etc .....
; VARIABLES
INCLUDE "VARS.inc"
VARSTART 0X27, 0X6F ; (BANK0, UNTIL LEAST 16 MIRRORED ADRESSES)
VARADD ARROW_TMP,1
VARADD BCD_TMP,1
VARADD BUFFER,1
VARADD BUFFER2,1
VARADD BUFFER3,1
... etc, etc (esses são os registradores utilizados no código do programa principal)
;*****************************************************
ORG 0X000 ; INICIO EM '0'
GOTO MAIN
ORG 0X04 ; RTI START
GOTO INTERRUPT_ROUTINE
; INCLUDES FILES
INCLUDE "USART.inc"
INCLUDE "HT1380.inc"
INCLUDE "LCD2X16.inc"
;*****************************************************
;*****************************************************
....o seu código....
;*****************************************************
Utilizando a macro "varadd" dentro dos includes que voce cria, são definidas automaticamente as posições das variáveis utilizadas dentro dos mesmos, na compilação, sem conflito com as demais utilizadas no programa. Um exemplo para "usart.inc":
;*****************************************************
; USART.inc
; PIC16 INTERNAL HARDWARE USART DRIVER - by zielpunkt
;
; STATEMENTS: USART_TX
; USART_RX
;
; INPUT: W = DATA TO SEND
;
; OUTPUT: VAR_USART_DATA = DATA RECEIVED
; VAR_USART_RXDAT_FLAG = 1, DATA RECEIVED OCCOURS
; VAR_USART_RXERR_FLAG = 1, ERROR WHEN RECEIVING
;*****************************************************
VARADD VAR_USART_FLAGS,1
VARADD VAR_USART,1
VARADD VAR_USART_DATA,1
#DEFINE VAR_USART_RXERR_FLAG VAR_USART_FLAGS, 0 ; 1, ASSIGN ERROR WHEN RECEIVING
#DEFINE VAR_USART_RXDAT_FLAG VAR_USART_FLAGS, 1 ; 1, ASSIGN THERE ARE DATA RECEIVED
#DEFINE VAR_USART_INTER_FLAG VAR_USART_FLAGS, 2 ; 1, ASSIGN THAT GIE WAS 'ON'
;
;******************************************************************************
;******************************************************************************
; MACROS
;******************************************************************************
;******************************************************************************
USART_INI MACRO
CALL USART_INI_
ENDM
USART_TX MACRO
CALL USART_TX_
ENDM
USART_RX MACRO
CALL USART_RX_
ENDM
;
;*****************************************
;*****************************************
; USART SETUP
;*****************************************
;*****************************************
USART_INI_
IF ((SPEED - 4000000) == 0)
MOVLW .25
MOVWF VAR_USART
ENDIF
IF ((SPEED - 8000000) == 0)
MOVLW .51
MOVWF VAR_USART
ENDIF
MOVFW VAR_USART
BANK1 ; TXSTA SETUP
MOVWF SPBRG ;
MOVLW B'00100100' ; ENABLE TX, ASYNC MODE, 8 BIT, HIGH SPEED
MOVWF TXSTA ;
BANK0 ; RCSTA SETUP
MOVLW B'10010000' ; ENABLE RX, 8 BIT, RX STOPED, ADDRESS DISABLED
MOVWF RCSTA ;
RETURN ;
;*****************************************
;*****************************************
; LOCAL - DISABLE INTERRUPTS
;*****************************************
;*****************************************
USART_DEAL_INTERR
BTFSS INTCON, GIE ; THERE WAS GIE_ON?
RETURN ; NO, JUST BACK
BCF INTCON,GIE ; DISABLE ALL INTERRUPTS
BTFSC INTCON,GIE ; IS INTCON DISABLED ?
GOTO $-1 ; NO, WAITING....
BSF VAR_USART_INTER_FLAG; ASSIGN IT
RETURN
;*****************************************
;*****************************************
; SUB SEND DATA BYTE BY SERIAL
;*****************************************
;*****************************************
USART_TX_
BCF VAR_USART_INTER_FLAG; DEAL INTERRUPTS
CALL USART_DEAL_INTERR ;
BANK1 ;
BTFSS TXSTA,TRMT ; IS TX BUFFER EMPTY ?
GOTO $-1 ;
BANK0 ;
MOVWF TXREG ; SEND 'W' DATA
BTFSC VAR_USART_INTER_FLAG; THERE WERE INTERRUPTS ON?
BSF INTCON,GIE ; YES, ENABLE INTERRUPTS
RETURN
;*****************************************
;*****************************************
; SUB RECEIVE DATA FROM SERIAL
;*****************************************
;*****************************************
USART_RX_
BCF VAR_USART_RXERR_FLAG; NO ERROR, YET...
BCF VAR_USART_RXDAT_FLAG; NO DATA RECEIVED, YET...
BSF RCSTA,CREN ; ENABLE SERIAL RECEIVER
BTFSS PIR1,RCIF ; WAS RECEIVED DATA ?
RETURN ; NO DATA RECEIVED
; BTFSS RCSTA,FERR ; IS BIT_STOP OK? (FERR=0=OK)
; GOTO $+3 ;
; BSF VAR_USART_RXERR_FLAG; RETURNS WITH ERROR ASSIGNED
; RETURN
BCF PIR1, RCIF ; CLEAR INTERRUPT FLAG
BSF VAR_USART_RXDAT_FLAG; ASSIGN SERIAL DATA RECEIVED
MOVFW RCREG ; TRANFER THE DATA TO 'W'
MOVWF VAR_USART_DATA ; AND THEN TO BUFFER
RETURN
;*****************************************************
O resultado disso é que voce passa a criar bibliotecas práticas dos principais periféricos que utiliza e pode utilizar chamadas do tipo "usart_tx", "usart_rx", "rtc_read", "lcd_print", etc, a partir do programa principal. São apenas idéias e alguns fragmentos de código que podem parecer um pouco confusos, mas que, mesmo com algumas limitações, funcionam. Principalmente para a visualização de um código asm mais "limpo". É isso.
zielpunkt