; BLHeli bootloader for SiLabs MCUs. Based upon AVRootloader (copyright HR)

XTAL					EQU	25000000	

IF MCU_48MHZ == 2
BOOT_START      EQU 0F000h ; Bootloader segment address
ELSE
BOOT_START			EQU	1C00h	; Bootloader segment address
ENDIF
BOOT_DELAY			EQU	XTAL/4	; About 250ms (don't set to fast to avoid connection problems)
BOOT_BAUDRATE			EQU	19200	; Only used if no baudrate detection activated, XTAL is than important
BOOT_VERSION			EQU	6		; Version 6 (must be not changed)
BOOT_PAGES			EQU	1		; Number of flash segments for bootloader

UART_LOOP				EQU 	26		; Depends upon timing of putc, getc 
BAUDTIME				EQU 	((XTAL/BOOT_BAUDRATE)/3)-UART_LOOP

SUCCESS				EQU 	030h
ERRORVERIFY			EQU 	0C0h
ERRORCOMMAND			EQU 	0C1h
ERRORCRC				EQU 	0C2h
ERRORPROG				EQU 	0C5h

POLYNOM				EQU 	0A001h		; CRC Polynom

Xl					EQU 	R0			; Temporary X
Xh					EQU 	R1
Paral				EQU 	R2			; Params for UART
Parah				EQU 	R3	
Cmdl					EQU 	R4			; Commands
Cmdh					EQU 	R5	
Cntl					EQU 	R6			; Baudtime
Cnth					EQU 	R7

DSEG AT 20h					
Bit_Reg:				DS	1			; Bit storage register
Byte_Reg:				DS	1			; Byte storage register
Crcl:				DS 	1			; CRC 16Bit
Crch:				DS 	1
Baudl:				DS 	1			; Baudtime
Baudh:				DS 	1
Bit_Cnt:				DS 	1			; Counter in UART loops
Byte_Cntl:			DS 	1			; Generic counter
Byte_Cnth:			DS 	1			
BL_Flash_Key_1:		DS	1			; Flash keys
BL_Flash_Key_2:		DS	1

CSEG AT BOOT_START		; Bootloader start
init:clr	IE_EA
	; Select register bank 0 for main program routines
	clr	PSW.3				; Select register bank 0 for main program routines	
	; Disable the WDT.
	mov	WDTCN, #0DEh			; Disable watchdog
	mov	WDTCN, #0ADh		
	; Initialize stack
	mov	SP, #0c0h				; Stack = 64 upper bytes of RAM
	; Initialize clock
	mov	CLKSEL, #00h			; Set clock divider to 1
	; Initialize VDD monitor
IF MCU_48MHZ < 2
	orl	VDM0CN, #080h    		; Enable the VDD monitor
ENDIF
	mov	Baudl, #38h  			; Wait 100us
	mov	Baudh, #03h
	acall waitf
	; Initialize flash keys
	mov	BL_Flash_Key_1, #0A5h	; First key code
	mov	BL_Flash_Key_2, #0F1h	; Second key code
	; Initialize ports
	orl	RTX_MDIN, #(1 SHL RTX_PIN)		; Set digital
	anl	RTX_MDOUT, #NOT(1 SHL RTX_PIN)	; Disable pushpull
	setb	RTX_PORT.RTX_PIN				; Set data high
	mov	RTX_SKIP, #0FFh
	mov	XBR2, #40h;					; Enable crossbar
	; Set number of connect attempts before exiting bootloader
	mov	Cmdh, #250

	; Identifier scanning
abd:	mov	Xl, #(low(BOOT_DELAY / 6)+1)
	mov	Xh, #(high(BOOT_DELAY / 6)+1)
	mov	Cmdl, #(high((BOOT_DELAY / 6) SHR 8)+1)
	mov	Crcl, #0
	mov	Crch, #0
	mov	DPTR, #BOOT_SIGN
	mov	Parah, #(BOOT_MSG - BOOT_SIGN)
	mov	Baudl, #low(BAUDTIME)
	mov	Baudh, #high(BAUDTIME)

wait_for_low:
	jnb	RTX_PORT.RTX_PIN, ($+5)
	ajmp wait_for_low

	; Identifier (BOOT_SIGN) scanning with timeout and checksum
id1:	jb	RTX_PORT.RTX_PIN, id3	; Look for high
	djnz	Xl, id1				; Subtract 1 from X (BOOT_DELAY)
	djnz	Xh, id1
	djnz	Cmdl, id1 

	ajmp	exit

id3:	jnb	RTX_PORT.RTX_PIN, id4	; Look for low
	djnz	Xl, id3				; Subtract 1 from X (BOOT_DELAY)
	djnz	Xh, id3
	djnz	Cmdl, id3

	ajmp	exit

id4:	acall getx				; Read character
	clr	A			
	movc A, @A+DPTR			; Load BOOT_SIGN character
	inc	DPTR
	clr	C
	subb	A, Paral				; Compare with read character
	jz	id5
	djnz	Cmdh, abd				; Retry if not last connect attempt
	ajmp	exit

id5:
	djnz	Parah, id1

	acall getw				; Read CRC
	jz	($+4)				; Check CRC
	ajmp	abd
	
	; Send info about chip/bootloader (BOOT_MSG + BOOT_INFO)
	mov	Parah, #((BOOT_INFO - BOOT_MSG) + 4)
in1:	clr	A			
	movc A, @A+DPTR			; Load character
	mov	Paral, A
	inc	DPTR
	acall putc
	djnz	Parah, in1


	; Main commandloop
	; 0=Run/restart 
	; 1=Program flash, 2=Erase flash, 3=Read flash
	; 0xFF=Set address, 0xFE=Set buffer, 0xFD=Keep alive 
main:mov	Paral, #SUCCESS
mai1:acall putc
	mov	Crcl, #0				; Reset CRC
	mov	Crch, #0
	acall getw				; Get command
	mov	A, Paral
	mov	Cmdl, A
	mov	A, Parah
	mov	Cmdh, A
	clr	C
	mov	A, Cmdh
	subb	A, #0FEh
	jc	mai2					; Jump if not set address or set buffer	

	acall getw				; Address or number of bytes
	mov	Byte_Cntl, Paral		; Store number of bytes for set buffer
	mov	Byte_Cnth, Parah	
	mov	A, Cmdh
	jnb	ACC.0, mai2			; Jump if set buffer

	mov	DPL, Paral			; Store flash address (for set address)
	mov	DPH, Parah

mai2:acall getw				; Get CRC
	mov	Paral, #ERRORCRC
	jnz	mai1
	clr	C
	mov	A, Cmdh
	subb	A, #0FEh
	jz	setbuf				; If command is set buffer, receive data
	jnc	main

	cjne	Cmdh, #0, mai4			; Jump if command != 0 (and not set buffer)

	; Run application/restart bootloader
	mov	A, Cmdl
	jz	rst
exit:mov	Bit_Access, #0			; Clear variable used by flash lock detect
	mov	Bit_Access_Int, #0FFh	; Set variable to indicate that program execution came from bootloader
	mov	BL_Flash_Key_1, #0		; Set flash keys to invalid values
	mov	BL_Flash_Key_2, #0
	ljmp	0000h
rst:	ajmp	init

	; Set buffer
setbuf:mov Xl, Byte_Cntl			; Set number of bytes 
	mov	Xh, Byte_Cnth
	inc	Xl	
	inc	Xh	
set4:djnz	Xl, set5
	djnz	Xh, set5
	ajmp	set6

set5:acall getc				; Receive data
	mov	A, Paral
	movx	@Xl, A				; Store data in XRAM
	ajmp	set4

set6:inc	Cmdh	
	ajmp	mai2

mai4:clr	C
	mov	A, Cmdh
	subb	A, #3
	jnc	mai5					; Jump if command >= 3

	; Program/erase
	mov	A, Cmdh
	mov	C, ACC.0
	mov	Bit_Reg.0, C	
	mov	Paral, #ERRORPROG
	clr	C
	mov	A, DPL
	subb	A, #low(BOOT_START)
	mov	A, DPH
	subb	A, #high(BOOT_START)
	jnc	mai1					; Jump if in bootloader segment
	jb	Bit_Reg.0, pro3		; Jump if program command

	; Erase flash
	orl	PSCTL, #02h			; Set the PSEE bit
	orl	PSCTL, #01h			; Set the PSWE bit
	mov	FLKEY, BL_Flash_Key_1	; First key code
	mov	FLKEY, BL_Flash_Key_2	; Second key code
	movx	@DPTR, A
	jnb	Bit_Reg.0, pro6		; Jump if erase command

	; Program flash
pro3:mov 	Xl, Byte_Cntl			; Set number of bytes 
	mov	Xh, Byte_Cnth
	inc	Xl	
	inc	Xh	
	orl	PSCTL, #01h			; Set the PSWE bit
	anl	PSCTL, #0FDh			; Clear the PSEE bit
pro4:djnz	Xl, pro5
	djnz	Xh, pro5
	ajmp	pro6

pro5:
	clr	C
	mov	A, DPH				; Check that address is not in bootloader area
IF MCU_48MHZ == 2
  subb  A, #0F0h
ELSE
	subb	A, #1Ch
ENDIF
	jc	($+5)

	inc	DPTR					; Increment flash address
	ajmp	pro4

	movx	A, @Xl				; Read from XRAM		
	mov	FLKEY, BL_Flash_Key_1	; First key code
	mov	FLKEY, BL_Flash_Key_2	; Second key code
	movx	@DPTR, A				; Write to flash
	inc	DPTR					; Increment flash address
	ajmp	pro4

pro6:anl	PSCTL, #0FCh			; Clear the PSEE and PSWE bits
	ajmp	main					; Successfully done erase or program

	; Read flash
mai5:mov	Paral, #ERRORCOMMAND	; Illegal command
	cjne	Cmdh, #3, mai6			; Jump if not read flash command

rd1:	clr	A
	movc	A, @A+DPTR			; Read from flash
	inc	DPTR					; Increment flash address
	mov	Paral, A
	acall putp
	djnz	Cmdl, rd1 			; Decrement bytes to read	

	acall putw				; CRC
	ajmp	main

mai6:ajmp	mai1




	; Send char with crc
putw:mov	Paral, Crcl
	mov	Parah, Crch
	acall putc
	mov	A, Parah
	mov	Paral, A
putp:mov	A, Paral
	xrl	Crcl, A
	mov	Bit_Cnt, #8
put1:clr	C
	mov	A, Crch
	rrc	A
	mov	Crch, A
	mov	A, Crcl
	rrc	A
	mov	Crcl, A
	jnc	put2

	xrl	Crch, #high(POLYNOM)
	xrl	Crcl, #low(POLYNOM)

put2:djnz	Bit_Cnt, put1


	; Send char
putc:acall waitf
	acall waitf
	mov	Bit_Cnt, #10
	mov	A, Paral
	cpl	A
put3:jb	Bit_Reg.1, ($+5)
	setb	RTX_PORT.RTX_PIN		; Set pin high
	jnb	Bit_Reg.1, ($+5)
	clr	RTX_PORT.RTX_PIN		; Set pin low
	acall waitf							
	clr	C
	rrc	A
	jc	put4		
						
	clr	Bit_Reg.1

put4:djnz	Bit_Cnt, put3

	ret


	; Receive char/word
getw:acall getc
	mov	A, Paral
	mov	Parah, A
getc:jb	RTX_PORT.RTX_PIN, ($+5)	; Wait for high
	ajmp	getc

get1:jnb	RTX_PORT.RTX_PIN, ($+5)	; Wait for low
	ajmp	get1

getx:mov	Bit_Cnt, #8
	mov	Cntl, Baudl
	mov	Cnth, Baudh
	clr	C
	mov	A, Cnth				; Wait half a baud
	rrc	A
	mov	Cnth, A
	mov	A, Cntl
	rrc	A
	mov	Cntl, A
	acall waith
get2:acall waitf				; Wait one baud							
	clr	C
	mov	A, Paral
	rrc	A
	jnb	RTX_PORT.RTX_PIN, ($+5)			
	orl	A, #080h

	mov	Paral, A
	jnb	ACC.7, ($+6) 
	xrl	Crcl, #low(POLYNOM)

	clr	C
	mov	A, Crch
	rrc	A
	mov	Crch, A
	mov	A, Crcl
	rrc	A
	mov	Crcl, A
	jnc	get3				

	xrl	Crch, #high(POLYNOM)
	xrl	Crcl, #low(POLYNOM)

get3:djnz	Bit_Cnt, get2

	mov	A, Crcl 
	xrl	A, Crch 
	xrl	A, Crch 
	mov	Crcl, A
	ret


	; UART delays										
waitf:mov	Cntl, Baudl	
	mov	Cnth, Baudh
waith:inc Cntl
	inc	Cnth
wait1:djnz Cntl, wait1
	djnz Cnth, wait1

	setb	Bit_Reg.1
	ret									


BOOT_SIGN:	DB	"BLHeli"	

BOOT_MSG:		DB	"471d"		; Interface-MCU_BootlaoderRevision

BOOT_INFO:	DB	SIGNATURE_001, SIGNATURE_002, BOOT_VERSION, BOOT_PAGES

