;TCP (transmission control protocol) functions ;NB to use these functions, you must pass "-DTCP" to ca65 when assembling "ip.s" ;otherwise inbound tcp packets won't get passed in to tcp_process ;currently only a single outbound (client) connection is supported ;to use, first call "tcp_connect" to create a connection. to send data on that connection, call "tcp_send". ;whenever data arrives, a call will be made to the routine pointed at by tcp_callback. MAX_TCP_PACKETS_SENT=8 ;timeout after sending 8 messages will be about 7 seconds (1+2+3+4+5+6+7+8)/4 .include "../inc/common.i" .ifndef KPR_API_VERSION_NUMBER .define EQU = .include "../inc/kipper_constants.i" .endif .import ip65_error .export tcp_init .export tcp_process .export tcp_connect .export tcp_callback .export tcp_connect_ip .export tcp_send_data_len .export tcp_send .export tcp_send_string .export tcp_close .export tcp_listen .export tcp_send_keep_alive .export tcp_connect_remote_port .export tcp_remote_ip .export tcp_state .export tcp_inbound_data_ptr .export tcp_inbound_data_length .import ip_calc_cksum .import ip_send .import ip_create_packet .import ip_inp .import ip_outp .import ip65_process .import check_for_abort_key .import timer_read .import ip65_random_word .importzp acc32 .importzp op32 .importzp acc16 .import add_32_32 .import add_16_32 .import cmp_32_32 .import cmp_16_16 .import sub_16_16 .importzp ip_cksum_ptr .importzp ip_header_cksum .importzp ip_src .importzp ip_dest .importzp ip_data .importzp ip_proto .importzp ip_proto_tcp .importzp ip_id .importzp ip_len .import copymem .importzp copy_src .importzp copy_dest .import cfg_ip tcp_cxn_state_closed = 0 tcp_cxn_state_listening = 1 ;(waiting for an inbound SYN) tcp_cxn_state_syn_sent = 2 ;(waiting for an inbound SYN/ACK) tcp_cxn_state_established = 3 ; ; tcp packet offsets tcp_inp = ip_inp + ip_data ;pointer to tcp packet inside inbound ethernet frame tcp_outp = ip_outp + ip_data ;pointer to tcp packet inside outbound ethernet frame tcp_src_port = 0 ;offset of source port field in tcp packet tcp_dest_port = 2 ;offset of destination port field in tcp packet tcp_seq = 4 ;offset of sequence number field in tcp packet tcp_ack = 8 ;offset of acknowledgement field in tcp packet tcp_header_length = 12 ;offset of header length field in tcp packet tcp_flags_field = 13 ;offset of flags field in tcp packet tcp_window_size = 14 ; offset of window size field in tcp packet tcp_checksum = 16 ; offset of checksum field in tcp packet tcp_urgent_pointer = 18 ; offset of urgent pointer field in tcp packet tcp_data=20 ;offset of data in tcp packet ; virtual header tcp_vh = tcp_outp - 12 tcp_vh_src = 0 tcp_vh_dest = 4 tcp_vh_zero = 8 tcp_vh_proto = 9 tcp_vh_len = 10 ; tcp_flag_FIN =1 tcp_flag_SYN =2 tcp_flag_RST =4 tcp_flag_PSH =8 tcp_flag_ACK =16 tcp_flag_URG =32 .segment "TCP_VARS" tcp_state: .res 1 tcp_local_port: .res 2 tcp_remote_port: .res 2 tcp_remote_ip: .res 4 tcp_sequence_number: .res 4 tcp_ack_number: .res 4 tcp_data_ptr: .res 2 tcp_data_len: .res 2 tcp_send_data_ptr: .res 2 tcp_send_data_len: .res 2 ;length (in bytes) of data to be sent over tcp connection tcp_callback: .res 2 ;vector to routine to be called when data is received over tcp connection tcp_flags: .res 1 tcp_fin_sent: .res 1 tcp_listen_port: .res 2 tcp_inbound_data_ptr: .res 2 ;pointer to data just recieved over tcp connection tcp_inbound_data_length: .res 2 ;length of data just received over tcp connection ;(if this is $ffff, that means "end of file", i.e. remote end has closed connection) tcp_connect_sequence_number: .res 4 ;the seq number we will next send out tcp_connect_expected_ack_number: .res 4 ;what we expect to see in the next inbound ack tcp_connect_ack_number: .res 4 ;what we will next ack tcp_connect_last_received_seq_number: .res 4 ;the seq field in the last inbound packet for this connection tcp_connect_last_ack: .res 4 ;ack field in the last inbound packet for this connection tcp_connect_local_port: .res 2 ; tcp_connect_remote_port: .res 2 tcp_connect_ip: .res 4 ;ip address of remote server to connect to tcp_timer: .res 1 tcp_loop_count: .res 1 tcp_packet_sent_count: .res 1 .code ; initialize tcp ;called automatically by ip_init if "ip.s" was compiled with -DTCP ; inputs: none ; outputs: none tcp_init: rts jmp_to_callback: jmp (tcp_callback) ;listen for an inbound tcp connection ;this is a 'blocking' call, i.e. it will not return until a connection has been made ;inputs: ; AX: destination port (2 bytes) ; tcp_callback: vector to call when data arrives on this connection ;outputs: ; carry flag is set if an error occured, clear otherwise tcp_listen: stax tcp_listen_port lda #tcp_cxn_state_listening sta tcp_state lda #0 ;reset the "packet sent" counter sta tcp_packet_sent_count sta tcp_fin_sent ;set the low word of seq number to $0000, high word to something random sta tcp_connect_sequence_number sta tcp_connect_sequence_number+1 jsr ip65_random_word stax tcp_connect_sequence_number+2 jsr set_expected_ack; ;due to various ugly hacks, the 'expected ack' value is now what is put into the 'SEQ' field in outbound packets @listen_loop: jsr ip65_process jsr check_for_abort_key bcc @no_abort lda #KPR_ERROR_ABORTED_BY_USER sta ip65_error rts @no_abort: lda #tcp_cxn_state_listening cmp tcp_state beq @listen_loop jmp tcp_connection_established rts ;make outbound tcp connection ;inputs: ; tcp_connect_ip: destination ip address (4 bytes) ; AX: destination port (2 bytes) ; tcp_callback: vector to call when data arrives on this connection ;outputs: ; carry flag is set if an error occured, clear otherwise tcp_connect: stax tcp_connect_remote_port jsr ip65_random_word stax tcp_connect_local_port lda #tcp_cxn_state_syn_sent sta tcp_state lda #0 ;reset the "packet sent" counter sta tcp_packet_sent_count sta tcp_fin_sent ;set the low word of seq number to $0000, high word to something random sta tcp_connect_sequence_number sta tcp_connect_sequence_number+1 jsr ip65_random_word stax tcp_connect_sequence_number+2 @tcp_polling_loop: ;create a SYN packet lda #tcp_flag_SYN sta tcp_flags lda #0 sta tcp_data_len sta tcp_data_len+1 ldx #3 ; : lda tcp_connect_ip,x sta tcp_remote_ip,x lda tcp_connect_sequence_number,x sta tcp_sequence_number,x dex bpl :- ldax tcp_connect_local_port stax tcp_local_port ldax tcp_connect_remote_port stax tcp_remote_port jsr tcp_send_packet lda tcp_packet_sent_count adc #1 sta tcp_loop_count ;we wait a bit longer between each resend @outer_delay_loop: jsr timer_read stx tcp_timer ;we only care about the high byte @inner_delay_loop: jsr ip65_process jsr check_for_abort_key bcc @no_abort lda #KPR_ERROR_ABORTED_BY_USER sta ip65_error rts @no_abort: lda tcp_state cmp #tcp_cxn_state_syn_sent bne @got_a_response jsr timer_read cpx tcp_timer ;this will tick over after about 1/4 of a second beq @inner_delay_loop dec tcp_loop_count bne @outer_delay_loop inc tcp_packet_sent_count lda tcp_packet_sent_count cmp #MAX_TCP_PACKETS_SENT-1 bpl @too_many_messages_sent jmp @tcp_polling_loop @too_many_messages_sent: @failed: lda #tcp_cxn_state_closed sta tcp_state lda #KPR_ERROR_TIMEOUT_ON_RECEIVE sta ip65_error sec ;signal an error rts @got_a_response: lda tcp_state cmp #tcp_cxn_state_closed bne @was_accepted sec ;if we got here, then the other side sent a RST or FIN, so signal an error to the caller rts @was_accepted: tcp_connection_established: ;inc the sequence number to cover the SYN we have sent ldax #tcp_connect_sequence_number stax acc32 ldax #$01 jsr add_16_32 set_expected_ack: ;set the expected ack number with current seq number ldx #3 ; : lda tcp_connect_sequence_number,x sta tcp_connect_expected_ack_number,x dex bpl :- clc rts tcp_close: ;close the current connection ;inputs: ; none ;outputs: ; carry flag is set if an error occured, clear otherwise lda tcp_state cmp #tcp_cxn_state_established beq :+ @connection_closed: lda #tcp_cxn_state_closed sta tcp_state clc rts : ;increment the expected sequence number for the SYN we are about to send ldax #tcp_connect_expected_ack_number stax acc32 ldax #1 sta tcp_fin_sent jsr add_16_32 @send_fin_loop: lda #tcp_flag_FIN+tcp_flag_ACK sta tcp_flags ldax #0 stax tcp_data_len ldx #3 ; : lda tcp_connect_ip,x sta tcp_remote_ip,x lda tcp_connect_ack_number,x sta tcp_ack_number,x lda tcp_connect_sequence_number,x sta tcp_sequence_number,x dex bpl :- ldax tcp_connect_local_port stax tcp_local_port ldax tcp_connect_remote_port stax tcp_remote_port jsr tcp_send_packet lda tcp_packet_sent_count adc #1 sta tcp_loop_count ;we wait a bit longer between each resend @outer_delay_loop: jsr timer_read stx tcp_timer ;we only care about the high byte @inner_delay_loop: jsr ip65_process lda tcp_state cmp #tcp_cxn_state_established bne @connection_closed jsr timer_read cpx tcp_timer ;this will tick over after about 1/4 of a second beq @inner_delay_loop dec tcp_loop_count bne @outer_delay_loop inc tcp_packet_sent_count lda tcp_packet_sent_count cmp #MAX_TCP_PACKETS_SENT-1 bpl @too_many_messages_sent jmp @send_fin_loop @too_many_messages_sent: @failed: lda #tcp_cxn_state_closed sta tcp_state lda #KPR_ERROR_TIMEOUT_ON_RECEIVE sta ip65_error sec ;signal an error rts ;send a string over the current tcp connection ;inputs: ; tcp connection should already be opened ; AX: pointer to buffer - data up to (but not including) ; the first nul byte will be sent. max of 255 bytes will be sent. ;outputs: ; carry flag is set if an error occured, clear otherwise tcp_send_string: stax tcp_send_data_ptr stax copy_src lda #0 tay sta tcp_send_data_len sta tcp_send_data_len+1 lda (copy_src),y bne @find_end_of_string rts ; if the string is empty, don't send anything! @find_end_of_string: lda (copy_src),y beq @done inc tcp_send_data_len iny bne @find_end_of_string @done: ldax tcp_send_data_ptr ;now we can fall through into tcp_send ;send tcp data ;inputs: ; tcp connection should already be opened ; tcp_send_data_len: length of data to send (exclusive of any headers) ; AX: pointer to buffer containing data to be sent ;outputs: ; carry flag is set if an error occured, clear otherwise tcp_send: stax tcp_send_data_ptr lda tcp_state cmp #tcp_cxn_state_established beq @connection_established lda #KPR_ERROR_CONNECTION_CLOSED sta ip65_error sec rts lda #0 ;reset the "packet sent" counter sta tcp_packet_sent_count @connection_established: ;increment the expected sequence number ldax #tcp_connect_expected_ack_number stax acc32 ldax tcp_send_data_len jsr add_16_32 @tcp_polling_loop: ;create a data packet lda #tcp_flag_ACK+tcp_flag_PSH sta tcp_flags ldax tcp_send_data_len stax tcp_data_len ldax tcp_send_data_ptr stax tcp_data_ptr ldx #3 ; : lda tcp_connect_ip,x sta tcp_remote_ip,x lda tcp_connect_sequence_number,x sta tcp_sequence_number,x dex bpl :- ldax tcp_connect_local_port stax tcp_local_port ldax tcp_connect_remote_port stax tcp_remote_port jsr tcp_send_packet lda tcp_packet_sent_count adc #1 sta tcp_loop_count ;we wait a bit longer between each resend @outer_delay_loop: jsr timer_read stx tcp_timer ;we only care about the high byte @inner_delay_loop: jsr ip65_process jsr check_for_abort_key bcc @no_abort lda #KPR_ERROR_ABORTED_BY_USER sta ip65_error lda #tcp_cxn_state_closed sta tcp_state rts @no_abort: ldax #tcp_connect_last_ack stax acc32 ldax #tcp_connect_expected_ack_number stax op32 jsr cmp_32_32 beq @got_ack jsr timer_read cpx tcp_timer ;this will tick over after about 1/4 of a second beq @inner_delay_loop dec tcp_loop_count bne @outer_delay_loop inc tcp_packet_sent_count lda tcp_packet_sent_count cmp #MAX_TCP_PACKETS_SENT-1 bpl @too_many_messages_sent jmp @tcp_polling_loop @too_many_messages_sent: @failed: lda #tcp_cxn_state_closed sta tcp_state lda #KPR_ERROR_TIMEOUT_ON_RECEIVE sta ip65_error sec ;signal an error rts @got_ack: ;finished - now we need to advance the sequence number for the data we just sent ldax #tcp_connect_sequence_number stax acc32 ldax tcp_send_data_len jsr add_16_32 clc rts ;send a single tcp packet ;inputs: ; tcp_remote_ip: IP address of destination server ; tcp_remote_port: destination tcp port ; tcp_local_port: source tcp port ; tcp_flags: 6 bit flags ; tcp_data_ptr: pointer to data to include in this packet ; tcp_data_len: length of data pointed at by tcp_data_ptr ;outputs: ; carry flag is set if an error occured, clear otherwise tcp_send_packet: ldax tcp_data_ptr stax copy_src ; copy data to output buffer ldax #tcp_outp + tcp_data stax copy_dest ldax tcp_data_len jsr copymem ldx #3 ; copy virtual header addresses : lda tcp_remote_ip,x sta tcp_vh + tcp_vh_dest,x ; set virtual header destination lda cfg_ip,x sta tcp_vh + tcp_vh_src,x ; set virtual header source dex bpl :- lda tcp_local_port ; copy source port sta tcp_outp + tcp_src_port + 1 lda tcp_local_port + 1 sta tcp_outp + tcp_src_port lda tcp_remote_port ; copy destination port sta tcp_outp + tcp_dest_port + 1 lda tcp_remote_port + 1 sta tcp_outp + tcp_dest_port ldx #3 ; copy sequence and ack (if ACK flag set) numbers (in reverse order) ldy #0 : lda tcp_sequence_number,x sta tcp_outp + tcp_seq,y lda #tcp_flag_ACK bit tcp_flags bne @ack_set lda #0 beq @sta_ack @ack_set: lda tcp_ack_number,x @sta_ack: sta tcp_outp + tcp_ack,y iny dex bpl :- lda #$50 ;4 bit header length in 32bit words + 4 bits of zero sta tcp_outp+tcp_header_length lda tcp_flags sta tcp_outp+tcp_flags_field lda #ip_proto_tcp sta tcp_vh + tcp_vh_proto ldax #$0010 ;$1000 in network byte order stax tcp_outp+tcp_window_size lda #0 ; clear checksum sta tcp_outp + tcp_checksum sta tcp_outp + tcp_checksum + 1 sta tcp_vh + tcp_vh_zero ; clear virtual header zero byte ldax #tcp_vh ; checksum pointer to virtual header stax ip_cksum_ptr lda tcp_data_len ; copy length + 20 clc adc #20 sta tcp_vh + tcp_vh_len + 1 ; lsb for virtual header tay lda tcp_data_len + 1 adc #0 sta tcp_vh + tcp_vh_len ; msb for virtual header tax ; length to A/X tya clc ; add 12 bytes for virtual header adc #12 bcc :+ inx : jsr ip_calc_cksum ; calculate checksum stax tcp_outp + tcp_checksum ldx #3 ; copy addresses : lda tcp_remote_ip,x sta ip_outp + ip_dest,x ; set ip destination address dex bpl :- jsr ip_create_packet ; create ip packet template lda tcp_data_len ; ip len = tcp data length +20 byte ip header + 20 byte tcp header ldx tcp_data_len +1 clc adc #40 bcc :+ inx : sta ip_outp + ip_len + 1 ; set length stx ip_outp + ip_len ldax #$1234 ; set ID stax ip_outp + ip_id lda #ip_proto_tcp ; set protocol sta ip_outp + ip_proto jmp ip_send ; send packet, sec on error check_current_connection: ;see if the ip packet we just got is for a valid (non-closed) tcp connection ;inputs: ; eth_inp: should contain an ethernet frame encapsulating an inbound tcp packet ;outputs: ; carry flag clear if inbound tcp packet part of existing connection lda tcp_state cmp #tcp_cxn_state_closed bne @connection_not_closed sec rts @connection_not_closed: ldax #ip_inp+ip_src stax acc32 ldax #tcp_connect_ip stax op32 jsr cmp_32_32 beq @remote_ip_matches sec rts @remote_ip_matches: ldax tcp_inp+tcp_src_port stax acc16 lda tcp_connect_remote_port+1 ;this value in reverse byte order to how it is presented in the TCP header ldx tcp_connect_remote_port jsr cmp_16_16 beq @remote_port_matches sec rts @remote_port_matches: ldax tcp_inp+tcp_dest_port stax acc16 lda tcp_connect_local_port+1 ;this value in reverse byte order to how it is presented in the TCP header ldx tcp_connect_local_port jsr cmp_16_16 beq @local_port_matches sec rts @local_port_matches: clc rts ;process incoming tcp packet ;called automatically by ip_process if "ip.s" was compiled with -DTCP ;inputs: ; eth_inp: should contain an ethernet frame encapsulating an inbound tcp packet ;outputs: ; none but if connection was found, an outbound message may be created, overwriting eth_outp ; also tcp_state and other tcp variables may be modified tcp_process: lda #tcp_flag_RST bit tcp_inp+tcp_flags_field beq @not_reset jsr check_current_connection bcs @not_current_connection_on_rst ;for some reason, search.twitter.com is sending RSTs with ID=$1234 (i.e. echoing the inbound ID) ;but then keeps the connection open and ends up sending the file. ;so lets ignore a reset with ID=$1234 lda ip_inp+ip_id cmp #$34 bne @not_invalid_reset lda ip_inp+ip_id+1 cmp #$12 bne @not_invalid_reset jmp @send_ack @not_invalid_reset: ;connection has been reset so mark it as closed lda #tcp_cxn_state_closed sta tcp_state lda #KPR_ERROR_CONNECTION_RESET_BY_PEER sta ip65_error lda #$ff sta tcp_inbound_data_length sta tcp_inbound_data_length+1 jsr jmp_to_callback ;let the caller see the connection has closed @not_current_connection_on_rst: ;if we get a reset for a closed or nonexistent connection, then ignore it rts @not_reset: lda tcp_inp+tcp_flags_field cmp #tcp_flag_SYN+tcp_flag_ACK bne @not_syn_ack ;it's a SYN/ACK jsr check_current_connection bcc @current_connection_on_syn_ack ;if we get a SYN/ACK for something that aint the connection we're expecting, ;terminate with extreme prejudice jmp @send_rst @current_connection_on_syn_ack: lda tcp_state cmp #tcp_cxn_state_syn_sent bne @not_expecting_syn_ack ;this IS the syn/ack we are waiting for :-) ldx #3 ; copy sequence number to ack (in reverse order) ldy #0 : lda tcp_inp + tcp_seq,y sta tcp_connect_ack_number,x iny dex bpl :- ldax #tcp_connect_ack_number stax acc32 ldax #$0001 ; jsr add_16_32 ;increment the ACK counter by 1, for the SYN we just received lda #tcp_cxn_state_established sta tcp_state @not_expecting_syn_ack: ;we get a SYN/ACK for the current connection, ;but we're not expecting it, it's probably ;a retransmist - just ACK it jmp @send_ack @not_syn_ack: ;is it an ACK - alone or with PSH/URGENT but not a SYN/ACK? lda #tcp_flag_ACK bit tcp_inp+tcp_flags_field bne @ack jmp @not_ack @ack: ;is this the current connection? jsr check_current_connection bcc @current_connection_on_ack ;if we get an ACK for something that is not the current connection ;we should send a RST jmp @send_rst @current_connection_on_ack: ;if it's an ACK, then record the last ACK (reorder the bytes in the process) ldx #3 ; copy seq & ack fields (in reverse order) ldy #0 : lda tcp_inp + tcp_ack,y sta tcp_connect_last_ack,x lda tcp_inp + tcp_seq,y sta tcp_connect_last_received_seq_number,x iny dex bpl :- ;was this the next sequence number we're waiting for? ldax #tcp_connect_ack_number stax acc32 ldax #tcp_connect_last_received_seq_number stax op32 jsr cmp_32_32 bne @not_expected_seq_number ;what is the size of data in this packet? lda ip_inp+ip_len+1 ;payload length (lo byte) sta acc16 lda ip_inp+ip_len ;payload length (hi byte) sta acc16+1 lda tcp_inp+tcp_header_length ;high 4 bits is header length in 32 bit words lsr ; A=A/2 lsr ; A=A/2 clc ; A now equal to tcp header length in bytes adc #20 ;add 20 bytes for IP header. this gives length of IP +TCP headers ldx #0 sta tcp_header_length jsr sub_16_16 ;acc16 now contains the length of data in this TCP packet lda acc16 sta tcp_inbound_data_length lda acc16+1 sta tcp_inbound_data_length+1 bne @not_empty_packet lda acc16 bne @not_empty_packet jmp @empty_packet @not_empty_packet: ;calculate ptr to tcp data clc lda tcp_header_length adc #ip_inp adc #0 sta tcp_inbound_data_ptr+1 ; do a callback jsr jmp_to_callback ; move ack ptr along ldax #tcp_connect_ack_number stax acc32 ldax tcp_inbound_data_length jsr add_16_32 @not_expected_seq_number: ;send an ACK with the sequence number we expect ;send the ACK for any data in this packet, then return to check for FIN flag jsr @send_ack @not_ack: @empty_packet: ;is it a FIN? lda #tcp_flag_FIN bit tcp_inp+tcp_flags_field bne @fin jmp @not_fin @fin: ;is this the current connection? jsr check_current_connection bcc :+ jmp @send_rst ;reset if not current connection : ldx #3 ; copy seq field (in reverse order) ldy #0 : lda tcp_inp + tcp_seq,y sta tcp_connect_last_received_seq_number,x iny dex bpl :- ;was this the next sequence number we're waiting for? ldax #tcp_connect_ack_number stax acc32 ldax #tcp_connect_last_received_seq_number stax op32 jsr cmp_32_32 beq :+ rts ;bail if not expected sequence number : ;set the length to $ffff lda #$ff sta tcp_inbound_data_length sta tcp_inbound_data_length+1 jsr jmp_to_callback ;let the caller see the connection has closed lda #tcp_cxn_state_closed sta tcp_state ;send a FIN/ACK ; move ack ptr along for the inbound FIN ldax #tcp_connect_ack_number stax acc32 ldax #$01 sta tcp_fin_sent jsr add_16_32 ;if we've already sent a FIN then just send back an ACK lda tcp_fin_sent beq @send_fin_ack ;if we get here, we've sent a FIN, and just received an inbound FIN. ;when we sent the fin, we didn't update the sequence number, since ;we want to use the old sequence on every resend of that FIN ;now that our fin has been ACKed, we need to inc the sequence number ;and then send another ACK. ldax #tcp_connect_sequence_number stax acc32 ldax #$0001 ; jsr add_16_32 ;increment the SEQ counter by 1, for the FIN we have been sending lda #tcp_flag_ACK jmp @send_packet @send_fin_ack: lda #tcp_flag_FIN+tcp_flag_ACK jmp @send_packet @not_fin: lda tcp_inp+tcp_flags_field cmp #tcp_flag_SYN beq @syn jmp @not_syn @syn: ;is this the port we are listening on? lda tcp_inp+tcp_dest_port+1 cmp tcp_listen_port bne @decline_syn_with_reset lda tcp_inp+tcp_dest_port cmp tcp_listen_port+1 bne @decline_syn_with_reset ;it's the right port - are we actually waiting for a connecting? lda #tcp_cxn_state_listening cmp tcp_state beq @this_is_connection_we_are_waiting_for ;is this the current connection? that would mean our ACK got lost, so resend jsr check_current_connection bcc @this_is_connection_we_are_waiting_for rts ;if we've currently got a connection open, then ignore any new requests ;the sender will timeout and resend the SYN, by which time we may be ;ready to accept it again. @this_is_connection_we_are_waiting_for: ; copy sequence number to ack (in reverse order) and remote IP ldx #3 ldy #0 : lda tcp_inp + tcp_seq,y sta tcp_connect_ack_number,x lda ip_inp+ip_src,x sta tcp_connect_ip,x iny dex bpl :- ;copy ports ldax tcp_listen_port stax tcp_connect_local_port lda tcp_inp+tcp_src_port+1 sta tcp_connect_remote_port lda tcp_inp+tcp_src_port sta tcp_connect_remote_port+1 lda #tcp_cxn_state_established sta tcp_state ldax #tcp_connect_ack_number stax acc32 ldax #$0001 ; jsr add_16_32 ;increment the ACK counter by 1, for the SYN we just received lda #tcp_flag_SYN+tcp_flag_ACK jmp @send_packet @decline_syn_with_reset: ;create a RST packet ldx #3 ; copy sequence number to ack (in reverse order) ldy #0 : lda tcp_inp + tcp_seq,y sta tcp_ack_number,x iny dex bpl :- ldax #tcp_ack_number stax acc32 ldax #$0001 ; jsr add_16_32 ;increment the ACK counter by 1, for the SYN we just received @send_rst: lda #tcp_flag_RST+tcp_flag_ACK sta tcp_flags ldax #0 stax tcp_data_len ldx #3 ; : lda ip_inp+ip_src,x sta tcp_remote_ip,x dex bpl :- ;copy src/dest ports in inverted byte order lda tcp_inp+tcp_src_port sta tcp_remote_port+1 lda tcp_inp+tcp_src_port+1 sta tcp_remote_port lda tcp_inp+tcp_dest_port sta tcp_local_port+1 lda tcp_inp+tcp_dest_port+1 sta tcp_local_port jsr tcp_send_packet rts @not_syn: rts @send_ack: ;create an ACK packet lda #tcp_flag_ACK @send_packet: sta tcp_flags ldax #0 stax tcp_data_len ldx #3 ; : lda tcp_connect_ip,x sta tcp_remote_ip,x lda tcp_connect_ack_number,x sta tcp_ack_number,x ;if we have just sent a packet out, we may not yet have updated tcp_connect_sequence_number yet ;so use current value of tcp_connect_expected_ack_number as outbound sequence number instead lda tcp_connect_expected_ack_number,x sta tcp_sequence_number,x dex bpl :- ldax tcp_connect_local_port stax tcp_local_port ldax tcp_connect_remote_port stax tcp_remote_port jmp tcp_send_packet ;send an empty ACK packet on the current connection ;inputs: ; none ;outputs: ; carry flag is set if an error occured, clear otherwise tcp_send_keep_alive=@send_ack ;-- LICENSE FOR tcp.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 --