2009-12-06 06:24:18 +00:00
; XMODEM file transfer
.include " . . / inc/ c o m m o n . i "
.ifndef KPR_API_VERSION_NUMBER
.define EQU =
.include " . . / inc/ k i p p e r _ c o n s t a n t s . i "
.endif
XMODEM_ B L O C K _ S I Z E = $ 8 0 ;how many bytes (excluding header & checksum) in each block?
XMODEM_ T I M E O U T _ S E C O N D S =5
XMODEM_ M A X _ E R R O R S =10
SOH = $ 0 1
EOT = $ 0 4
ACK = $ 0 6
NAK = $ 1 5
CAN = $ 1 8
2009-12-21 09:59:05 +00:00
PAD = $ 1 A ;padding added to end of file
2009-12-06 06:24:18 +00:00
.export xmodem_receive
2009-12-20 11:14:43 +00:00
.export xmodem_send
2009-12-06 06:24:18 +00:00
2009-12-20 08:24:10 +00:00
.export xmodem_iac_escape ;are IAC bytes ($FF) escaped?
2009-12-06 06:24:18 +00:00
.import ip65_process
.import ip65_error
.import tcp_callback
.import copymem
.importzp copy_src
.importzp copy_dest
.import tcp_send
.import tcp_send_data_len
.import tcp_inbound_data_ptr
.import tcp_inbound_data_length
.import check_for_abort_key
.import print_a
.import print_cr
.import print_ascii_as_native
.import print_hex
.segment " SELF_ M O D I F I E D _ C O D E "
got_byte :
jmp $ f f f f
2009-12-20 11:14:43 +00:00
get_byte :
jmp $ f f f f
2009-12-06 06:24:18 +00:00
next_char :
lda b u f f e r _ l e n g t h
bne @not_eof
lda b u f f e r _ l e n g t h + 1
bne @not_eof
sec
rts
@not_eof:
next_ c h a r _ p t r = * + 1
lda $ f f f f
pha
inc n e x t _ c h a r _ p t r
bne : +
inc n e x t _ c h a r _ p t r + 1
:
sec
lda b u f f e r _ l e n g t h
sbc #1
sta b u f f e r _ l e n g t h
lda b u f f e r _ l e n g t h + 1
sbc #0
sta b u f f e r _ l e n g t h + 1
pla
2009-12-20 11:14:43 +00:00
clc
rts
2009-12-06 06:24:18 +00:00
2009-12-20 11:14:43 +00:00
emit_a :
2009-12-21 09:59:05 +00:00
;put a byte to the output buffer
;if the byte is $FF, and xmodem_iac_escape is not zero, it is doubled
jsr @real_emit_a
cmp #$ f f
bne e x i t _ e m i t _ a
ldx x m o d e m _ i a c _ e s c a p e
beq e x i t _ e m i t _ a
@real_emit_a:
2009-12-20 11:14:43 +00:00
emit_ a _ p t r = * + 1
sta $ f f f f
inc e m i t _ a _ p t r
bne : +
inc e m i t _ a _ p t r + 1
:
inc x m o d e m _ b l o c k _ b u f f e r _ l e n g t h
bne : +
inc x m o d e m _ b l o c k _ b u f f e r _ l e n g t h + 1
:
2009-12-21 09:59:05 +00:00
exit_emit_a :
2009-12-06 06:24:18 +00:00
rts
2009-12-20 11:14:43 +00:00
2009-12-06 06:24:18 +00:00
.bss
original_tcp_callback : .res 2
getc_timeout_end : .res 1
getc_timeout_seconds : .res 1
buffer_length : .res 2
.code
2009-12-20 11:14:43 +00:00
xmodem_send :
;send a file via XMODEM (checksum mode only, not CRC)
;assumes that a tcp connection has already been set up, and that the other end is waiting to start receiving
;inputs: AX points to routine to call once for each byte in file to send (e.g. save to disk, print to screen, whatever) - byte will be in A, carry flag set means EOF
2009-12-20 08:24:10 +00:00
; xmodem_iac_escape should be set to non-zero if the remote end escapes $FF bytes (i.e. if it is a real telnet server)
2009-12-06 06:24:18 +00:00
;outputs: none
2009-12-20 11:14:43 +00:00
stax g e t _ b y t e + 1
jsr x m o d e m _ t r a n s f e r _ s e t u p
2009-12-21 09:59:05 +00:00
lda #0
sta a t _ e o f
2009-12-20 11:14:43 +00:00
@send_block:
ldax #s e n d i n g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
ldax #b l o c k _ n u m b e r _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
lda e x p e c t e d _ b l o c k _ n u m b e r
jsr p r i n t _ h e x
jsr p r i n t _ c r
@wait_for_ack_or_nak:
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c
bcs @synch_error
cmp #A C K
beq @got_ack
cmp #N A K
beq @got_nak
2009-12-06 06:24:18 +00:00
2009-12-20 11:14:43 +00:00
@synch_error:
pha
lda u s e r _ a b o r t
beq @no_user_abort
2009-12-21 09:59:05 +00:00
pla
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
@no_user_abort:
2009-12-21 09:59:05 +00:00
2009-12-06 06:24:18 +00:00
2009-12-21 09:59:05 +00:00
;flush the input buffer
lda #0
sta b u f f e r _ l e n g t h
lda b u f f e r _ l e n g t h + 1
2009-12-20 11:14:43 +00:00
lda #' ( '
jsr p r i n t _ a
pla
jsr p r i n t _ h e x
lda #' ) '
jsr p r i n t _ a
inc e r r o r _ n u m b e r
ldax #s y n c _ e r r o r _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
ldax #e r r o r _ c o u n t _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
lda e r r o r _ n u m b e r
jsr p r i n t _ h e x
jsr p r i n t _ c r
lda e r r o r _ n u m b e r
cmp #X M O D E M _ M A X _ E R R O R S
bcc @wait_for_ack_or_nak
lda #K P R _ E R R O R _ T O O _ M A N Y _ E R R O R S
sta i p65 _ e r r o r
2009-12-21 09:59:05 +00:00
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
@got_ack:
inc e x p e c t e d _ b l o c k _ n u m b e r
2009-12-21 09:59:05 +00:00
lda a t _ e o f
bne @send_eot
2009-12-20 11:14:43 +00:00
@got_nak:
lda #0
sta c h e c k s u m
sta x m o d e m _ b l o c k _ b u f f e r _ l e n g t h
sta x m o d e m _ b l o c k _ b u f f e r _ l e n g t h + 1
ldax #x m o d e m _ b l o c k _ b u f f e r
stax e m i t _ a _ p t r
lda #S O H
jsr e m i t _ a
lda e x p e c t e d _ b l o c k _ n u m b e r
jsr e m i t _ a
eor #$ f f
jsr e m i t _ a
lda #$ 80
sta b l o c k _ p t r
@copy_one_byte:
2009-12-21 09:59:05 +00:00
lda a t _ e o f
bne @add_pad_byte
jsr g e t _ b y t e
bcc @got_byte
;sec indicates EOF
lda b l o c k _ p t r
cmp #$ 80 ;have we sent any data at all?
bne @add_pad_byte
@send_eot:
;if we get here, we should send an EOT, then read an ACK
ldax #1
stax t c p _ s e n d _ d a t a _ l e n
ldax #e o t _ p a c k e t
jsr t c p _ s e n d
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c ;should be an ACK coming back, doesn't really matter if we don't see it though
jmp x m o d e m _ t r a n s f e r _ e x i t
@add_pad_byte:
lda #P A D
@got_byte:
2009-12-20 11:14:43 +00:00
pha
clc
adc c h e c k s u m
sta c h e c k s u m
pla
jsr e m i t _ a
dec b l o c k _ p t r
bne @copy_one_byte
lda c h e c k s u m
jsr e m i t _ a
ldax x m o d e m _ b l o c k _ b u f f e r _ l e n g t h
stax t c p _ s e n d _ d a t a _ l e n
ldax #x m o d e m _ b l o c k _ b u f f e r
jsr t c p _ s e n d
2009-12-21 09:59:05 +00:00
bcc @send_ok
ldax #s e n d _ e r r o r
jsr p r i n t _ a s c i i _ a s _ n a t i v e
lda i p65 _ e r r o r
jsr p r i n t _ h e x
jsr p r i n t _ c r
@send_ok:
2009-12-20 11:14:43 +00:00
jmp @send_block
rts
xmodem_transfer_setup :
2009-12-06 06:24:18 +00:00
lda #0
sta b u f f e r _ l e n g t h
sta b u f f e r _ l e n g t h + 1
sta e r r o r _ n u m b e r
sta u s e r _ a b o r t
lda #1
sta e x p e c t e d _ b l o c k _ n u m b e r
ldax t c p _ c a l l b a c k
stax o r i g i n a l _ t c p _ c a l l b a c k
2009-12-20 11:14:43 +00:00
ldax #x m o d e m _ t c p _ c a l l b a c k
2009-12-06 06:24:18 +00:00
stax t c p _ c a l l b a c k
2009-12-20 11:14:43 +00:00
rts
xmodem_receive :
;recieve a file via XMODEM (checksum mode only, not CRC)
;assumes that a tcp connection has already been set up, and that the other end is waiting to start sending
;inputs: AX points to routine to call once for each byte in downloaded file (e.g. save to disk, print to screen, whatever) - byte will be in A
; xmodem_iac_escape should be set to non-zero if the remote end escapes $FF bytes (i.e. if it is a real telnet server)
;outputs: none
stax g o t _ b y t e + 1
jsr x m o d e m _ t r a n s f e r _ s e t u p
2009-12-06 06:24:18 +00:00
jsr s e n d _ n a k
@next_block:
lda #0
sta b l o c k _ p t r
sta c h e c k s u m
ldax #e x p e c t i n g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
ldax #b l o c k _ n u m b e r _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
lda e x p e c t e d _ b l o c k _ n u m b e r
jsr p r i n t _ h e x
jsr p r i n t _ c r
@wait_for_block_start:
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c
bcc @got_block_start
lda u s e r _ a b o r t
beq @no_user_abort
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
2009-12-06 06:24:18 +00:00
@no_user_abort:
jsr s e n d _ n a k
inc e r r o r _ n u m b e r
ldax #t i m e o u t _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
2009-12-20 11:14:43 +00:00
ldax #e r r o r _ c o u n t _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
2009-12-06 06:24:18 +00:00
lda e r r o r _ n u m b e r
jsr p r i n t _ h e x
jsr p r i n t _ c r
lda e r r o r _ n u m b e r
cmp #X M O D E M _ M A X _ E R R O R S
bcc @wait_for_block_start
lda #K P R _ E R R O R _ T O O _ M A N Y _ E R R O R S
sta i p65 _ e r r o r
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
2009-12-06 06:24:18 +00:00
@got_block_start:
cmp #E O T
bne : +
jsr s e n d _ a c k
clc
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
2009-12-06 06:24:18 +00:00
:
cmp #S O H
bne @wait_for_block_start
;now get block number
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c
bcc : +
jsr s e n d _ n a k
jmp @wait_for_block_start
:
sta a c t u a l _ b l o c k _ n u m b e r
;now get block number check
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c
bcc : +
jsr s e n d _ n a k
jmp @wait_for_block_start
:
adc a c t u a l _ b l o c k _ n u m b e r
cmp #$ f f
bne @wait_for_block_start
ldax #r e c e i v i n g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
ldax #b l o c k _ n u m b e r _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
lda a c t u a l _ b l o c k _ n u m b e r
jsr p r i n t _ h e x
jsr p r i n t _ c r
@next_byte:
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c
2009-12-20 08:24:10 +00:00
bcc : +
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
2009-12-20 08:24:10 +00:00
:
2009-12-06 06:24:18 +00:00
ldx b l o c k _ p t r
sta x m o d e m _ b l o c k _ b u f f e r ,x
adc c h e c k s u m
sta c h e c k s u m
inc b l o c k _ p t r
lda b l o c k _ p t r
bpl @next_byte
ldax #c h e c k s u m _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
2009-12-20 11:14:43 +00:00
2009-12-06 06:24:18 +00:00
lda c h e c k s u m
jsr p r i n t _ h e x
lda #' / '
jsr p r i n t _ a
lda #X M O D E M _ T I M E O U T _ S E C O N D S
jsr g e t c
2009-12-20 11:14:43 +00:00
bcs x m o d e m _ t r a n s f e r _ e x i t
2009-12-06 06:24:18 +00:00
sta r e c e i v e d _ c h e c k s u m
jsr p r i n t _ h e x
jsr p r i n t _ c r
lda r e c e i v e d _ c h e c k s u m
cmp c h e c k s u m
beq @checksum_ok
2009-12-20 08:24:10 +00:00
;checksum error :-(
inc e r r o r _ n u m b e r
ldax #c h e c k s u m _ e r r o r _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
2009-12-20 11:14:43 +00:00
ldax #e r r o r _ c o u n t _ m s g
jsr p r i n t _ a s c i i _ a s _ n a t i v e
2009-12-20 08:24:10 +00:00
lda e r r o r _ n u m b e r
jsr p r i n t _ h e x
jsr p r i n t _ c r
lda e r r o r _ n u m b e r
cmp #X M O D E M _ M A X _ E R R O R S
bcs : +
jmp @wait_for_block_start
:
lda #K P R _ E R R O R _ T O O _ M A N Y _ E R R O R S
sta i p65 _ e r r o r
2009-12-20 11:14:43 +00:00
jmp x m o d e m _ t r a n s f e r _ e x i t
2009-12-20 08:24:10 +00:00
2009-12-06 06:24:18 +00:00
jsr s e n d _ n a k
jmp @next_block
@checksum_ok:
lda e x p e c t e d _ b l o c k _ n u m b e r
cmp a c t u a l _ b l o c k _ n u m b e r
bne @skip_block_output
lda #0
sta b l o c k _ p t r
@output_byte:
ldx b l o c k _ p t r
lda x m o d e m _ b l o c k _ b u f f e r ,x
jsr g o t _ b y t e
inc b l o c k _ p t r
lda b l o c k _ p t r
bpl @output_byte
inc e x p e c t e d _ b l o c k _ n u m b e r
@skip_block_output:
jsr s e n d _ a c k
jmp @next_block
clc
2009-12-20 11:14:43 +00:00
xmodem_transfer_exit :
2009-12-06 06:24:18 +00:00
ldax o r i g i n a l _ t c p _ c a l l b a c k
stax t c p _ c a l l b a c k
rts
2009-12-20 11:14:43 +00:00
xmodem_tcp_callback :
2009-12-06 06:24:18 +00:00
lda t c p _ i n b o u n d _ d a t a _ l e n g t h + 1
cmp #$ f f
bne @not_eof
rts
@not_eof:
ldax t c p _ i n b o u n d _ d a t a _ p t r
stax c o p y _ s r c
ldax #x m o d e m _ s t r e a m _ b u f f e r
stax c o p y _ d e s t
stax n e x t _ c h a r _ p t r
ldax t c p _ i n b o u n d _ d a t a _ l e n g t h
stax b u f f e r _ l e n g t h
2009-12-20 11:14:43 +00:00
jsr c o p y m e m
rts
2009-12-06 06:24:18 +00:00
send_nak :
ldax #1
stax t c p _ s e n d _ d a t a _ l e n
ldax #n a k _ p a c k e t
jmp t c p _ s e n d
send_ack :
ldax #1
stax t c p _ s e n d _ d a t a _ l e n
ldax #a c k _ p a c k e t
jmp t c p _ s e n d
getc :
2009-12-20 08:24:10 +00:00
jsr @real_getc
bcc : + ;of we got an error, then bail
rts
:
cmp #$ f f
beq @got_ff
clc
rts
@got_ff:
lda x m o d e m _ i a c _ e s c a p e
bne @real_getc ;need to skip over the $FF and go read another byte
lda #$ f f
clc
rts
@real_getc:
2009-12-06 06:24:18 +00:00
sta g e t c _ t i m e o u t _ s e c o n d s
clc
lda $ d c09 ;time of day clock: seconds (in BCD)
sed
adc g e t c _ t i m e o u t _ s e c o n d s
cmp #$ 60
bcc @timeout_set
sec
sbc #$ 60
@timeout_set:
cld
sta g e t c _ t i m e o u t _ e n d
@poll_loop:
jsr n e x t _ c h a r
bcs @no_char
rts ;done!
@no_char:
jsr c h e c k _ f o r _ a b o r t _ k e y
bcc @no_abort
lda #K P R _ E R R O R _ A B O R T E D _ B Y _ U S E R
sta i p65 _ e r r o r
inc u s e r _ a b o r t
rts
@no_abort:
jsr i p65 _ p r o c e s s
lda $ d c09 ;time of day clock: seconds
cmp g e t c _ t i m e o u t _ e n d
bne @poll_loop
2009-12-20 11:14:43 +00:00
lda #00
2009-12-06 06:24:18 +00:00
sec
rts
.rodata
ack_packet : .byte A C K
nak_packet : .byte N A K
2009-12-21 09:59:05 +00:00
eot_packet : .byte E O T
2009-12-06 06:24:18 +00:00
block_number_msg : .byte " block $ " , 0
expecting : .byte " expecting " , 0
receiving : .byte " receiving " , 0
2009-12-20 11:14:43 +00:00
sending : .byte " sending " , 0
2009-12-06 06:24:18 +00:00
bad_block_number : .byte " bad block number " , 0
checksum_msg : .byte " checksum $ " , 0
2009-12-20 11:14:43 +00:00
checksum_ e r r o r _ m s g : . b y t e " c h e c k s u m " ,0
timeout_msg : .byte " timeout error " , 0
sync_error_msg : .byte " sync " , 0
error_count_msg : .byte " error - error count $ " , 0
2009-12-21 09:59:05 +00:00
send_error : .byte " send error - $ " , 0
2009-12-06 06:24:18 +00:00
.segment " APP_ S C R A T C H "
xmodem_stream_buffer : .res 1600
2009-12-20 11:14:43 +00:00
xmodem_block_buffer : .res 300
xmodem_block_buffer_length : .res 2
2009-12-06 06:24:18 +00:00
expected_block_number : .res 1
actual_block_number : .res 1
checksum : .res 1
received_checksum : .res 1
block_ptr : .res 1
error_number : .res 1
user_abort : .res 1
2009-12-20 08:24:10 +00:00
xmodem_iac_escape : .res 1
2009-12-21 09:59:05 +00:00
at_eof : .res 1
2009-12-06 06:24:18 +00:00
;-- LICENSE FOR xmodem.s --
; The contents of this file are subject to the Mozilla Public License
; Version 1.1 (the "License"); you may not use this file except in
; compliance with the License. You may obtain a copy of the License at
; http://www.mozilla.org/MPL/
;
; Software distributed under the License is distributed on an "AS IS"
; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
; License for the specific language governing rights and limitations
; under the License.
;
; The Original Code is ip65.
;
; The Initial Developer of the Original Code is Jonno Downes,
; jonno@jamtronix.com.
; Portions created by the Initial Developer are Copyright (C) 2009
; Jonno Downes. All Rights Reserved.
; -- LICENSE END --