411 lines
13 KiB
NASM
411 lines
13 KiB
NASM
; XMODEM/CRC Receiver for AVR
|
|
;
|
|
; A simple file transfer program to allow upload from a console device
|
|
; to the SBC utilizing the x-modem/CRC transfer protocol. Requires just
|
|
; under 1k of either RAM or ROM, 132 bytes of RAM for the receive buffer,
|
|
; and 8 bytes of zero page RAM for variable storage.
|
|
;
|
|
;**************************************************************************
|
|
; This implementation of XMODEM/CRC does NOT conform strictly to the
|
|
; XMODEM protocol standard in that it (1) does not accurately time character
|
|
; reception or (2) fall back to the Checksum mode.
|
|
|
|
; (1) For timing, it uses a crude timing loop to provide approximate
|
|
; delays. Windows HyperTerminal worked quite well!
|
|
;
|
|
; (2) Most modern terminal programs support XMODEM/CRC which can detect a
|
|
; wider range of transmission errors so the fallback to the simple checksum
|
|
; calculation was not implemented to save space.
|
|
;**************************************************************************
|
|
;
|
|
;-------------------------- The Code ----------------------------
|
|
;
|
|
; Register usage
|
|
; (these are repurposed from the Main program and will generate
|
|
; assembler warnings)
|
|
;
|
|
.def crc = R22 ;\ CRC lo byte (two byte variable)
|
|
.def crch = R23 ;/ CRC hi byte
|
|
.def blkno = R24 ; block number
|
|
.def retry = R6 ; retry counter
|
|
.def retry2 = R25 ; 2nd counter
|
|
.def bflag = R7 ; block flag
|
|
.def zsav = R8 ; Z save loc
|
|
.def zsavh = R9 ;
|
|
.def rzsav = R10 ; RAMPZ save
|
|
;
|
|
;
|
|
; non-zero page variables and buffers
|
|
;
|
|
;
|
|
.equ Rbuff = 0x0200 ; temp 132 byte receive buffer
|
|
;
|
|
; XMODEM Control Character Constants
|
|
.equ SOH = 0x01 ; start block
|
|
.equ EOT = 0x04 ; end of text marker
|
|
.equ ACK = 0x06 ; good block acknowledged
|
|
.equ NAK = 0x15 ; bad block acknowledged
|
|
.equ CAN = 0x18 ; cancel (not standard, not supported)
|
|
.equ CR = 0x0d ; carriage return
|
|
.equ LF = 0x0a ; line feed
|
|
.equ ESC = 0x1b ; ESC to exit
|
|
|
|
;
|
|
;^^^^^^^^^^^^^^^^^^^^^^ Start of Program ^^^^^^^^^^^^^^^^^^^^^^
|
|
;
|
|
; Xmodem/CRC upload routine
|
|
; By Daryl Rictor, Oct 30, 2012
|
|
;
|
|
; v 1.1 2nd release - 48K Rom size
|
|
;
|
|
XModem:
|
|
clr zsav
|
|
ldi i, 0x40
|
|
mov zsavh, i ; start of flash
|
|
XModem1:
|
|
mov rzsav, zero
|
|
out rampz, zero ; start on bank zero
|
|
ldi xl, 0x00
|
|
ldi xh, 0x10 ; Temp RAM page buffer
|
|
ldi yh, high(rbuff)
|
|
rcall PrintMsg ; send prompt and info
|
|
ldi blkno, 0x01 ; set block # to 1
|
|
mov bflag, blkno ; set flag to get address from block 1
|
|
StartCrc:
|
|
ldi i, 'C' ; "C" start with CRC mode
|
|
rcall Put_Chr ; send it
|
|
ldi retry2, 0xff ; set loop counter for ~3 sec delay
|
|
clr crc
|
|
clr crch ; init CRC value
|
|
rcall GetByte ; wait for input
|
|
brcs GotByte ; byte received, process it
|
|
brcc StartCrc ; resend "C"
|
|
|
|
StartBlk:
|
|
ldi retry2, 0xff ; set loop counter for ~3 sec delay
|
|
clr crc ;
|
|
clr crch ; init CRC value
|
|
rcall GetByte ; get first byte of block
|
|
brcc StartBlk ; timed out, keep waiting...
|
|
GotByte:
|
|
cpi i, ESC ; quitting?
|
|
brne GotByte1 ; no
|
|
jmp Reset ; YES - do BRK or change to RTS if desired
|
|
GotByte1:
|
|
cpi i, SOH ; start of block?
|
|
breq BegBlk ; yes
|
|
cpi i, EOT ;
|
|
brne BadCrc ; Not SOH or EOT, so flush buffer & send NAK
|
|
rjmp Done ; EOT - all done!
|
|
BegBlk:
|
|
clr yl ; byte counter
|
|
GetBlk:
|
|
ldi retry2, 0xff ; 3 sec window to receive characters
|
|
GetBlk1:
|
|
rcall GetByte ; get next character
|
|
brcc BadCrc ; chr rcv error, flush and send NAK
|
|
GetBlk2:
|
|
st Y+, i ; (buff) good char, save it in the rcv buffer
|
|
cpi yl, 0x84 ; <01> <FE> <128 bytes> <CRCH> <CRCL>
|
|
brne GetBlk ; get 132 characters
|
|
clr yl ;
|
|
ld i, Y+ ; (buff) get block # from buffer
|
|
cp i, blkno ; compare to expected block #
|
|
breq GoodBlk1 ; matched!
|
|
rcall Print_Err ; Unexpected block number - abort
|
|
rcall Flush ; mismatched - flush buffer and then do BRK
|
|
jmp Reset ; unexpected block # - fatal error - BRK or RTS
|
|
GoodBlk1:
|
|
ldi j, 0xff
|
|
eor i, j ; 1's comp of block #
|
|
ld j, Y
|
|
cp i, j ; compare with expected 1's comp of block #
|
|
breq GoodBlk2 ; matched!
|
|
write_err:
|
|
rcall Print_Err ; Unexpected block number - abort
|
|
rcall Flush ; mismatched - flush buffer and then do BRK
|
|
jmp Reset ; bad 1's comp of block#
|
|
GoodBlk2:
|
|
ldi yl, 0x02 ;
|
|
CalcCrc:
|
|
ld i, Y+ ; calculate the CRC for the 128 bytes of data
|
|
rcall UpdCrc ; could inline sub here for speed
|
|
cpi yl, 0x82 ; 128 bytes
|
|
brne CalcCrc ;
|
|
ld i, Y+ ; get hi CRC from buffer
|
|
cp i, crch ; compare to calculated hi CRC
|
|
brne BadCrc ; bad crc, send NAK
|
|
ld i, Y ; get lo CRC from buffer
|
|
cp i, crc ; compare to calculated lo CRC
|
|
breq GoodCrc ; good CRC
|
|
BadCrc:
|
|
rcall Flush ; flush the input port
|
|
ldi i, NAK ;
|
|
rcall Put_Chr ; send NAK to resend block
|
|
rjmp StartBlk ; start over, get the block again
|
|
GoodCrc:
|
|
ldi yl, 0x02 ;
|
|
Write1:
|
|
ld i, Y+ ; move buffer to RAM Holding Area
|
|
st X+, i
|
|
cpi yl, 0x82 ;
|
|
brne Write1
|
|
tst xl
|
|
brne IncBlk ; if not zero, get next block
|
|
; write 256 bytes to Flash
|
|
movw zl, zsav ; restore flash address
|
|
out rampz, rzsav ; and bank address
|
|
ldi xl, 0x00
|
|
ldi xh, 0x10 ; start of buffer
|
|
cpi blkno, 0x02 ; first page write, don't test zh
|
|
breq write11
|
|
cpi zh, 0x00 ; exceeded flash space?
|
|
brne write11
|
|
mov rzsav, one
|
|
out rampz, rzsav
|
|
Write11:
|
|
in j, sreg ; save sreg
|
|
cli ; disable irq's
|
|
fploop:
|
|
ld r0, X+
|
|
ld r1, X+
|
|
stbf1:
|
|
in i, spmcsr ; get status
|
|
andi i, 0x01 ; mask spmen
|
|
brne stbf1 ; wait for spm to complete
|
|
out spmcsr, one ; enable SPM only - write to int buffer
|
|
spm
|
|
adiw zl, 0x02 ; inc Z pointer (word pointer)
|
|
tst zl
|
|
brne fploop ; store all 256 bytes 128 words
|
|
dec zh ; back to current page
|
|
erase1:
|
|
in i, spmcsr ; get status
|
|
andi i, 0x01 ; mask spmen
|
|
brne erase1 ; wait for spm to complete
|
|
ldi i, 0x03 ; SPM En + PGERS (erase page)
|
|
out spmcsr, i
|
|
spm
|
|
write2:
|
|
in i, spmcsr ; get status
|
|
andi i, 0x01 ; mask spmen
|
|
brne write2 ; wait for spm to complete
|
|
ldi i, 0x05 ; SPM En + PGWRT (write page)
|
|
out spmcsr, i
|
|
spm
|
|
inc zh ; go to next page
|
|
write3:
|
|
in i, spmcsr ; get status
|
|
andi i, 0x01 ; mask spmen
|
|
brne write3 ; wait for spm to complete
|
|
ldi i, 0x11 ; SPM En + RWWSRE (Enable RWW reads)
|
|
out spmcsr, i
|
|
spm
|
|
movw zsav, zl ; save page address
|
|
in rzsav, rampz ; save bank address
|
|
ldi xl, 0x00
|
|
ldi xh, 0x10 ; start of buffer
|
|
out sreg, j ; restore sreg & int state
|
|
IncBlk:
|
|
inc blkno ; done. Inc the block #
|
|
ldi i, ACK ; send ACK
|
|
call Put_Chr ;
|
|
rjmp StartBlk ; get next block
|
|
Done:
|
|
ldi i, ACK ; last block, send ACK and exit.
|
|
rcall Put_Chr ;
|
|
rcall Flush ; get leftover characters, if any
|
|
rcall Print_Good ;
|
|
out rampz, zero ; reset rampz to zero
|
|
ret ;
|
|
;
|
|
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
;
|
|
; subroutines
|
|
;
|
|
; ;
|
|
GetByte: ; wait for chr input and cycle timing loop
|
|
clr retry ; set low value of timing loop
|
|
StartCrcLp:
|
|
rcall Get_chr ; get chr from serial port, don't wait
|
|
brcs GetByte1 ; got one, so exit
|
|
|
|
ldi j, 0x24 ; delay loop
|
|
getb2234:
|
|
lpm i, Z
|
|
dec j
|
|
brne getb2234 ; delay
|
|
|
|
dec retry ; no character received, so dec counter
|
|
brne StartCrcLp ;
|
|
dec retry2 ; dec hi byte of counter
|
|
brne StartCrcLp ; look for character again
|
|
clc ; if loop times out, CLC, else SEC and return
|
|
GetByte1:
|
|
ret ; with character in "A"
|
|
;
|
|
Flush: ; flush receive buffer
|
|
ldi retry2, 0x70 ; flush until empty for ~1 sec.
|
|
Flush1:
|
|
rcall GetByte ; read the port
|
|
brcs Flush ; if chr recvd, wait for another
|
|
ret ; else done
|
|
;
|
|
PrintMsg:
|
|
in rzsav, rampz
|
|
out rampz, one
|
|
ldi zh, high(Msg*2) ; PRINT starting message
|
|
ldi zl, low(Msg*2) ;
|
|
PrtMsg1:
|
|
elpm i, Z+
|
|
tst i
|
|
breq PrtMsg2
|
|
rcall Put_Chr
|
|
rjmp PrtMsg1
|
|
PrtMsg2:
|
|
out rampz, rzsav
|
|
ret
|
|
|
|
Msg:
|
|
.db "Begin XMODEM/CRC transfer. Press <Esc> to abort..."
|
|
.db CR, LF, 0x00, 0x00
|
|
;
|
|
Print_Err:
|
|
in rzsav, rampz
|
|
out rampz, one
|
|
ldi zh, high(ErrMsg*2) ; PRINT Error message
|
|
ldi zl, low(ErrMsg*2) ;
|
|
PrtErr1:
|
|
elpm i, Z+
|
|
tst i
|
|
breq PrtErr2
|
|
rcall Put_Chr
|
|
rjmp PrtErr1
|
|
PrtErr2:
|
|
out rampz, rzsav
|
|
ret
|
|
|
|
ErrMsg:
|
|
.db "Upload Error! "
|
|
.db CR, LF, 0x00, 0x00
|
|
;
|
|
Print_Good:
|
|
in rzsav, rampz
|
|
out rampz, one
|
|
ldi zh, high(GoodMsg*2) ; PRINT Good Transfer message
|
|
ldi zl, low(GoodMsg*2) ;
|
|
Prtgood1:
|
|
elpm i, Z+
|
|
tst i
|
|
breq Prtgood2
|
|
rcall Put_Chr
|
|
rjmp Prtgood1
|
|
Prtgood2:
|
|
out rampz, rzsav
|
|
ret
|
|
|
|
GoodMsg:
|
|
.db "Upload Successful!"
|
|
.db CR, LF, 0x00, 0x00
|
|
;
|
|
;
|
|
;======================================================================
|
|
; I/O Device Specific Routines
|
|
;
|
|
; Two routines are used to communicate with the I/O device.
|
|
;
|
|
; "Get_Chr" routine will scan the input port for a character. It will
|
|
; return without waiting with the Carry flag CLEAR if no character is
|
|
; present or return with the Carry flag SET and the character in the "A"
|
|
; register if one was present.
|
|
;
|
|
; "Put_Chr" routine will write one byte to the output port. Its alright
|
|
; if this routine waits for the port to be ready. its assumed that the
|
|
; character was send upon return from this routine.
|
|
;
|
|
; input chr from USART (no waiting)
|
|
;
|
|
|
|
Get_Chr:
|
|
clc ; no chr present
|
|
lds i, ucsr0a ; get Serial port status
|
|
sbrs i, RXC0 ; mask rcvr full bit
|
|
rjmp Get_Chr2 ; if not chr, done
|
|
lds i, udr0 ; else get chr
|
|
sec ; and set the Carry Flag
|
|
Get_Chr2:
|
|
ret ; done
|
|
;
|
|
; output to OutPut Port
|
|
;
|
|
Put_Chr:
|
|
lds j, ucsr0a ;
|
|
sbrs j, udre0
|
|
rjmp Put_chr ; is tx buffer empty
|
|
sts udr0, i ; put character to Port
|
|
ret ; done
|
|
;=========================================================================
|
|
;
|
|
;
|
|
; CRC subroutines
|
|
;
|
|
;
|
|
UpdCrc:
|
|
eor i, crch ; Quick CRC computation with lookup tables
|
|
in rzsav, rampz
|
|
out rampz, one
|
|
ldi zl, low(CRCHI*2) ; updates the two bytes at crc & crc+1
|
|
ldi zh, high(CRCHI*2) ; with the byte send in the "A" register
|
|
add zl, i ; with the byte send in the "A" register
|
|
adc zh, zero
|
|
elpm crch, Z
|
|
eor crch, crc
|
|
dec zh ; assumes crclo is 256 bytes lower in mem
|
|
elpm crc, Z
|
|
out rampz, rzsav
|
|
ret
|
|
|
|
; low byte CRC lookup table
|
|
|
|
crclo:
|
|
.db 0x00,0x21,0x42,0x63,0x84,0xA5,0xC6,0xE7,0x08,0x29,0x4A,0x6B,0x8C,0xAD,0xCE,0xEF
|
|
.db 0x31,0x10,0x73,0x52,0xB5,0x94,0xF7,0xD6,0x39,0x18,0x7B,0x5A,0xBD,0x9C,0xFF,0xDE
|
|
.db 0x62,0x43,0x20,0x01,0xE6,0xC7,0xA4,0x85,0x6A,0x4B,0x28,0x09,0xEE,0xCF,0xAC,0x8D
|
|
.db 0x53,0x72,0x11,0x30,0xD7,0xF6,0x95,0xB4,0x5B,0x7A,0x19,0x38,0xDF,0xFE,0x9D,0xBC
|
|
.db 0xC4,0xE5,0x86,0xA7,0x40,0x61,0x02,0x23,0xCC,0xED,0x8E,0xAF,0x48,0x69,0x0A,0x2B
|
|
.db 0xF5,0xD4,0xB7,0x96,0x71,0x50,0x33,0x12,0xFD,0xDC,0xBF,0x9E,0x79,0x58,0x3B,0x1A
|
|
.db 0xA6,0x87,0xE4,0xC5,0x22,0x03,0x60,0x41,0xAE,0x8F,0xEC,0xCD,0x2A,0x0B,0x68,0x49
|
|
.db 0x97,0xB6,0xD5,0xF4,0x13,0x32,0x51,0x70,0x9F,0xBE,0xDD,0xFC,0x1B,0x3A,0x59,0x78
|
|
.db 0x88,0xA9,0xCA,0xEB,0x0C,0x2D,0x4E,0x6F,0x80,0xA1,0xC2,0xE3,0x04,0x25,0x46,0x67
|
|
.db 0xB9,0x98,0xFB,0xDA,0x3D,0x1C,0x7F,0x5E,0xB1,0x90,0xF3,0xD2,0x35,0x14,0x77,0x56
|
|
.db 0xEA,0xCB,0xA8,0x89,0x6E,0x4F,0x2C,0x0D,0xE2,0xC3,0xA0,0x81,0x66,0x47,0x24,0x05
|
|
.db 0xDB,0xFA,0x99,0xB8,0x5F,0x7E,0x1D,0x3C,0xD3,0xF2,0x91,0xB0,0x57,0x76,0x15,0x34
|
|
.db 0x4C,0x6D,0x0E,0x2F,0xC8,0xE9,0x8A,0xAB,0x44,0x65,0x06,0x27,0xC0,0xE1,0x82,0xA3
|
|
.db 0x7D,0x5C,0x3F,0x1E,0xF9,0xD8,0xBB,0x9A,0x75,0x54,0x37,0x16,0xF1,0xD0,0xB3,0x92
|
|
.db 0x2E,0x0F,0x6C,0x4D,0xAA,0x8B,0xE8,0xC9,0x26,0x07,0x64,0x45,0xA2,0x83,0xE0,0xC1
|
|
.db 0x1F,0x3E,0x5D,0x7C,0x9B,0xBA,0xD9,0xF8,0x17,0x36,0x55,0x74,0x93,0xB2,0xD1,0xF0
|
|
|
|
; hi byte CRC lookup table
|
|
|
|
crchi:
|
|
.db 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x81,0x91,0xA1,0xB1,0xC1,0xD1,0xE1,0xF1
|
|
.db 0x12,0x02,0x32,0x22,0x52,0x42,0x72,0x62,0x93,0x83,0xB3,0xA3,0xD3,0xC3,0xF3,0xE3
|
|
.db 0x24,0x34,0x04,0x14,0x64,0x74,0x44,0x54,0xA5,0xB5,0x85,0x95,0xE5,0xF5,0xC5,0xD5
|
|
.db 0x36,0x26,0x16,0x06,0x76,0x66,0x56,0x46,0xB7,0xA7,0x97,0x87,0xF7,0xE7,0xD7,0xC7
|
|
.db 0x48,0x58,0x68,0x78,0x08,0x18,0x28,0x38,0xC9,0xD9,0xE9,0xF9,0x89,0x99,0xA9,0xB9
|
|
.db 0x5A,0x4A,0x7A,0x6A,0x1A,0x0A,0x3A,0x2A,0xDB,0xCB,0xFB,0xEB,0x9B,0x8B,0xBB,0xAB
|
|
.db 0x6C,0x7C,0x4C,0x5C,0x2C,0x3C,0x0C,0x1C,0xED,0xFD,0xCD,0xDD,0xAD,0xBD,0x8D,0x9D
|
|
.db 0x7E,0x6E,0x5E,0x4E,0x3E,0x2E,0x1E,0x0E,0xFF,0xEF,0xDF,0xCF,0xBF,0xAF,0x9F,0x8F
|
|
.db 0x91,0x81,0xB1,0xA1,0xD1,0xC1,0xF1,0xE1,0x10,0x00,0x30,0x20,0x50,0x40,0x70,0x60
|
|
.db 0x83,0x93,0xA3,0xB3,0xC3,0xD3,0xE3,0xF3,0x02,0x12,0x22,0x32,0x42,0x52,0x62,0x72
|
|
.db 0xB5,0xA5,0x95,0x85,0xF5,0xE5,0xD5,0xC5,0x34,0x24,0x14,0x04,0x74,0x64,0x54,0x44
|
|
.db 0xA7,0xB7,0x87,0x97,0xE7,0xF7,0xC7,0xD7,0x26,0x36,0x06,0x16,0x66,0x76,0x46,0x56
|
|
.db 0xD9,0xC9,0xF9,0xE9,0x99,0x89,0xB9,0xA9,0x58,0x48,0x78,0x68,0x18,0x08,0x38,0x28
|
|
.db 0xCB,0xDB,0xEB,0xFB,0x8B,0x9B,0xAB,0xBB,0x4A,0x5A,0x6A,0x7A,0x0A,0x1A,0x2A,0x3A
|
|
.db 0xFD,0xED,0xDD,0xCD,0xBD,0xAD,0x9D,0x8D,0x7C,0x6C,0x5C,0x4C,0x3C,0x2C,0x1C,0x0C
|
|
.db 0xEF,0xFF,0xCF,0xDF,0xAF,0xBF,0x8F,0x9F,0x6E,0x7E,0x4E,0x5E,0x2E,0x3E,0x0E,0x1E
|
|
;
|
|
;
|
|
; End of File
|
|
;
|