;=============================================================================
; Bootstrap Loader for Hexadecimal Files
; written by G. Goodhue, Signetics Co.
; This program downloading a hexadecimal program file over an asynchronous
; serial link to a code RAM in an 80C51 system. The downloaded code may then
; be executed as the main program for the system. This technique may be used
; in a system that normally connects to a host PC so that the code may come
; from a disk and thus be easily updated. The system RAM must be wired to the
; 80C51 system so that it appears as both data and program memory (wire the
; RAM normally, but use the logical AND of RD and PSEN for the output enable.)
; To use the bootstrap program, an Intel Hex file is sent through the serial
; port in 8-N-1 format at 9600 baud. The baud rate and format may be altered
; by making small changes in the serial port setup routine (SerStart).
; Note that there is no hardware handshaking (e.g. RTS/CTS or XON/XOFF)
; implemented between the host and the bootstrap system. This was done to keep
; the protocol between the two systems as simple as possible.
; Since the bootstrap program does not echo the data file, there is no chance
; of an overrun unless the 80C51 is running very slowly and/or the
; communication is very fast. An 80C51 running at 11.0592 MHz (the most
; commonly used frequency in systems with serial communication) will be able
; to easily keep up with 38.4K baud communication without handshaking.
;=============================================================================
; The download protocol for this program is as follows:
; - When the bootstrap program starts up, it sends a prompt character ("=")
; up the serial link to the host.
; - The host may then send the hexadecimal program file down the serial link.
; At any time, the host may send an escape character (1B hex) to abort and
; restart the download process from scratch, beginning from the "=" prompt.
; This procedure may be used to restart if a download error occurs.
; - At the end of a hex file download, a colon (":") prompt is returned. If
; an error or other suspicious circumstance occurred, a flag value will
; also be returned as shown below. The flag is a bit map of possible
; conditions and so may represent more than one problem. If an error
; occurs, the bootstrap program will refuse to execute the downloaded
; program.
; Exception codes:
; 01 - non-hexadecimal characters found embedded in a data line.
; 02 - bad record type found.
; 04 - incorrect line checksum found.
; 08 - no data found.
; 10 - incremented address overflowed back to zero.
; 20 - RAM data write did not verify correctly.
; - If a download error occurs, the download may be retried by first sending
; an escape character. Until the escape is received, the bootstrap program
; will refuse to accept any data and will echo a question mark ("?") for
; any character sent.
; - After a valid file download, the bootstrap program will send a message
; containing the file checksum. This is the arithmetic sum of all of the
; DATA bytes (not addresses, record types, etc.) in the file, truncated to
; 16 bits. This checksum appears in parentheses: "(abcd)". Program
; execution may then be started by telling the bootstrap program the
; correct starting address. The format for this is to send a slash ("/")
; followed by the address in ASCII hexadecimal, followed by a carriage
; return. Example: "/8A31<CR>"
; - If the address is accepted, an at sign ("@") is returned before executing
; the jump to the downloaded file.
; The bootstrap loader can be configured to re-map interrupt vectors to the
; downloaded program if jumps to the correct addresses are set up. For
; instance, if the program RAM in the system where this program is to be used
; starts at 8000 hexadecimal, the re-mapped interrupts may begin at 8003 for
; external interrupt 0, etc.
;=============================================================================
$Title(Bootstrap Loader for Hexadecimal Files)
$Date(04-13-92)
$MOD51
;=============================================================================
; Definitions
;=============================================================================
LF EQU 0Ah ; Line Feed character.
CR EQU 0Dh ; Carriage Return character.
ESC EQU 1Bh ; Escape character.
StartChar EQU ':' ; Line start character for hex file.
Slash EQU '/' ; Go command character.
Skip EQU 13 ; Value for "Skip" state.
Ch DATA 0Fh ; Last character received.
State DATA 10h ; Identifies the state in process.
DataByte DATA 11h ; Last data byte received.
ByteCount DATA 12h ; Data byte count from current line.
HighAddr DATA 13h ; High and low address bytes from the
LowAddr DATA 14h ; current data line.
RecType DATA 15h ; Line record type for this line.
ChkSum DATA 16h ; Calculated checksum received.
HASave DATA 17h ; Saves the high and low address bytes
LASave DATA 18h ; from the last data line.
FilChkHi DATA 19h ; File checksum high byte.
FilChkLo DATA 1Ah ; File checksum low byte.
Flags DATA 20h ; State condition flags.
HexFlag BIT Flags.0 ; Hex character found.
EndFlag BIT Flags.1 ; End record found.
DoneFlag BIT Flags.2 ; Processing done (end record or some
; kind of error.
EFlags DATA 21h ; Exception flags.
ErrFlag1 BIT EFlags.0 ; Non-hex character embedded in data.
ErrFlag2 BIT EFlags.1 ; Bad record type.
ErrFlag3 BIT EFlags.2 ; Bad line checksum.
ErrFlag4 BIT EFlags.3 ; No data found.
ErrFlag5 BIT EFlags.4 ; Incremented address overflow.
ErrFlag6 BIT EFlags.5 ; Data storage verify error.
DatSkipFlag BIT Flags.3 ; Any data found should be ignored.
;=============================================================================
; Reset and Interrupt Vectors
;=============================================================================
; The following are dummy labels for re-mapped interrupt vectors. The
; addresses should be changed to match the memory map of the target system.
ExInt0 EQU 8003h ; Remap address for ext interrupt 0.
T0Int EQU 800Bh ; Timer 0 interrupt.
ExInt1 EQU 8013h ; External interrupt 1.
T1Int EQU 801Bh ; Timer 1 interrupt.
SerInt EQU 8023h ; Serial port interrupt.
ORG 0000h
LJMP Start ; Go to the downloader program.
; The following are intended to allow re-mapping the interrupt vectors to the
; users downloaded program. The jump addresses should be adjusted to reflect
; the memory mapping used in the actual application.
; Other (or different) interrupt vectors may need to be added if the target
; processor is not an 80C51.
ORG 0003h
; LJMP ExInt0 ; External interrupt 0.
RETI
ORG 000Bh
; LJMP T0Int ; Timer 0 interrupt.
RETI
ORG 0013h
; LJMP ExInt1 ; External interrupt 1.
RETI
ORG 001Bh
; LJMP T1Int ; Timer 1 interrupt.
RETI
ORG 0023h
; LJMP SerInt ; Serial port interrupt.
RETI
;=============================================================================
; Reset and Interrupt Vectors
;=============================================================================
Start: MOV IE,#0 ; Turn off all interrupts.
MOV SP,#5Fh ; Start stack near top of '51 RAM.
ACALL SerStart ; Setup and start serial port.
ACALL CRLF ; Send a prompt that we are here.
MOV A,#'=' ; "<CRLF> ="
ACALL PutChar
ACALL HexIn ; Try to read hex file from serial port.
ACALL ErrPrt ; Send a message for any errors or
; warnings that were noted.
MOV A,EFlags ; We want to get stuck if a fatal
JZ HexOK ; error occurred.
ErrLoop: MOV A,#'?' ; Send a prompt to confirm that we
ACALL PutChar ; are 'stuck'. " ? "
ACALL GetChar ; Wait for escape char to flag reload.
SJMP ErrLoop
HexOK: MOV EFlags,#0 ; Clear errors flag in case we re-try.
ACALL GetChar ; Look for GO command.
CJNE A,#Slash,HexOK ; Ignore other characters received.
ACALL GetByte ; Get the GO high address byte.
JB ErrFlag1,HexOK ; If non-hex char found, try again.
MOV HighAddr,DataByte ; Save upper GO address byte.
ACALL GetByte ; Get the GO low address byte.
JB ErrFlag1,HexOK ; If non-hex char found, try again.
MOV LowAddr,DataByte ; Save the lower GO address byte.
ACALL GetChar ; Look for CR.
CJNE A,#CR,HexOK ; Re-try if CR not there.
; All conditions are met, so hope the data file and the GO address are all
; correct, because now we're committed.
MOV A,#'@' ; Send confirmation to GO. " @ "
ACALL PutChar
JNB TI,$ ; Wait for completion before GOing.
PUSH LowAddr ; Put the GO address on the stack,
PUSH HighAddr ; so we can Return to it.
RET ; Finally, go execute the user program!
;=============================================================================
; Hexadecimal File Input Routine
;=============================================================================
HexIn: CLR A ; Clear out some variables.
MOV State,A
MOV Flags,A
MOV HighAddr,A
MOV LowAddr,A
MOV HASave,A
MOV LASave,A
MOV ChkSum,A
MOV FilChkHi,A
MOV FilChkLo,A
MOV EFlags,A
SETB ErrFlag4 ; Start with a 'no data' condition.
StateLoop: ACALL GetChar ; Get a character for processing.
ACALL AscHex ; Convert ASCII-hex character to hex.
MOV Ch,A ; Save result for later.
ACALL GoState ; Go find the next state based on
; this char.
JNB DoneFlag,StateLoop ; Repeat until done or terminated.
ACALL PutChar ; Send the file checksum back as
MOV A,#'(' ; confirmation. " (abcd) "
ACALL PutChar
MOV A,FilChkHi
ACALL PrByte
MOV A,FilChkLo
ACALL PrByte
MOV A,#')'
ACALL PutChar
ACALL CRLF
RET ; Exit to main program.
; Find and execute the state routine pointed to by "State".
GoState: MOV A,State ; Get current state.
ANL A,#0Fh ; Insure branch is within table range.
RL A ; Adjust offset for 2 byte insts.
MOV DPTR,#StateTable
JMP @A+DPTR ; Go to appropriate state.
StateTable: AJMP StWait ; 0 - Wait for start.
AJMP StLeft ; 1 - First nibble of count.
AJMP StGetCnt ; 2 - Get count.
AJMP StLeft ; 3 - First nibble of address byte 1.
AJMP StGetAd1 ; 4 - Get address byte 1.
AJMP StLeft ; 5 - First nibble of address byte 2.
AJMP StGetAd2 ; 6 - Get address byte 2.
AJMP StLeft ; 7 - First nibble of record type.
AJMP StGetRec ; 8 - Get record type.
AJMP StLeft ; 9 - First nibble of data byte.
AJMP StGetDat ; 10 - Get data byte.
AJMP StLeft ; 11 - First nibble of checksum.
AJMP StGetChk ; 12 - Get checksum.
AJMP StSkip ; 13 - Skip data after error condition.
AJMP BadState ; 14 - Should never get here.
AJMP BadState ; 15 - " " " "
; This state is used to wait for a line start character. Any other characters
; received prior to the line start are simply ignored.
StWait: MOV A,Ch ; Retrieve input character.
CJNE A,#StartChar,SWEX ; Check for line start.
INC State ; Received line start.
SWEX: RET
; Process the first nibble of any hex byte.
StLeft: MOV A,Ch ; Retrieve input character.
JNB HexFlag,SLERR ; Check for hex character.
ANL A,#0Fh ; Isolate one nibble.
SWAP A ; Move nibble too upper location.
MOV DataByte,A ; Save left/upper nibble.
INC State ; Go to next state.
RET ; Return to state loop.
SLERR: SETB ErrFlag1 ; Error - non-hex character found.
SETB DoneFlag ; File considered corrupt. Tell main.
RET
; Process the second nibble of any hex byte.
StRight: MOV A,Ch ; Retrieve input character.
JNB HexFlag,SRERR ; Check for hex character.
ANL A,#0Fh ; Isolate one nibble.
ORL A,DataByte ; Complete one byte.
MOV DataByte,A ; Save data byte.
ADD A,ChkSum ; Update line checksum,
MOV ChkSum,A ; and save.
RET ; Return to state loop.
SRERR: SETB ErrFlag1 ; Error - non-hex character found.
SETB DoneFlag ; File considered corrupt. Tell main.
RET
; Get data byte count for line.
StGetCnt: ACALL StRight ; Complete the data count byte.
MOV A,DataByte
MOV ByteCount,A
INC State ; Go to next state.
RET ; Return to state loop.
; Get upper address byte for line.
StGetAd1: ACALL StRight ; Complete the upper address byte.
MOV A,DataByte
MOV HighAddr,A ; Save new high address.
INC State ; Go to next state.
RET ; Return to state loop.
; Get lower address byte for line.
StGetAd2: ACALL StRight ; Complete the lower address byte.
MOV A,DataByte
MOV LowAddr,A ; Save new low address.
INC State ; Go to next state.
RET ; Return to state loop.
; Get record type for line.
StGetRec: ACALL StRight ; Complete the record type byte.
MOV A,DataByte
MOV RecType,A ; Get record type.
JZ SGRDat ; This is a data record.
CJNE A,#1,SGRErr ; Check for end record.
SETB EndFlag ; This is an end record.
SETB DatSkipFlag ; Ignore data embedded in end record.
MOV State,#11 ; Go to checksum for end record.
SJMP SGREX
SGRDat: INC State ; Go to next state.
SGREX: RET ; Return to state loop.
SGRErr: SETB ErrFlag2 ; Error, bad record type.
SETB DoneFlag ; File considered corrupt. Tell main.
RET
; Get a data byte.
StGetDat: ACALL StRight ; Complete the data byte.
JB DatSkipFlag,SGD1 ; Don't process the data if the skip
; flag is on.
ACALL Store ; Store data byte in memory.
MOV A,DataByte ; Update the file checksum,
ADD A,FilChkLo ; which is a two-byte summation of
MOV FilChkLo,A ; all data bytes.
CLR A
ADDC A,FilChkHi
MOV FilChkHi,A
MOV A,DataByte
SGD1: DJNZ ByteCount,SGDEX ; Last data byte?
INC State ; Done with data, go to next state.
SJMP SGDEX2
SGDEX: DEC State ; Set up state for next data byte.
SGDEX2: RET ; Return to state loop.
; Get checksum.
StGetChk: ACALL StRight ; Complete the checksum byte.
JNB EndFlag,SGC1 ; Check for an end record.
SETB DoneFlag ; If this was an end record,
SJMP SGCEX ; we are done.
SGC1: MOV A,ChkSum ; Get calculated checksum.
JNZ SGCErr ; Result should be zero.
MOV ChkSum,#0 ; Preset checksum for next line.
MOV State,#0 ; Line done, go back to wait state.
MOV LASave,LowAddr ; Save address byte from this line for
MOV HASave,HighAddr ; later check.
SGCEX: RET ; Return to state loop.
SGCErr: SETB ErrFlag3 ; Line checksum error.
SETB DoneFlag ; File considered corrupt. Tell main.
RET
; This state used to skip through any additional data sent, ignoring it.
StSkip: RET ; Return to state loop.
; A place to go if an illegal state comes up somehow.
BadState: MOV State,#Skip ; If we get here, something very bad
RET ; happened, so return to state loop.
; Store - Save data byte in external RAM at specified address.
Store: MOV DPH,HighAddr ; Set up external RAM address in DPTR.
MOV DPL,LowAddr
MOV A,DataByte
MOVX @DPTR,A ; Store the data.
MOVX A,@DPTR ; Read back data for integrity check.
CJNE A,DataByte,StoreErr ; Is read back OK?
CLR ErrFlag4 ; Show that we've found some data.
INC DPTR ; Advance to the next addr in sequence.
MOV HighAddr,DPH ; Save the new address
MOV LowAddr,DPL
CLR A
CJNE A,HighAddr,StoreEx ; Check for address overflow
CJNE A,LowAddr,StoreEx ; (both bytes are 0).
SETB ErrFlag5 ; Set warning for address overflow.
StoreEx: RET
StoreErr: SETB ErrFlag6 ; Data storage verify error.
SETB DoneFlag ; File considered corrupt. Tell main.
RET
;=============================================================================
; Subroutines
;=============================================================================
; Subroutine summary:
; SerStart - Serial port setup and start.
; GetChar - Get a character from the serial port for processing.
; GetByte - Get a hex byte from the serial port for processing.
; PutChar - Output a character to the serial port.
; AscHex - See if char in ACC is ASCII-hex and if so convert to hex nibble.
; HexAsc - Convert a hexadecimal nibble to its ASCII character equivalent.
; ErrPrt - Return any error codes to our host.
; CRLF - output a carriage return / line feed pair to the serial port.
; PrByte - Send a byte out the serial port in ASCII hexadecimal format.
; SerStart - Serial port setup and start.
SerStart: MOV A,PCON ; Make sure SMOD is off.
CLR ACC.7
MOV PCON,A
MOV TH1,#0FDh ; Set up timer 1.
MOV TL0,#0FDh
MOV TMOD,#20h
MOV TCON,#40h
MOV SCON,#52h ; Set up serial port.
RET
; GetByte - Get a hex byte from the serial port for processing.
GetByte: ACALL GetChar ; Get first character of byte.
ACALL AscHex ; Convert to hex.
MOV Ch,A ; Save result for later.
ACALL StLeft ; Process as top nibble of a hex byte.
ACALL GetChar ; Get second character of byte.
ACALL AscHex ; Convert to hex.
MOV Ch,A ; Save result for later.
ACALL StRight ; Process as bottom nibble of hex byte.
RET
; GetChar - Get a character from the serial port for processing.
GetChar: JNB RI,$ ; Wait for receiver flag.
CLR RI ; Clear receiver flag.
MOV A,SBUF ; Read character.
CJNE A,#ESC,GCEX ; Re-start immediately if Escape char.
LJMP Start
GCEX: RET
; PutChar - Output a character to the serial port.
PutChar: JNB TI,$ ; Wait for transmitter flag.
CLR TI ; Clear transmitter flag.
MOV SBUF,A ; Send character.
RET
; AscHex - See if char in ACC is ASCII-hex and if so convert to a hex nibble.
; Returns nibble in A, HexFlag tells if char was really hex. The ACC is not
; altered if the character is not ASCII hex. Upper and lower case letters
; are recognized.
AscHex: CJNE A,#'0',AH1 ; Test for ASCII numbers.
AH1: JC AHBad ; Is character is less than a '0'?
CJNE A,#'9'+1,AH2 ; Test value range.
AH2: JC AHVal09 ; Is character is between '0' and '9'?
CJNE A,#'A',AH3 ; Test for upper case hex letters.
AH3: JC AHBad ; Is character is less than an 'A'?
CJNE A,#'F'+1,AH4 ; Test value range.
AH4: JC AHValAF ; Is character is between 'A' and 'F'?
CJNE A,#'a',AH5 ; Test for lower case hex letters.
AH5: JC AHBad ; Is character is less than an 'a'?
CJNE A,#'f'+1,AH6 ; Test value range.
AH6: JNC AHBad ; Is character is between 'a' and 'f'?
CLR C
SUBB A,#27h ; Pre-adjust character to get a value.
SJMP AHVal09 ; Now treat as a number.
AHBad: CLR HexFlag ; Flag char as non-hex, don't alter.
SJMP AHEX ; Exit
AHValAF: CLR C
SUBB A,#7 ; Pre-adjust character to get a value.
AHVal09: CLR C
SUBB A,#'0' ; Adjust character to get a value.
SETB HexFlag ; Flag character as 'good' hex.
AHEX: RET
; HexAsc - Convert a hexadecimal nibble to its ASCII character equivalent.
HexAsc: ANL A,#0Fh ; Make sure we're working with only
; one nibble.
CJNE A,#0Ah,HA1 ; Test value range.
HA1: JC HAVal09 ; Value is 0 to 9.
ADD A,#7 ; Value is A to F, extra adjustment.
HAVal09: ADD A,#'0' ; Adjust value to ASCII hex.
RET
; ErrPrt - Return an error code to our host.
ErrPrt: MOV A,#':' ; First, send a prompt that we are
CALL PutChar ; still here.
MOV A,EFlags ; Next, print the error flag value if
JZ ErrPrtEx ; it is not 0.
CALL PrByte
ErrPrtEx: RET
; CRLF - output a carriage return / line feed pair to the serial port.
CRLF: MOV A,#CR
CALL PutChar
MOV A,#LF
CALL PutChar
RET
; PrByte - Send a byte out the serial port in ASCII hexadecimal format.
PrByte: PUSH ACC ; Print ACC contents as ASCII hex.
SWAP A
CALL HexAsc ; Print upper nibble.
CALL PutChar
POP ACC
CALL HexAsc ; Print lower nibble.
CALL PutChar
RET
;=============================================================================
END