; upload.asm (hooked into SbcOS at $fa00-feff) ; By Daryl Rictor & Ross Archer Aug 2002 ; ; 21st century code for 20th century CPUs (tm?) ; ; A simple file transfer program to allow upload from a serial ; port to the SBC. It integrates both x-modem/CRC transfer protocol ; and Intel Hex formatted files. Primary is XMODEM-CRC, due to its ; superior reliability. Fallback to Intel Hex is automagical following ; receipt of the first Hexfile d/l character, so the selection is ; transparent to the user. ; ; Files uploaded via XMODEM-CRC must be ; in .o64 format -- the first two bytes are the load address in ; little-endian format: ; FIRST BLOCK ; offset(0) = lo(load start address), ; offset(1) = hi(load start address) ; offset(2) = data byte (0) ; offset(n) = data byte (n-2) ; ; Subsequent blocks ; offset(n) = data byte (n) ; ; The TASS assembler and most Commodore 64-based tools generate this ; data format automatically and you can transfer their .obj/.o64 output ; file directly. ; ; The only time you need to do anything special is if you have ; a raw memory image file (say you want to load a data ; table into memory). For XMODEM you'll have to ; "insert" the start address bytes to the front of the file. ; Otherwise, XMODEM would have no idea where to start putting ; the data. ; ; The "fallback" format is Intel Hex. As address information is included ; at the start of each line of an Intel Hex file, there is no need for a special ; "first block". As soon as the receiver sees an Intel Hex ; character ':' coming in, it aborts the XMODEM-CRC upload attempt and ; tries to accept Intel Hex instead. This is the format used natively ; by a lot of generic tools such as TASM. ; Note there is no "fallback fallback." Once it quits CRC and ; thinks you're sending it Intel Hex, you either have to finish the download ; or press CTRL-C to abort. ; ; By having support for both formats under the same "U"pload command, ; it enables seamless switching between either kind of toolchain with ; no special user intervention. This seemed like a Good Thing (tm). ; ; Note: testing shows that no end-of-line delay is required for Intel Hex ; uploads, but in case your circumstances differ and you encounter ; error indications from a download (especially if you decided to run the ; controller under 1 Mhz), adding a 10-50 mS delay after each line is ; harmless and will ensure no problems even at low clock speeds ; ; ; Style conventions being tried on this file for possible future adoption: ; 1. Constants known at assembly time are ALL CAPS ; 2. Variables are all lower-case, with underscores used as the word separator ; 3. Labels are PascalStyleLikeThis to distinguish from constants and variables ; 4. Old labels from external modules are left alone. We may want ; to adopt these conventions and retrofit old source later. ; 5. Op-codes are lower-case ; 6. Comments are free-style but ought to line up with similar adjacent comments ; zero page variables (Its ok to stomp on the monitor's zp vars) ; ; crc = $38 ; CRC lo byte crch = $39 ; CRC hi byte ptr = $3a ; data pointer ptrh = $3b ; " " blkno = $3c ; block number retry = $3d ; retry counter retry2 = $3e ; 2nd counter bflag = $3f ; block flag reclen = $39 ; record length in bytes chksum = crc ; record checksum accumulator start_lo = $3b start_hi = $3c rectype = $3d dlfail = $3e ; flag for upload failure temp = $3f ; save hex value strptr = $40 strptrh = $41 ; temporary string pointer (not preserved across calls) ; ; tables and constants ; ;crclo = $7a00 ; Two 256-byte tables for quick lookup ;crchi = $7b00 ; (should be page-aligned for speed) Rbuff = $0300 ; temp 128 byte receive buffer ; (uses the Monitor's input buffer) SOH = $01 ; start block EOT = $04 ; end of text marker ACK = $06 ; good block acknowleged NAK = $15 ; bad block acknowleged CAN = $18 ; cancel (not standard, not supported) CRN = 13 LF = 10 ESC = 27 ; ESC to exit ; *= $fa00 ; lo CRC lookup table crclo .byte $00,$21,$42,$63,$84,$A5,$C6,$E7,$08,$29,$4A,$6B,$8C,$AD,$CE,$EF .byte $31,$10,$73,$52,$B5,$94,$F7,$D6,$39,$18,$7B,$5A,$BD,$9C,$FF,$DE .byte $62,$43,$20,$01,$E6,$C7,$A4,$85,$6A,$4B,$28,$09,$EE,$CF,$AC,$8D .byte $53,$72,$11,$30,$D7,$F6,$95,$B4,$5B,$7A,$19,$38,$DF,$FE,$9D,$BC .byte $C4,$E5,$86,$A7,$40,$61,$02,$23,$CC,$ED,$8E,$AF,$48,$69,$0A,$2B .byte $F5,$D4,$B7,$96,$71,$50,$33,$12,$FD,$DC,$BF,$9E,$79,$58,$3B,$1A .byte $A6,$87,$E4,$C5,$22,$03,$60,$41,$AE,$8F,$EC,$CD,$2A,$0B,$68,$49 .byte $97,$B6,$D5,$F4,$13,$32,$51,$70,$9F,$BE,$DD,$FC,$1B,$3A,$59,$78 .byte $88,$A9,$CA,$EB,$0C,$2D,$4E,$6F,$80,$A1,$C2,$E3,$04,$25,$46,$67 .byte $B9,$98,$FB,$DA,$3D,$1C,$7F,$5E,$B1,$90,$F3,$D2,$35,$14,$77,$56 .byte $EA,$CB,$A8,$89,$6E,$4F,$2C,$0D,$E2,$C3,$A0,$81,$66,$47,$24,$05 .byte $DB,$FA,$99,$B8,$5F,$7E,$1D,$3C,$D3,$F2,$91,$B0,$57,$76,$15,$34 .byte $4C,$6D,$0E,$2F,$C8,$E9,$8A,$AB,$44,$65,$06,$27,$C0,$E1,$82,$A3 .byte $7D,$5C,$3F,$1E,$F9,$D8,$BB,$9A,$75,$54,$37,$16,$F1,$D0,$B3,$92 .byte $2E,$0F,$6C,$4D,$AA,$8B,$E8,$C9,$26,$07,$64,$45,$A2,$83,$E0,$C1 .byte $1F,$3E,$5D,$7C,$9B,$BA,$D9,$F8,$17,$36,$55,$74,$93,$B2,$D1,$F0 *= $fb00 ; hi CRC lookup table crchi .byte $00,$10,$20,$30,$40,$50,$60,$70,$81,$91,$A1,$B1,$C1,$D1,$E1,$F1 .byte $12,$02,$32,$22,$52,$42,$72,$62,$93,$83,$B3,$A3,$D3,$C3,$F3,$E3 .byte $24,$34,$04,$14,$64,$74,$44,$54,$A5,$B5,$85,$95,$E5,$F5,$C5,$D5 .byte $36,$26,$16,$06,$76,$66,$56,$46,$B7,$A7,$97,$87,$F7,$E7,$D7,$C7 .byte $48,$58,$68,$78,$08,$18,$28,$38,$C9,$D9,$E9,$F9,$89,$99,$A9,$B9 .byte $5A,$4A,$7A,$6A,$1A,$0A,$3A,$2A,$DB,$CB,$FB,$EB,$9B,$8B,$BB,$AB .byte $6C,$7C,$4C,$5C,$2C,$3C,$0C,$1C,$ED,$FD,$CD,$DD,$AD,$BD,$8D,$9D .byte $7E,$6E,$5E,$4E,$3E,$2E,$1E,$0E,$FF,$EF,$DF,$CF,$BF,$AF,$9F,$8F .byte $91,$81,$B1,$A1,$D1,$C1,$F1,$E1,$10,$00,$30,$20,$50,$40,$70,$60 .byte $83,$93,$A3,$B3,$C3,$D3,$E3,$F3,$02,$12,$22,$32,$42,$52,$62,$72 .byte $B5,$A5,$95,$85,$F5,$E5,$D5,$C5,$34,$24,$14,$04,$74,$64,$54,$44 .byte $A7,$B7,$87,$97,$E7,$F7,$C7,$D7,$26,$36,$06,$16,$66,$76,$46,$56 .byte $D9,$C9,$F9,$E9,$99,$89,$B9,$A9,$58,$48,$78,$68,$18,$08,$38,$28 .byte $CB,$DB,$EB,$FB,$8B,$9B,$AB,$BB,$4A,$5A,$6A,$7A,$0A,$1A,$2A,$3A .byte $FD,$ED,$DD,$CD,$BD,$AD,$9D,$8D,$7C,$6C,$5C,$4C,$3C,$2C,$1C,$0C .byte $EF,$FF,$CF,$DF,$AF,$BF,$8F,$9F,$6E,$7E,$4E,$5E,$2E,$3E,$0E,$1E ; ; Xmodem/CRC upload routine ; By Daryl Rictor, July 31, 2002 ; ; ; v0.3 tested good minus CRC ; v0.4 CRC fixed!!! init to $0000 rather than $FFFF as stated ; v0.5 added CRC tables vs. generation at run time ; v 1.0 recode for use with SBC2 ; v 1.1 added block 1 masking (block 257 would be corrupted) *= $fc00 ; XModem jsr prtMsg ; send prompt and info lda #$01 sta blkno ; set block # to 1 sta bflag ; set flag to get address from block 1 StartCrc lda #"C" ; "C" start with CRC mode jsr output ; send it lda #$FF sta retry2 ; set loop counter for ~3 sec delay lda #$00 sta crc sta crch ; init CRC value jsr GetByte ; wait for input bcs GotByte ; byte received, process it bcc StartCrc ; resend "C" StartBlk lda #$FF ; sta retry2 ; set loop counter for ~3 sec delay lda #$00 ; sta crc ; sta crch ; init CRC value jsr GetByte ; get first byte of block bcc StartBlk ; timed out, keep waiting... GotByte cmp #ESC ; quitting? bne GotByte3 brk GotByte3 cmp #SOH ; start of block? beq BegBlk ; yes cmp #":" ; Intel-Hex format - jump to its handler below bne GotByte1 ; jmp HexUpLd ; GotByte1 cmp #EOT ; bne BadCrc ; Not SOH, ":", EOT, so flush buffer & send NAK jmp Done ; EOT - all done! BegBlk ldx #$00 GetBlk lda #$ff ; 3 sec window to receive characters sta retry2 ; GetBlk1 jsr GetByte ; get next character bcc BadCrc ; chr rcv error, flush and send NAK GetBlk2 sta Rbuff,x ; good char, save it in the rcv buffer inx ; inc buffer pointer cpx #$84 ; <01> <128 bytes> bne GetBlk ; get 132 characters ldx #$00 ; lda Rbuff,x ; get block # from buffer cmp blkno ; compare to expected block # beq GoodBlk1 ; matched! lda #>MsgCrcBadBlkno ldx #MsgCrcBadBlkno ldx #MsgCrcDone ldx # to abort... " .byte 0 ; ; Flush lda #$70 ; flush receive buffer sta retry2 ; flush until empty for ~1 sec. Flush1 jsr GetByte ; read the port bcs Flush rts ; ; CRC subroutines ; UpdCrc eor crc+1 ; Quick CRC computation with lookup tables tax lda crc eor CRCHI,X sta crc+1 lda CRCLO,X sta crc rts ; ; ;**************************************************** ; ; Intel-hex 6502 upload program ; Ross Archer, 25 July 2002 ; ; HexUpLd lda #CRN jsr output lda #LF jsr output lda #0 sta dlfail ;Start by assuming no D/L failure beq IHex HdwRecs jsr GetSer ; Wait for start of record mark ':' cmp #":" bne HdwRecs ; not found yet ; Start of record marker has been found IHex jsr GetHex ; Get the record length sta reclen ; save it sta chksum ; and save first byte of checksum jsr GetHex ; Get the high part of start address sta start_hi clc adc chksum ; Add in the checksum sta chksum ; jsr GetHex ; Get the low part of the start address sta start_lo clc adc chksum sta chksum jsr GetHex ; Get the record type sta rectype ; & save it clc adc chksum sta chksum lda rectype bne HdEr1 ; end-of-record ldx reclen ; number of data bytes to write to memory ldy #0 ; start offset at 0 HdLp1 jsr GetHex ; Get the first/next/last data byte sta (start_lo),y ; Save it to RAM clc adc chksum sta chksum ; iny ; update data pointer dex ; decrement count bne HdLp1 jsr GetHex ; get the checksum clc adc chksum bne HdDlF1 ; If failed, report it ; Another successful record has been processed lda #"#" ; Character indicating record OK = '#' sta UDR0 ; write it out but don't wait for output jmp HdwRecs ; get next record HdDlF1 lda #"F" ; Character indicating record failure = 'F' sta dlfail ; upload failed if non-zero sta UDR0 ; write it to transmit buffer register jmp HdwRecs ; wait for next record start HdEr1 cmp #1 ; Check for end-of-record type beq HdEr2 lda #>MsgUnknownRecType ldx # upload has failed jsr Print1Byte ; print it lda #CRN ; but we'll let it finish so as not to jsr output ; falsely start a new d/l from existing lda #LF ; file that may still be coming in for jsr output ; quite some time yet. jmp HdwRecs ; We've reached the end-of-record record HdEr2 jsr GetHex ; get the checksum clc adc chksum ; Add previous checksum accumulator value beq HdEr3 ; checksum = 0 means we're OK! lda #>MsgBadRecChksum ldx #MsgUploadFail ldx #MsgUploadOK ldx # 255 PrintStrAXX1 pla tay rts ; CRC messages MsgCrcBadBlkno .byte CRN,LF,CRN,LF .byte "Unexpected block number received" .byte " Aborting" .byte CRN,LF .byte 0 MsgCrcDone .byte CRN,LF .byte "XMODEM-CRC download is complete" .byte 0 ; Checksum messages ; MsgUnknownRecType .byte CRN,LF,CRN,LF .byte "Unknown record type $" .byte 0 ; null-terminate every string MsgBadRecChksum .byte CRN,LF,CRN,LF .byte "Bad record checksum!" .byte 0 ; Null-terminate MsgUploadFail .byte CRN,LF,CRN,LF .byte "Upload Failed",CRN,LF .byte "Aborting!" .byte 0 ; null-terminate every string or crash'n'burn MsgUploadOK .byte CRN,LF,CRN,LF .byte "Upload Successful!" .byte 0 ; Fin. ;