Files
SyncHome/trunk/workspace/AVR-Computer/AVR65C02/AVRSource/xmodem-receive.asm
2023-03-13 08:36:51 +00:00

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
;