RegBase:	equ	$0000
;
	include	"..\DP256Reg\SCI.Asm"
	include	"..\DP256Reg\CRG.Asm"
	include	"..\DP256Reg\MEBI.Asm"
	include	"..\DP256Reg\Flash.Asm"
	include	"..\DP256Reg\MMC.Asm"
;
OscClk:	equ	8000000
Eclock:	equ	24000000		; final E-clock frequency (PLL)
RefClock:	equ	8000000
;
REFDVVal:	equ	(OscClk/RefClock)-1	
SYNRVal:	equ	(Eclock/RefClock)-1
	if	OscClk>12800000
FCLKDIVVal:	equ	(OscClk/200000/8)+FDIV8
	else
FCLKDIVVal:	equ	(OscClk/200000)
	endif
;
Baud115200:	equ	Eclock/16/115200
Baud57600:	equ	Eclock/16/57600		; value for baud register, based on clock frequency.
Baud38400:	equ	Eclock/16/38400
Baud9600:	equ	Eclock/16/9600		; value for baud register, based on clock frequency.
;
ATDDIEN0	equ	$008d
PortAD0:	equ	$008f
ATDDIEN1	equ	$012d
PortAD1	equ	$012f
PARTID:	equ	$001a
;
Dx128ID:	equ	$01
FlashStart:	equ	$8000	; start address of the flash window.
EEStart:	equ	$400
BootBlkSize:	equ	4096	; Erase protected bootblock size.
StackTop:	equ	$ff80	; stack location after RAM is moved.
RAMBoot256:	equ	$3000
RAMBoot128:	equ	$1000
SectorSize:	equ	512	; size of a Flash Sector.
PPAGESize:	equ	16384
;
mSDlyCnt:	equ	Eclock/4000
;
SRecLow256:	equ	$c0000
SRecLow128:	equ	$0e
SRecHi:	equ	$ff000
;
S0RecType:	equ	'0'
S1RecType:	equ	'1'
S2RecType:	equ	'2'
S8RecType:	equ	'8'
S9RecType:	equ	'9'
;
FEraseError:	equ	1	; Flash failed to erase.
SRecRngErr:	equ	2	; S-Record out of range
FlashPrgErr:	equ	3	; Flash programming error.
EEraseError:	equ	4	; EEPROM Failed to erase.
BadHexError:	equ	5	; Bad Hex Data.
CheckSumError:	equ	6	; S-Record checksum error
SRecDataErr:	equ	7	; Received S-Record contained an odd number of data bytes.
SRecAddrErr:	equ	8	; S-Record Address is odd.
SRecLenErr:	equ	9	; S-Record is too long.
;
;*************************************************************************************************************
;
;
	org	$f000
;
;
BootStart:	bra	OverGlobals
;
SRecLow:	dc.b	SRecLow256
NumFBlocks:	dc.b	3		; number of Flash blocks to perform a mass erase on.
PPAGEBase:	dc.b	$30
;
OverGlobals:	ldaa	#$03		; enable the digital input buffer for bit 0 & 1 of
	staa	ATDDIEN0		; A2D #0 port.
	nop
	nop
	ldaa	PortAD0		; get PortAD0 value.
	anda	#$03		; mask off all but bit 0.
	cmpa	#$01		; jump to internal EEPROM?
	beq	JumpEE		; yes.
;
	cmpa	#$03		; should we execute the bootloader?
	beq	Boot		; yes.
DEBUG12:	jmp	[Reset-BootBlkSize,pcr]	; no. jump to the users program pointed to by the the secondary
	                                         	; reset vector.
JumpEE:	ldab	PARTID
	cmpb	#Dx128ID
	beq	JumpEE128
	jmp	EEStart
JumpEE128:	ldab	#$20		; move the RAM to $2000 (so the EEPROM becomes visible).
	stab	INITRM
	jmp	EEStart		; then jump to the start of internal EEPROM (above the I/O registers).
;
Boot:	clr	COPCTL		; disable watchdog
;
BootCopy:	lds	#StackTop		; initialize the stack pointer
	ldx	#BootStart		; point to the start of the Flash bootloader in Flash.
	ldy	#RAMBoot256		; point to the start of on-chip RAM.
	ldab	PARTID
	cmpb	#Dx128ID
	bne	On256KPart
	ldy	#RAMBoot128		; point to the start of on-chip RAM.
On256KPart:	ldd	#BootLoadEnd		; calculate the size of the bootloader code.
	subd	#BootStart
MoveMore:	movb	1,x+,1,y+		; move a byte of the bootloader into RAM.
	dbne	d,MoveMore		; dec byte count, move till done.
;
	ldab	#$e0+RAMHAL		; write to the INITRM register to overlay the Flash bootblock with RAM.
;
	if	*&$0001<>0
	nop
	endif
;
	stab	>INITRM		; this instruction MUST use extended addressing an be aligned to an even byte boundary.
;
	ldab	PARTID
	cmpb	#Dx128ID
	bne	On256KPart2
	ldab	#SRecLow128
	stab	SRecLow,pcr
	ldab	#1
	stab	NumFBlocks,pcr
	ldab	#$38
	stab	PPAGEBase
;
On256KPart2:	ldab	#REFDVVal		; set the REFDV register to give us an 8.0 MHz reference.
	stab	REFDV
	ldab	#SYNRVal		; set the SYNR register to give us a 24.0 MHz E-clock.
	stab	SYNR
	nop			; nops required for bug in initial silicon.
	nop
	nop
	nop
	brclr	CRGFLG,#LOCK,*		; wait here till the PLL is locked.	
	bset	CLKSEL,#PLLSEL		; switch the bus clock to the PLL.
;
	ldab	#FCLKDIVVal		; value for the Flash & EEPROM clock divider register.
	stab	FCLKDIV
	stab	ECLKDIV
;
	ldd	#Baud9600		; set SCI to 9600 baud @ 24.0 MHz
	jsr	SCIInit,pcr		; go initialize the SCI.
	cli
;
	leax	SignOn,pcr		; get the bootloader signon message
	jsr	OutStr,pcr		; send it to the terminal.
CmdLoop:	clr	ErrorFlag,pcr		; clear the global error flag.
	leax	BLPrompt,pcr		; get the bootloader prompt
	bsr	PromptResp		; go display the prompt & get a 1 character response.
	cmpb	#$61		; do a range check. less than 'a'?
	blo	CmdLoop		; yes. just re-display the prompt.
	cmpb	#$64		; greater than 'd'?
	bhi	CmdLoop		; yes. just re-display the prompt.
	andb	#$0f		; no. mask off the upper nybble.
	decb			; reduce by 1 for indexing into the command offset table.
	lslb			; mult by 2 as each cmd table entry is a 2 byte address.
	leax	CmdTable,pcr		; point to the command table.
	ldd	b,x		; get the offset from the beginning of the table to the command.
	jsr	d,x		; execute the command.
	ldab	ErrorFlag,pcr		; error executing the command?
	beq	CmdLoop		; no. go display the prompt, wait for entered command.
	decb			; subtract 1 from the error number for indexing.
	lslb			; multiply by 2 because each address in the table is 2 bytes.
	leax	ErrorTable,pcr		; yes. point to the error table.
	ldd	b,x		; get the offset from the start of the table to the string.
	leax	d,x		; calculate the address of the error string from the table.
	jsr	OutStr,pcr		; send error message to the terminal.
	bsr	CmdAbort
	bra	CmdLoop		; go display the prompt.
;
;*************************************************************************************************************
;
mSCount:	ds.w	1
;
CmdAbort:	bra	ClrmSCount		; begin by clearing the mS counter.
CmdAbort1:	jsr	SCIGetBuf,pcr		; any characters in the Rx buffer?
	beq	CmdAbort2		; no. go delay for 1 mS, check the mS count.
	jsr	getchar,pcr		; yes. get a character.
ClrmSCount:	clra			; zero to acc d.
	clrb
	std	mSCount,pcr		; init the mS counter
	bra	CmdAbort1		; go see if there are any more.
;
CmdAbort2:	bsr	mSDelay		; delay 1 mS.
	ldd	mSCount,pcr		; get the mS count.
	addd	#1		; add 1 to the count.
	std	mSCount,pcr		; save result.
	cpd	#500		; delayed 500 mS w/o receiving a character?
	bls	CmdAbort1		; no. go see if we received any characters during the 1 mS delay.
	rts			; yes. return.
;
mSDelay:	ldx	#mSDlyCnt		; load 1mS delay count into x
mSDelayLoop:	nop			; decrement count
	dbne	x,mSDelayLoop		; loop till done.
	rts			; return.
;
;*************************************************************************************************************
;
PromptResp:	jsr	OutStr,pcr		; send prompt to the terminal.
	jsr	getchar,pcr		; go get the user's choice.
	jsr	putchar,pcr		; echo it.
	pshb			; save it.
	leax	CrLfStr,pcr		; go to the next line.
	jsr	OutStr,pcr
	pulb			; restore the entered character.
	rts
;
;*************************************************************************************************************
;
CmdTable:	dc.w	EraseFlash-CmdTable	; cmd table entry for 'Erase Flash' command.
	dc.w	ProgFlash-CmdTable	; cmd table entry for 'Program Flash' command.
	dc.w	SetBaud-CmdTable	; cmd table entry for 'Set Baud Rate' command.
	dc.w	EraseEE-CmdTable	; cmd table entry for 'Erase EEPROM' command.
;
SignOn:	dc.b	$0d,$0a,"D-Bug12 Bootloader v1.0.0",$0d,$0a,0
;
BLPrompt:	dc.b	$0d,$0a,"a) Erase Flash",$0d,$0a
	dc.b	"b) Program Flash",$0d,$0a
	dc.b	"c) Set Baud Rate",$0d,$0a
	dc.b	"d) Erase EEPROM",$0d,$0a
	dc.b	"? ",0
;
CrLfStr:	dc.b	$0d,$0a,0
;
BaudPrompt:	dc.b	$0d,$0a,"1) 9600",$0d,$0a
	dc.b	"2) 38400",$0d,$0a
	dc.b	"3) 57600",$0d,$0a
	dc.b	"4) 115200",$0d,$0a
	dc.b	"? ",0
;
BaudChgPrompt:	dc.b	"Change Terminal BR, Press Return",0
;
ErrorTable:	dc.w	FNotErasedStr-ErrorTable
	dc.w	SRecRngStr-ErrorTable
	dc.w	FlashPrgErrStr-ErrorTable
	dc.w	ENotErasedStr-ErrorTable
	dc.w	BadHexErrorStr-ErrorTable
	dc.w	ChkSumErrorStr-ErrorTable
	dc.w	SRecDataErrStr-ErrorTable
	dc.w	SRecAddrErrStr-ErrorTable
	dc.w	SRecLenErrStr-ErrorTable
;
FNotErasedStr:	dc.b	$0d,$0a,"Flash Not Erased",$0d,$0a,0
SRecRngStr:	dc.b	$0d,$0a,"S-Record out of Range",$0d,$0a,0
FlashPrgErrStr:	dc.b	$0d,$0a,"Flash Programming Error",$0d,$0a,0
ENotErasedStr:	dc.b	$0d,$0a,"EEPROM Not Erased",$0d,$0a,0
BadHexErrorStr:	dc.b	$0d,$0a,"Bad Hex Data",$0d,$0a,0
ChkSumErrorStr:	dc.b	$0d,$0a,"S-Record Checksum Error",$0d,$0a,0
SRecDataErrStr:	dc.b	$0d,$0a,"S-Record Code/Data Length Is Odd",$0d,$0a,0
SRecAddrErrStr:	dc.b	$0d,$0a,"S-Record Address Is Odd",$0d,$0a,0
SRecLenErrStr:	dc.b	$0d,$0a,"S-Record Code/Data Field Too Long",$0d,$0a,0
;
;*************************************************************************************************************
;
EraseEE:	ldab	#ERASE+MASS		; perform a bulk erase.
	std	EEStart		; latch address for erase command.
	stab	ECMD
	ldab	#CBEIF
	stab	ESTAT		; initiate the erase command.
	brclr	ESTAT,#PVIOL+ACCERR,EEErCmdOK	; continue if the privliage violation & Access error flags are clear.
	ldaa	#EEraseError
	bra	SaveEEError
EEErCmdOK:	brclr	ESTAT,#CCIF,*		; wait until the command has completed.
	ldab	#ERVER+MASS		; perform an erase verify.
	std	EEStart		; latch address for erase verify command.
	stab	ECMD
	ldab	#CBEIF
	stab	ESTAT		; initiate the erase command.
	brclr	ESTAT,#PVIOL+ACCERR,EEVerfCmdOK	; indicate failure if the privliage violation or Access error flags are set.
EENotErased:	ldaa	#EEraseError
	bra	SaveEEError
EEVerfCmdOK:	brclr	ESTAT,#CCIF,*		; wait until the command has completed.
	brclr	ESTAT,#BLANK,EENotErased	; flag a not erased error if the BLANK bit did not set.
	ldab	#BLANK		; clear the BLANK status bit.
	stab	ESTAT
	clra
SaveEEError:	staa	ErrorFlag,pcr		; put error code where pod can access it.
	rts			; if we fall through, we automatically return a non-zero condition.
;
;*************************************************************************************************************
;
SetBaud:	leax	BaudPrompt,pcr		; get the bootloader prompt
	jsr	PromptResp,pcr		; go display the prompt & get a 1 character response.
	cmpb	#$31		; do a range check. less than '1'?
	blo	SetBaud		; yes. just re-display the prompt.
	cmpb	#$34		; greater than '4'?
	bhi	SetBaud		; yes. just re-display the prompt.
	andb	#$0f		; no. mask off the upper nybble.
	decb
	lslb
	leax	BaudTable,pcr
	ldd	b,x
	pshd
	leax	BaudChgPrompt,pcr
	jsr	OutStr,pcr		; send it to the terminal.
	brclr	SCI0SR1,#TC,*		; wait until the last character is sent until we change the baud rate.
	puld
	std	SCI0BD
	jsr	getchar,pcr		; go get the user's choice.
	leax	CrLfStr,pcr		; go to the next line.
	jsr	OutStr,pcr
	rts
;
BaudTable:	dc.w	Baud9600
	dc.w	Baud38400
	dc.w	Baud57600
	dc.w	Baud115200
;
;*************************************************************************************************************
;
ProgFlash:	ldab	#PVIOL+ACCERR		; if either the PVIOL or ACCERR bit is set from a previous error,
	stab	FSTAT		; reset them so we can program the Flash.
	bra	FSkipFirst		; don't send the progress character the first time.
FSendPace:	ldab	#'*'		; the ascii asterisk is the progress character.
	jsr	putchar,pcr		; let the user know we've processed an S-Record.
FSkipFirst:	jsr	GetSRecord,pcr		; go get an S-Record.
	lbne	ProgRtn		; non-zero condition means there was an error
	brclr	DataBytes,pcr,#$01,DataLOK	; is the received S-Record length even?
	ldaa	#SRecDataErr		; no. report the error.
	bra	ProgDone		; stop programming.
DataLOK:	brclr	LoadAddr+2,pcr,#$01,SRecOK	; is the received S-Record address even?
	ldaa	#SRecAddrErr		; no. report the error.
	bra	ProgDone		; stop programming.
;
SRecOK:	ldab	RecType,pcr		; check the record type.
	cmpb	#S1RecType		; S1 record received?
	bne	ChckNext		; no. check for S0, S2, S8 & S9 records.
	ldaa	#SRecRngErr		; yes. only S2 records w/ load addresses $C0000 - $FEFFF allowed.
	bra	ProgDone		; save error & return.
;	
ChckNext:	cmpb	#S9RecType		; was it an S9 record?
	beq	ProgRtn		; yes. we're done.
	cmpb	#S8RecType		; was it an S8 record?
	beq	ProgRtn		; yes. we're done.
	cmpb	#S0RecType		; no. was it an S0 record?
	beq	FSendPace		; yes. just ignore it.
;
	ldab	LoadAddr,pcr		; it was an S2 record. Get the high byte of the 24-bit address.
	cmpb	SRecLow,pcr		; less than $c0000 or $e0000?
	bhs	ChkHiLimit		; no. check the upper limit.
BadSRecRng:	ldaa	#SRecRngErr		; yes. S-Record out of range.
	bra	ProgDone		; save the error code & return.
;
ChkHiLimit:	ldab	DataBytes,pcr		; get the number of bytes in the S-Record.
	clra			; zero extend it.
	addd	LoadAddr+1,pcr		; add in the lower 16-bits of the 24-bit address.
	tfr	d,x		; save rthe result in X.
	ldab	LoadAddr,pcr		; get the upper 8-bits of the 24-bit address.
	adcb	#$00		; add in possible carry from lower 16-bits.
	cmpb	#SRecHi>>16		; greater than $0fxxxx?
	blo	AddrOK		; no. S-Record within range.
	cpx	#SRecHi&$ffff		; yes. check the lower 16- bits. Out of range?
	bhi	BadSRecRng		; yes. S-Record out of range.
;
AddrOK:	ldab	LoadAddr,pcr		; get upper 8-bits of 24-bit load address.
	exg	b,y		; zero extend b into y for the 32-bit divide
	ldd	LoadAddr+1,pcr		; get the lower 16-bits of 24-bit load address.
	ldx	#PPAGESize		; divide the load address by the PPAGE window size.
	ediv
	addd	#FlashStart		; add the PPAGE window start address to the remainder (this gives the PPAGE window load address).
	exg	d,y		; lower byte of the quotent is the PPAGE value.
	stab	PPAGE
	lsrb			; calculate the value of the block select bits based
	lsrb			; on bits 3:2 of the PPAGE register value.
	comb
	andb	#$03		; mask off all but the lower 2 bits.
	stab	FCNFG		; select the block to erase.
	sty	PPAGEWAddr,pcr		; save the PPAGE window address.
	jsr	ProgFBlock,pcr		; no. that means it was an S1 record. go program the data into Flash.
	lbeq	FSendPace		; zero condition means all went ok.
	ldaa	#FlashPrgErr
ProgDone:	staa	ErrorFlag,pcr		; put error code where pod can access it.
ProgRtn:	rts			; if we fall through, we automatically return a non-zero condition.
;
;*************************************************************************************************************
;
	offset	0
;
NumWords:	ds	1
;LocalSize:	set	*
;
ProgFBlock:	ldab	DataBytes,pcr		; get the block size.
	lsrb			; divide the byte count by 2 since we program a word at a time.
	pshb			; allocate the local.
	ldx	PPAGEWAddr,pcr		; get the S-Record (Flash) load address.
	leay	SRecData,pcr		; point to the received S-Record data.
ProgLoop:	ldd	2,y+		; get a word from the buffer.
	std	2,x+		; latch the address & data into the Flash program/erase buffers.
	ldab	#PROG		; get the program command.
	stab	FCMD		; write it to the command register.
	ldab	#CBEIF		; start the command by writing a 1 to CBEIF.
	stab	FSTAT
	ldab	FSTAT		; check to see if there was a problem executing the command.
	bitb	#PVIOL+ACCERR		; if either the PVIOL or ACCERR bit is set,
	bne	Return		; return.
	brclr	FSTAT,#CBEIF,*		; wait here till the command buffer is empty.
	dec	NumWords,sp		; any more words to program?
	bne	ProgLoop		; yes. continue until done.
	brclr	FSTAT,#CCIF,*		; no. wait until all commands complete.
;
	ldab	DataBytes,pcr		; get the block size.
	lsrb			; divide the byte count by 2 since we verify a word at a time.
	stab	NumWords,sp	
	ldx	PPAGEWAddr,pcr		; get the S-Record (Flash) load address.
	leay	SRecData,pcr		; point to the received S-Record data.
VerfLoop:	ldd	2,y+		; get a word from the buffer.
	cpd	2,x+		; same as the word in Flash?
	bne	Return		; no. return w/ an error (!= condition).
	dec	NumWords,sp		; yes. done comparing all words?
	bne	VerfLoop		; no. compare some more.
;
Return:	pulb			; deallocate the local.
	rts			; return.
;
;
;*************************************************************************************************************
;
	offset	0
;
BlockCnt:	ds.b	1		; number of 64K blocks to erase.
;
;LocalSize:	set	*
;
EraseFlash:	ldab	NumFBlocks,pcr
	pshb
	ldab	PPAGEBase,pcr
EraseLoop:	stab	PPAGE		; write the PPAGE register to allow writes to the proper Flash block.
	lsrb			; calculate the value of the block select bits based
	lsrb			; on bits 3:2 of the PPAGE register.
	comb
	andb	#$03
	stab	FCNFG		; select the block to erase.
	ldx	#FlashStart		; latch address for erase command
	ldab	#ERASE+MASS		; perform a bulk erase.
	bsr	EraseCmd
	bne	SaveEFError
	ldab	#ERVER+MASS		; perform an erase verify.
	std	0,x		; latch address for erase verify command.
	stab	FCMD		; put command in the command buffer.
	ldab	#CBEIF
	stab	FSTAT		; initiate the erase command.
	brclr	FSTAT,#PVIOL+ACCERR,VerfCmdOK	; indicate failure if the privliage violation or Access error flags are set.
NotErased:	ldaa	#FEraseError
	bra	SaveEFError
VerfCmdOK:	brclr	FSTAT,#CCIF,*		; wait until the command has completed.
	brclr	FSTAT,#BLANK,NotErased	; flag a not erased error if the BLANK bit did not set.
	ldab	#BLANK		; clear the BLANK status bit.
	stab	FSTAT
	ldab	PPAGE		; get the current PPAGE value.
	addb	#$04		; add 4 to select the next 64K Flash block.
	dec	BlockCnt,sp		; done with 3 of the 64K blocks?
	bne	EraseLoop		; no.
	bsr	EraseBlk0		; block 0 must be erased seperately because it contains the bootblock.
SaveEFError:	staa	ErrorFlag,pcr		; put error code where pod can access it.
FEEDone:	pulb
	rts			; return.
;
;	EraseBlk0 erases Flash block 0 a sector (512 bytes) at a time because the bootblock is protected.
;
	offset	0
;
PPAGECnt:	ds.b	1		; number of 16K PPAGE windows that will be completely erased.
;
;LocalSize:	set	*
;
EraseBlk0:	movb	#3,1,-sp		; 3 16K PPAGE windows will be completely erased.
	stab	PPAGE		; PPAGE for first 16K page of block 0.
	clr	FCNFG		; set block select bits to 0.
EraseBlk0Loop:	ldx	#FlashStart		; point to the start of the PPAGE window.
	ldab	#PPAGESize/SectorSize	; number of sectors in a PPAGE window.
	bsr	EraseSectors		; go erase the PPAGE window a sector at a time.
	bne	BadBlk0		; non-zero value returned in A indiciates a sector didn't erase.
	inc	PPAGE		; go to the next PPAGE.
	dec	PPAGECnt,sp		; done with all full PPAGE blocks?
	bne	EraseBlk0Loop		; no. erase more blocks.
	ldx	#FlashStart		; yes. point to the start of the PPAGE window.
	ldab	#(PPAGESize-BootBlkSize)/SectorSize ; number of sectors in PPAGE $3F minus the bootblock.
	bsr	EraseSectors		; erase all sectors outside the bootblock.
BadBlk0:	pulb			; remove the page count from the stack.
	rts

;
;	Erases 'b' (accumulator) sectors beginning at address 'x' (index register)
;
EraseSectors:	exg	b,y		; put the sector count in y.
EraseSectLoop:	ldab	#ERASE		; perform a sector erase.
	bsr	EraseCmd
	beq	DoEraseVerf		; if no problem with the erase command, do a verify.
Rtn:	rts			; if problem, return with an error code in a.
DoEraseVerf:	bsr	VerfSector
	bne	Rtn		; if problem, return with an error code in a.
	leax	SectorSize,x		; point to the next sector.
	dbne	y,EraseSectLoop		; continue to erase remaining sectors.
	rts			; done. return.
;
;	Erases a block or sector of Flash
;
EraseCmd:	std	0,x		; latch address for erase command.
	stab	FCMD
	ldab	#CBEIF
	stab	FSTAT		; initiate the erase command.
	brclr	FSTAT,#PVIOL+ACCERR,EraseCmdOK	; continue if the privliage violation & Access error flags are clear.
	ldaa	#FEraseError
	rts
EraseCmdOK:	brclr	FSTAT,#CCIF,*		; wait until the command has completed.
	clra
	rts
;
;	Verify that a sector was properly erased
;	Must verify a word at a time because the built in verify command only works on a block (64K)
;
VerfSector:	pshx			; save the base address of the sector.
	pshy			; save the sector count.
	ldy	#SectorSize/2		; we'll check 2 bytes at a time.
VerfSectLoop:	ldd	2,x+		; get a byte from the sector.
	ibeq	d,WordOK
	ldaa	#FEraseError
	bra	SectRtn
WordOK:	dbne	y,VerfSectLoop		; yes. dec the sector word count.
	clra
SectRtn:	puly			; restore the sector count.
	pulx			; restore the base address of the sector.
	rts			; return.
;
;*************************************************************************************************************
;
	offset	0
;
SRecBytes:	ds.b	1		; number of bytes in the address, data & checksum fields.
CheckSum:	ds.b	1		; used for calculated checksum.
;
;LocalSize:	set	*
;
GetSRecord:	equ	*
	leas	-2,sp		; allocate stack space for variables.
	leax	LoadAddr,pcr		; point to the code/data buffer.
	clr	0,x		; clear the upper byte of the 24 bit address (in case we receive a 16-bit address).
LookForSOR:	jsr	getchar,pcr		; get a character from the receiver.
	cmpb	#'S'		; start-of-record character?
	bne	LookForSOR		; no. go back & get another character.
	jsr	getchar,pcr		; yes. we found the start-of-record character (ASCII 'S')
	cmpb	#S0RecType		; found an S0 (header) record?
	bne	CheckForS9		; no. go check for an S9 record.
	stab	RecType,pcr		; yes. set the record type to '0'
	ldaa	#3
	staa	DataBytes,pcr
	bra	RcvSRec		; go receive the S0 record.
;
CheckForS9:	cmpb	#S9RecType		; found an S9 (end) record?
	bne	ChkForS1		; no. go check for an S1 record.
	bra	Addr16		; go receive the S9 record.
;
ChkForS1:	cmpb	#S1RecType		; found an S1 (code/data) record?
	bne	ChkForS2		; no. false start-of-record character received. go check for another.
Addr16:	inx
	ldaa	#3
	staa	DataBytes,pcr
	bra	SaveRecType		; go receive the S9 record.
;
ChkForS2:	cmpb	#S2RecType		; S2 record? (24-bit load address)
	bne	ChkForS8
	bra	Addr24		; go receive the S9 record.
;
ChkForS8:	cmpb	#S8RecType		; no. s8 record? (24-bit transfer address)
	bne	LookForSOR		; no. go look for next Start of Record.
Addr24:	ldaa	#4
	staa	DataBytes,pcr
SaveRecType:	stab	RecType,pcr		; yes. save the record type.
;
RcvSRec:	jsr	GetHexByte,pcr		; get the S-Record length byte.
	beq	HexOK		; return if there was an error.
BadHex:	ldab	#BadHexError
	bra	SaveError

HexOK:	stab	SRecBytes,sp		; save the total number of S-Record bytes we are to receive.
	stab	CheckSum,sp		; initialize the checksum calculation with the data byte count
	subb	DataBytes,pcr		; subtract the load address & checksum field from the data field count.
	stab	DataBytes,pcr		; save the code/data field size.
	cmpb	#64		; is the code/data field <= 64?
	bls	RcvData		; yes. it can be received.
	ldaa	#SRecLenErr		; no. the code/data field  is limited to 64 bytes.
	bra	SaveError		; return with the error code in a.

RcvData:	jsr	GetHexByte,pcr		; get an S-Record data byte.
	bne	BadHex		; return if there was an error.
	stab	1,x+		; save the byte in the data buffer.
	addb	CheckSum,sp		; add the byte into the checksum.
	stab	CheckSum,sp		; save the result.
	dec	SRecBytes,sp		; received all the S-Record bytes?
	bne	RcvData		; no. go get some more.
	inc	CheckSum,sp		; if checksum was ok, the result will be zero.
	beq	SRecRtn
	ldab	#CheckSumError
SaveError:	stab	ErrorFlag,pcr
SRecRtn:	leas	2,sp
	rts
;
;*************************************************************************************************************
;
IsHex:	equ	*
	cmpb	#'0'		; less than ascii hex zero?
	blo	NotHex		; yes. character is not hex. return a non-zero ccr indication.
	cmpb	#'9'		; less than or equal to ascii hex nine?
	bls	IsHex1		; yes. character is hex. return a zero ccr indication.
	cmpb	#'A'		; less than ascii hex 'A'?
	blo	NotHex		; yes. character is not hex. return a non-zero ccr indication.
	cmpb	#'F'		; less than or equal to ascii hex 'F'?
	bhi	NotHex		; yes. character is hex. return a non-zero ccr indication.
IsHex1:	orcc	#$04		; no. return a zero ccr indication.
NotHex:	rts
;
;*************************************************************************************************************
;
GetHexByte:	equ	*
	jsr	getchar,pcr		; get the upper nybble from the SCI.
	bsr	IsHex		; valid hex character?
	beq	OK1		; yes. go convert it to binary.
	rts			; no. return with a non-zero ccr indication.
OK1:	bsr	CvtHex		; convert the ascii-hex character to binary.
	ldaa	#16		; shift it to the upper 4-bits.
	mul
	pshb			; save it on the stack.
	jsr	getchar,pcr		; get the lower nybble from the SCI.
	bsr	IsHex		; valid hex character?
	beq	OK2		; yes. go convert it to binary.
	pulb			; remove saved upper byte from the stack.
	rts			; no. return with a non-zero ccr indication.
OK2:	bsr	CvtHex		; convert the ascii-hex character to binary.
	addb	1,sp+		; add it to the upper nybble.
	clra			; simple way to set the Z ccr bit.
	rts			; return.
;
;*************************************************************************************************************
;
CvtHex:	subb	#'0'		; subtract ascii '0' from the hex character.
	cmpb	#$09		; was it a decimal digit?
	bls	CvtHexRtn		; yes. ok as is.
	subb	#$07		; no. it was an ascii hex letter ('A' - 'F').
CvtHexRtn:	rts
;
;*************************************************************************************************************
;
OutStr:	equ	*		; send a null terminated string to the display.
	ldab	1,x+		; get a character, advance pointer, null?
	beq	OutStrDone		; yes. return.
	jsr	putchar,pcr		; no. send it out the SCI.
	bra	OutStr		; go get the next character.
OutStrDone:	rts
;
;
;*************************************************************************************************************
;
RxBufSize:	equ	64
TxBufSize:	equ	32
;
XOnCount:	equ	RxBufSize-8		; number of bytes avail. in the Rx queue before an XOn can be sent.
XOffCount:	equ	(RxBufSize-16)+2	; number of bytes remaining in the Rx queue when an XOff is sent.
;
XOn:	equ	$11		; ASCII DC1
XOff:	equ	$13		; ASCII DC3
;
RxBuff:	dcb	RxBufSize,0		; receive queue size.
TxBuff:	dcb	TxBufSize,0		; transmit queue size.
RxIn:	dc.b	0		; next available location in the Rx queue.
RxOut:	dc.b	0		; next character to be removed from the Rx queue.
TxIn:	dc.b	0		; next available location in the Tx queue
TxOut:	dc.b	0		; next character to be sent from the Tx queue.
RxBAvail:	dc.b	0		; number of bytes left in the Rx queue.
TxBAvail:	dc.b	0		; number of bytes left in the Tx queue.
XOffSent:	dc.b	0		; if != 0, an XOff has been sent.
SendXOff:	dc.b	0		; flag to TXISR to send an XOff to the host.
;
;
;*************************************************************************************************************
;
SCIInit:	std	SCI0BD		; initialize the baud rate register.
	ldab	#RxBufSize		; init number of available Rx
	stab	RxBAvail,pcr		; queue bytes to RxBuffSize.
	ldab	#TxBufSize		; init number of available Tx
	stab	TxBAvail,pcr		; queue bytes to TxBuffSize.
	ldab	#TE+RE+RIE		; get bit mask for Tx, Rx & Rx interrupt.
	stab	SCI0CR2		; enable Tx & Rx & Rx interrupts.
	leax	SCIISR,pcr		; setup SCI interrupt vector to point to the
	stx	SCI0		; bootloader's SCI interrupt service routine.
	rts			; done.
;
;*************************************************************************************************************
;
SCIISR:	brclr	SCI0CR2,#RIE,ChkRxInts	; Rx interrupts enabled?
	brset	SCI0SR1,#RDRF,RxIRQ	; yes. if RDRF flag set, service Rx interrupt.
	brset	SCI0SR1,#OR,RxIRQ	; yes. if OR flag set, service Rx interrupt.
ChkRxInts:	brclr	SCI0CR2,#TIE,NoSCIInt	; Tx interrupts enabled?
	brset	SCI0SR1,#TDRE,TxIRQ	; Yes. if TDRE is set, service Tx interrupt
NoSCIInt:	rti			; return w/o any action.
;
;*************************************************************************************************************
;
RxIRQ:	tst	XOffSent,pcr		; was an XOff previously sent to the host?
	bne	AlreadySent		; yes. go place the received character in the Rx queue.
	ldaa	RxBAvail,pcr		; no. get the number of bytes available in the Rx queue.
	cmpa	#XOffCount		; more than enough space to receive a FIFO full of data from the host?
	bhi	AlreadySent		; yes. go place the received byte in the Rx queue.
	inc	XOffSent,pcr		; set the 'XOff Sent' flag
	inc	SendXOff,pcr		; set flag indicating to TxISR to send an XOff to the host.
	bset	SCI0CR2,#TIE		; enable Tx interrupts.
;
AlreadySent:	tst	RxBAvail,pcr		; any room left in the Rx queue?
	beq	Buffull		; no. just throw the character away.
	dec	RxBAvail,pcr		; yes. there'll be one less now.
	leax	RxBuff,pcr		; point to the physical start of the Rx queue.
	ldaa	RxIn,pcr		; get the index for the next available queue location.
	ldab	SCI0DRL		; get the received character.
	stab	a,x		; place it in the queue.
	inca			; next available queue location.
	cmpa	#RxBufSize		; wrap around to start of queue?
	blo	NoRxWrap		; no. just update the index.
	clra			; yes. start at begining of  queue.
NoRxWrap:	staa	RxIn,pcr		; update the next available queue location index.
	rti			; return from the SCI Rx interrupt.
;
Buffull:	ldab	SCI0DRL		; the queue was full. get character & throw it away.
	rti			; return.
;
;*************************************************************************************************************
;
TxIRQ:	tst	SendXOff,pcr		; send an XOff to the host?
	beq	NoXOff		; no. just xmit the next
	ldab	#XOff		; get the XOff character
	stab	SCI0DRL		; send it.
	clr	SendXOff,pcr		; clear the XOff send flag.
	bra	ChckTxBuff		; go check to see if we need to disable Tx interrupts
;
NoXOff:	leax	TxBuff,pcr		; point to the physical start of the Tx queue.
	ldaa	TxOut,pcr		; get the index for the next character to send.
	ldab	a,x		; get the data.
	stab	SCI0DRL		; send it.
	inca			; advance to next character to send.
	cmpa	#TxBufSize		; reached the end of the queue?
	blo	NoTxWrap		; no.
	clra			; yes. wrap to the start.
NoTxWrap:	staa	TxOut,pcr		; update the queue index.
	inc	TxBAvail,pcr		; one more byte available in the queue.
ChckTxBuff:	ldab	TxBAvail,pcr		; get the count of avail bytes in the Tx queue.
	cmpb	#TxBufSize		; any more characters to send?
	bne	TxRTI		; yes. more characters to send, don't disable Tx interrupts.
	bclr	SCI0CR2,#TIE		; yes. queue is empty turn off TDRE interrupts.
TxRTI:	rti			; return.
;
;*************************************************************************************************************
;
SCIGetBuf:	ldab	#RxBufSize		; are there any characters in the Rx queue?
	subb	RxBAvail,pcr
	rts			; return number available.
;
;*************************************************************************************************************
;
getchar:	pshx			; save the registers we'll use.
	psha
RxChk:	ldab	#RxBufSize		; any characters available?
	subb	RxBAvail,pcr
	beq	RxChk		; no. just wait until some are.
	leax	RxBuff,pcr		; point to the physical start of the Rx queue.
	ldaa	RxOut,pcr		; get the index to the next available character in the Rx queue.
	ldab	a,x		; get the character.
	inca			; point to the next location in the queue.
	cmpa	#RxBufSize		; reached the end of the queue?
	blo	NogcWrap		; no.
	clra			; yes. wrap to the start.
NogcWrap:	staa	RxOut,pcr		; update the queue index.
	inc	RxBAvail,pcr		; we removed a character from the queue, there's 1 more available.
	tst	XOffSent,pcr		; was an XOff character previously sent by the RX ISR?
	beq	gcReturn		; no. just return.
	ldaa	RxBAvail,pcr		; yes. get the number of bytes available in the Rx queue.
	cmpa	#XOnCount		; enough space available to receive more?
	bhs	gcReturn		; no. just return.
	pshb			; yes. save the character we retrieved from the Rx queue.
	ldab	#XOn		; send an XOn character to the host.
	bsr	putchar
	clr	XOffSent,pcr		; clear the XOff flag.
	pulb			; restore the character we retrieved from the Rx queue.
gcReturn:	pula			; restore what we saved.
	pulx
	rts			; return.
;
;*************************************************************************************************************
;
putchar:	pshx			; save the registers we'll use.
	psha
TxChk:	tst	TxBAvail,pcr		; Any room left in the Tx queue?
	beq	TxChk		; no. just wait here till it is.
	leax	TxBuff,pcr		; point to the physical start of the Tx queue.
	ldaa	TxIn,pcr		; get the index to the next available spot.
	stab	a,x		; put the character in.
	inca			; point to the next available spot.
	cmpa	#TxBufSize		; go past the end of the queue?
	blo	NopcWrap		; no.
	clra			; yes. wrap around to the start.
NopcWrap:	staa	TxIn,pcr		; update the queue index
	dec	TxBAvail,pcr		; one less byte available in the Tx queue.
	bset	SCI0CR2,#TIE		; enable transmitter interrupts.
	pula			; restore what we saved.
	pulx
	rts			; return
;
;
;
BootLoadEnd:	equ	*
;
;
;
;	Global Variable declarations
;
;
ErrorFlag:	ds.b	1		; error code stored by various routines.
RecType:	ds.b	1		; received record type. '0' = S0; '1' = S1; '2' = S2; '8' = S8; '9' = S9
DataBytes:	ds.b	1		; number of data bytes in the S-Record.
PPAGEWAddr:	ds.b	2
LoadAddr:	ds.b	3		; load address of the S-Record.
SRecData:	ds.b	65		; S-Record data storage. (handle 64-byte S-Records + received checksum)
;
;
;*************************************************************************************************************
;
;       This is the jump table that is used to access the secondary interrupt vector table. Each one
;       of the actual interrupt vectors, begining at $ff8c, points to an entry in this table. Each jmp
;       instruction uses indexed indirect program counter relative (pcr) addressing to access the
;       secondary interrupt vector table that is located just below the bootblock.
;
;*************************************************************************************************************
;
JPWMEShutdown:	jmp	[PWMEShutdown-BootBlkSize,pcr]
JPortPInt:	jmp	[PortPInt-BootBlkSize,pcr]
JMSCAN4Tx:	jmp	[MSCAN4Tx-BootBlkSize,pcr]
JMSCAN4Rx:	jmp	[MSCAN4Rx-BootBlkSize,pcr]
JMSCAN4Errs:	jmp	[MSCAN4Errs-BootBlkSize,pcr]
JMSCAN4WakeUp:	jmp	[MSCAN4WakeUp-BootBlkSize,pcr]
JMSCAN3Tx:	jmp	[MSCAN3Tx-BootBlkSize,pcr]
JMSCAN3Rx:	jmp	[MSCAN3Rx-BootBlkSize,pcr]
JMSCAN3Errs:	jmp	[MSCAN3Errs-BootBlkSize,pcr]
JMSCAN3WakeUp:	jmp	[MSCAN3WakeUp-BootBlkSize,pcr]
JMSCAN2Tx:	jmp	[MSCAN2Tx-BootBlkSize,pcr]
JMSCAN2Rx:	jmp	[MSCAN2Rx-BootBlkSize,pcr]
JMSCAN2Errs:	jmp	[MSCAN2Errs-BootBlkSize,pcr]
JMSCAN2WakeUp:	jmp	[MSCAN2WakeUp-BootBlkSize,pcr]
JMSCAN1Tx:	jmp	[MSCAN1Tx-BootBlkSize,pcr]
JMSCAN1Rx:	jmp	[MSCAN1Rx-BootBlkSize,pcr]
JMSCAN1Errs:	jmp	[MSCAN1Errs-BootBlkSize,pcr]
JMSCAN1WakeUp:	jmp	[MSCAN1WakeUp-BootBlkSize,pcr]
JMSCAN0Tx:	jmp	[MSCAN0Tx-BootBlkSize,pcr]
JMSCAN0Rx:	jmp	[MSCAN0Rx-BootBlkSize,pcr]
JMSCAN0Errs:	jmp	[MSCAN0Errs-BootBlkSize,pcr]
JMSCAN0WakeUp:	jmp	[MSCAN0WakeUp-BootBlkSize,pcr]
JFlash:	jmp	[Flash-BootBlkSize,pcr]
JEEPROM:	jmp	[EEPROM-BootBlkSize,pcr]
JSPI2:	jmp	[SPI2-BootBlkSize,pcr]
JSPI1:	jmp	[SPI1-BootBlkSize,pcr]
JIICBus:	jmp	[IICBus-BootBlkSize,pcr]
JDLC:	jmp	[DLC-BootBlkSize,pcr]
JSCME:	jmp	[SCMEVect-BootBlkSize,pcr]
JCRGLock:	jmp	[CRGLock-BootBlkSize,pcr]
JPACCBOv:	jmp	[PACCBOv-BootBlkSize,pcr]
JModDnCtr:	jmp	[ModDnCtr-BootBlkSize,pcr]
JPortHInt:	jmp	[PortHInt-BootBlkSize,pcr]
JPortJInt:	jmp	[PortJInt-BootBlkSize,pcr]
JATD1:	jmp	[ATD1-BootBlkSize,pcr]
JATD0:	jmp	[ATD0-BootBlkSize,pcr]
JSCI1:	jmp	[SCI1-BootBlkSize,pcr]
JSCI0:	jmp	[SCI0-BootBlkSize,pcr]
JSPI0:	jmp	[SPI0-BootBlkSize,pcr]
JPACCAEdge:	jmp	[PACCAEdge-BootBlkSize,pcr]
JPACCAOv:	jmp	[PACCAOv-BootBlkSize,pcr]
JTimerOv:	jmp	[TimerOv-BootBlkSize,pcr]
JTimerCh7:	jmp	[TimerCh7-BootBlkSize,pcr]
JTimerCh6:	jmp	[TimerCh6-BootBlkSize,pcr]
JTimerCh5:	jmp	[TimerCh5-BootBlkSize,pcr]
JTimerCh4:	jmp	[TimerCh4-BootBlkSize,pcr]
JTimerCh3:	jmp	[TimerCh3-BootBlkSize,pcr]
JTimerCh2:	jmp	[TimerCh2-BootBlkSize,pcr]
JTimerCh1:	jmp	[TimerCh1-BootBlkSize,pcr]
JTimerCh0:	jmp	[TimerCh0-BootBlkSize,pcr]
JRTI:	jmp	[RTI-BootBlkSize,pcr]
JIRQ:	jmp	[IRQ-BootBlkSize,pcr]
JXIRQ	jmp	[XIRQ-BootBlkSize,pcr]
JSWI:	jmp	[SWI-BootBlkSize,pcr]
JIllop:	jmp	[Illop-BootBlkSize,pcr]
JCOPFail:	jmp	[COPFail-BootBlkSize,pcr]
JClockFail:	jmp	[ClockFail-BootBlkSize,pcr]
;
	org	$ff0d
;
	dc.b	$cf		; setup a 4K booblock in Flash block 0.
;
	org	$ff0f
;
	dc.b	$fe
;
	org	$ff8c
;
PWMEShutdown:	dc.w	JPWMEShutdown
PortPInt:	dc.w	JPortPInt
MSCAN4Tx:	dc.w	JMSCAN4Tx
MSCAN4Rx:	dc.w	JMSCAN4Rx
MSCAN4Errs:	dc.w	JMSCAN4Errs
MSCAN4WakeUp:	dc.w	JMSCAN4WakeUp
MSCAN3Tx:	dc.w	JMSCAN3Tx
MSCAN3Rx:	dc.w	JMSCAN3Rx
MSCAN3Errs:	dc.w	JMSCAN3Errs
MSCAN3WakeUp:	dc.w	JMSCAN3WakeUp
MSCAN2Tx:	dc.w	JMSCAN2Tx
MSCAN2Rx:	dc.w	JMSCAN2Rx
MSCAN2Errs:	dc.w	JMSCAN2Errs
MSCAN2WakeUp:	dc.w	JMSCAN2WakeUp
MSCAN1Tx:	dc.w	JMSCAN1Tx
MSCAN1Rx:	dc.w	JMSCAN1Rx
MSCAN1Errs:	dc.w	JMSCAN1Errs
MSCAN1WakeUp:	dc.w	JMSCAN1WakeUp
MSCAN0Tx:	dc.w	JMSCAN0Tx
MSCAN0Rx:	dc.w	JMSCAN0Rx
MSCAN0Errs:	dc.w	JMSCAN0Errs
MSCAN0WakeUp:	dc.w	JMSCAN0WakeUp
Flash:	dc.w	JFlash
EEPROM:	dc.w	JEEPROM
SPI2:	dc.w	JSPI2
SPI1:	dc.w	JSPI1
IICBus:	dc.w	JIICBus
DLC:	dc.w	JDLC
SCMEVect:	dc.w	JSCME
CRGLock:	dc.w	JCRGLock
PACCBOv:	dc.w	JPACCBOv
ModDnCtr:	dc.w	JModDnCtr
PortHInt:	dc.w	JPortHInt
PortJInt:	dc.w	JPortJInt
ATD1:	dc.w	JATD1
ATD0:	dc.w	JATD0
SCI1:	dc.w	JSCI1
SCI0:	dc.w	JSCI0
SPI0:	dc.w	JSPI0
PACCAEdge:	dc.w	JPACCAEdge
PACCAOv:	dc.w	JPACCAOv
TimerOv:	dc.w	JTimerOv
TimerCh7:	dc.w	JTimerCh7
TimerCh6:	dc.w	JTimerCh6
TimerCh5:	dc.w	JTimerCh5
TimerCh4:	dc.w	JTimerCh4
TimerCh3:	dc.w	JTimerCh3
TimerCh2:	dc.w	JTimerCh2
TimerCh1:	dc.w	JTimerCh1
TimerCh0:	dc.w	JTimerCh0
RTI:	dc.w	JRTI
IRQ:	dc.w	JIRQ
XIRQ:	dc.w	JXIRQ
SWI:	dc.w	JSWI
Illop:	dc.w	JIllop
COPFail:	dc.w	JCOPFail
ClockFail:	dc.w	JClockFail
Reset:	dc.w	BootStart
