rendered paste body; ==================================================
; == Botload, el bootloader de EuskalBot ==
; ==================================================
; Basado en tinybld
RADIX DEC
LIST P=18F2520
include "p18f2520.inc"
; ==================================================
; CONFIGURACIÓN
; ==================================================
; configuración de placa y rs232
xtal EQU 10000000
baud EQU 115200
; calcular SPBRG, redondeando correctamente
; settings: BRG16=1, BRGH=1
; cuando la PLL esta actica, BRGH=0 nos da un divisor de 4 adicional
spbrgval EQU ((xtal+(baud*2))/(baud*4))-1
; configuración del PIC
__CONFIG _CONFIG1H, _OSC_HSPLL_1H & _FCMEN_OFF_1H & _IESO_OFF_1H
__CONFIG _CONFIG2L, _PWRT_ON_2L & _BOREN_SBORDIS_2L & _BORV_2_2L
__CONFIG _CONFIG2H, _WDT_OFF_2H
__CONFIG _CONFIG3H, _MCLRE_ON_3H & _LPT1OSC_OFF_3H & _PBADEN_OFF_3H & _CCP2MX_PORTC_3H
__CONFIG _CONFIG4L, _STVREN_ON_4L & _LVP_OFF_4L & _XINST_OFF_4L & _DEBUG_OFF_4L
__CONFIG _CONFIG5L, _CP0_OFF_5L & _CP1_OFF_5L & _CP2_OFF_5L & _CP3_OFF_5L
__CONFIG _CONFIG5H, _CPB_OFF_5H & _CPD_OFF_5H
__CONFIG _CONFIG6L, _WRT0_OFF_6L & _WRT1_OFF_6L & _WRT2_OFF_6L & _WRT3_OFF_6L
__CONFIG _CONFIG6H, _WRTB_OFF_6H & _WRTC_OFF_6H & _WRTD_OFF_6H
__CONFIG _CONFIG7L, _EBTR0_OFF_7L & _EBTR1_OFF_7L & _EBTR2_OFF_7L & _EBTR3_OFF_7L
__CONFIG _CONFIG7H, _EBTRB_OFF_7H
; dirección de configuración que contiene el modo de oscilador (PLL o no)
CONFIG1H EQU 0x300001
; dirección con el ID del PIC
DEVID1 EQU 0x3ffffe
; dirección con flags sobre el codigo de usuario
SW_FLAGS_ADDR EQU 0x007FFE
; ==================================================
; VARIABLES
; ==================================================
BLVARS UDATA_ACS
FLAGS RES 1
SW_FLAGS RES 1
RCV_CD_1 RES 1
RCV_CD_2 RES 1
RCV_CD_3 RES 1
CRC RES 1
CMD RES 1
CTR RES 1
CTRO RES 1
PROGBUF UDATA 0x100
BUFFER RES 64
#define USING_PLL FLAGS,0
#define RECONFIG FLAGS,1
; ==================================================
; VECTORES
; ==================================================
; vector de reset de usuario (saltar al bootloader)
CPU_RST_VECTOR CODE 0x0
goto STARTBL
; vector de reset del bootloader (saltar al codigo)
BL_VECTORS CODE 0x7E00
STARTBL bra START
BL_INTH_VECTOR CODE 0x7E02
INTBL bra USERINT_HANDLER
; codigo principal del bootloader
LOADER CODE
; ==================================================
; MACROS
; ==================================================
SENDCHAR MACRO CHR
movlw CHR
rcall UART_SEND
ENDM
; no contar con que W tenga el valor correcto, porque a veces no se usa
MOVLWF MACRO FILE,LITERAL
if LITERAL==0xFF
setf FILE
else
if LITERAL==0x00
clrf FILE
else
movlw LITERAL
movwf FILE
endif
endif
ENDM
; ==================================================
; CÓDIGO
; ==================================================
START
; resetear flags
clrf FLAGS
RECONFIGURE
bcf USING_PLL
; leer byte de config CONFIG1H
MOVLWF TBLPTRU, upper CONFIG1H
MOVLWF TBLPTRH, high CONFIG1H
MOVLWF TBLPTRL, low CONFIG1H
tblrd*
; comprobar modo PLL
movf TABLAT,W
andlw b'00001111'
xorlw _OSC_HSPLL_1H & b'00001111'
; si la PLL esta activa, apuntarlo
skpnz
bsf USING_PLL
; leer byte de flags del software
MOVLWF TBLPTRU, upper SW_FLAGS_ADDR
MOVLWF TBLPTRH, high SW_FLAGS_ADDR
MOVLWF TBLPTRL, low SW_FLAGS_ADDR
tblrd*
movff TABLAT, SW_FLAGS
; configurar TX UART, modo rapido (divisor BRG /4)
MOVLWF TXSTA, (0<<TX9)|(1<<TXEN)|(0<<SYNC)|(1<<BRGH)
; si la PLL esta activa, configurar divisor BRG /16
btfsc USING_PLL
bcf TXSTA,BRGH
; configurar TMR0
MOVLWF T0CON,(1<<TMR0ON)|(1<<T08BIT)|(0<<T0CS)|(0<<PSA)|(7<<T0PS0)
; configurar baudrate
MOVLWF SPBRGH, high spbrgval
MOVLWF SPBRG, low spbrgval
MOVLWF BAUDCON, (1<<BRG16)
; activar UART y RX
MOVLWF RCSTA, (1<<SPEN)|(0<<RX9)|(1<<CREN)
btfsc RECONFIG
bra MAINLOOP
; esperar al PC
rcall RECEIVE_RESET_TO
WAITLOOP rcall RECEIVE_NO_RESET
sublw 'B'
; si no es 'B', seguir esperando
bnz WAITLOOP
rcall RECEIVE_NO_RESET
sublw 'L'
; si no es 'L', seguir esperando
bnz WAITLOOP
; 'BL' recibido
; enviar ID del PIC
MOVLWF TBLPTRU, upper DEVID1
MOVLWF TBLPTRH, high DEVID1
MOVLWF TBLPTRL, low DEVID1
tblrd*+
movf TABLAT,W
rcall UART_SEND
tblrd*
movf TABLAT,W
rcall UART_SEND
; cuando saltemos a RECONFIGURE, nos saltaremos el dialogo inicial con el PC
bsf RECONFIG
MAINLOOP
; esperar a que finalice la escritura anterior
btfsc EECON1,WR
bra $-2
; desactivar escritura
clrf EECON1
; esperando comandos
SENDCHAR 'K'
; recibir comando
rcall RECEIVE
movwf CMD
; escribir EEPROM
movlw 'E'
cpfseq CMD
bra CMD_NO_E
; recibir dirección y datos
clrf CRC
rcall RECEIVE
movwf EEADR
rcall RECEIVE
movwf EEDATA
; recibir y comprobar CRC
rcall RECEIVE
movf CRC,F
bnz BAD_CRC
; configurar escritura
movlw (0<<EEPGD)|(0<<CFGS)|(1<<WREN)
; escribir
rcall WRITE
bra MAINLOOP
CMD_NO_E ; escribir FLASH
movlw 'F'
cpfseq CMD
bra CMD_NO_F
clrf CRC
; recibir dirección
rcall GET_TBLPTR
; recibir 64 bytes
movlw 64
movwf CTR
; cargar dirección del buffer en FSR0
lfsr FSR0,BUFFER
FLASH_READ_LOOP ; recibir byte
rcall RECEIVE
; poner en el buffer
movwf POSTINC0
; decrementar CTR hasta 0
decfsz CTR,F
bra FLASH_READ_LOOP
; recibir y comprobar CRC
rcall RECEIVE
movf CRC,F
bnz BAD_CRC
; configurar borrado
movlw (1<<EEPGD)|(0<<CFGS)|(1<<FREE)|(1<<WREN)
; borrar sector
rcall WRITE
; decrementar TBLPTR
tblrd*-
; escribir 64 bytes (32*2)
movlw 2
movwf CTRO
FLASH_PROG_OLOOP
; escribir 32 bytes
movlw 32
movwf CTR
; resetear dirección del buffer en FSR0
clrf FSR0L
FLASH_PROG_LOOP ; leer byte desde el buffer
movf POSTINC0,W
; poner en los latches de escritura
movwf TABLAT
tblwt+*
; decrementar CTR hasta 0
decfsz CTR,F
bra FLASH_PROG_LOOP
; configurar escritura
movlw (1<<EEPGD)|(0<<CFGS)|(0<<FREE)|(1<<WREN)
; escribir sector
rcall WRITE
; decrementar CTRO hasta 0
decfsz CTRO,F
bra FLASH_PROG_OLOOP
bra MAINLOOP
; escribir CONFIG
CMD_NO_F movlw 'C'
cpfseq CMD
bra CMD_NO_C
clrf CRC
; leer dirección
rcall GET_TBLPTR
; leer datos
rcall RECEIVE
movwf TABLAT
; leer y comprobar CRC
rcall RECEIVE
movf CRC,F
bnz BAD_CRC
; guardar config en el latch
tblwt*
; configurar escritura
movlw (1<<EEPGD)|(1<<CFGS)|(1<<WREN)
; escribir configuración
rcall WRITE
; delay de 3,2ms para dejar que la PLL se estabilice, si la hemos activado
clrf TMR0L
btfss TMR0L,7
bra $-2
bra RECONFIGURE
; ejecutar programa
CMD_NO_C movlw 'R'
cpfseq CMD
bra CMD_NO_R
bra RUN_USER_PROGRAM
; keepalive (no-op)
CMD_NO_R movlw 'N'
cpfseq CMD
bra CMD_NO_N
bra MAINLOOP
CMD_NO_N ; comando incorrecto
movlw '?'
bra SEND_AND_RESET
BAD_CRC
movlw '!'
bra SEND_AND_RESET
; ==================================================
; FUNCIONES
; ==================================================
; --------------------------------------------------
; leer TBLPTR
; --------------------------------------------------
GET_TBLPTR
rcall RECEIVE
movwf TBLPTRU
rcall RECEIVE
movwf TBLPTRH
rcall RECEIVE
movwf TBLPTRL
return
; --------------------------------------------------
; procedimiento de escritura
; --------------------------------------------------
WRITE
movwf EECON1
movlw 0x55 ; procedimiento de control
movwf EECON2
movlw 0xAA
movwf EECON2
bsf EECON1,WR ; escritura
return
; --------------------------------------------------
; enviar caracter via uart
; --------------------------------------------------
UART_SEND
btfss TXSTA,TRMT
bra $-2
movwf TXREG
return
; --------------------------------------------------
; esperar a que se hayan enviado todos los caracteres, y resetear
; --------------------------------------------------
SEND_AND_RESET
rcall UART_SEND
btfss TXSTA,TRMT
bra $-2
reset
; --------------------------------------------------
; resetear timeout de recepción
; --------------------------------------------------
receive_cycles EQU ((6*256+3)*256+3)
RECEIVE_RESET_TO
movlw xtal/4/receive_cycles
btfsc USING_PLL
movlw xtal/receive_cycles
movwf RCV_CD_1
clrf RCV_CD_2
clrf RCV_CD_3
return
; --------------------------------------------------
; recibir caracter
; --------------------------------------------------
RECEIVE
rcall RECEIVE_RESET_TO
RECEIVE_CL2
clrf RCV_CD_2
RECEIVE_CL3
clrf RCV_CD_3
; recibir caracter sin reset del timeout
RECEIVE_NO_RESET
; comprobar
btfss PIR1,RCIF
bra RECEIVE_NO
movf RCREG,W
; actualizar CRC
addwf CRC,F
return
RECEIVE_NO
decfsz RCV_CD_3,F
bra RECEIVE_NO_RESET
decfsz RCV_CD_2,F
bra RECEIVE_CL3
decfsz RCV_CD_1,F
bra RECEIVE_CL2
; timeout!
bra RUN_USER_PROGRAM
; --------------------------------------------------
; ejecutar codigo de usuario
; --------------------------------------------------
RUN_USER_PROGRAM
; comprobar modo de interrupción
btfss SW_FLAGS,0
bra NO_INTERRUPT_SETUP
; configurar interrupción de recepción
bcf PIR1,RCIF
bsf PIE1,RCIE
bsf INTCON,PEIE
bsf INTCON,GIE
NO_INTERRUPT_SETUP
; comprobar modo UART
btfsc SW_FLAGS,1
bra NO_UART_RESET
; resetear UART
clrf RCSTA
clrf TXSTA
clrf BAUDCON
clrf SPBRGH
clrf SPBRG
NO_UART_RESET
; resetear registros
clrf STKPTR
clrf TBLPTRU
clrf TBLPTRH
clrf TBLPTRL
clrf TABLAT
clrf FSR0H
setf T0CON
clrf TMR0H
clrf EECON1
clrf EEDATA
clrf EEADR
; saltar a codigo de usuario
bra USER_RST
; --------------------------------------------------
; interrupción de usuario, comprobar recepción UART
; --------------------------------------------------
USERINT_HANDLER
; comprobar si es una interrupción de UART
btfss PIR1,RCIF
bra RETURN_TO_USER
; leer byte recibido
movf RCREG,W
sublw 'B'
; si no es 'B', continuar comprobando RCIF hasta que se ponga a 0
bnz USERINT_HANDLER
; 'B' recibido, resetear al bootloader
reset
RETURN_TO_USER ; hacer sitio en la pila
PUSH
; guardar la dirección de el vector de interrupción de usuario
MOVLWF TOSU,upper USER_INTH
MOVLWF TOSH,high USER_INTH
MOVLWF TOSL,low USER_INTH
; saltar al codigo de usuario, restaurando los valores de STATUS,W,BSR de camino
return FAST
; ==================================================
; DATOS BOOTLOADER
; ==================================================
BOOTLOADER_DATA CODE 0x7FC0
USER_RST reset
nop
nop
nop
USER_INTH
reset
nop
nop
nop
nop
nop
nop
nop
SOFTWARE_FLAGS CODE SW_FLAGS_ADDR
dw 0x0000
END