; 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 "zeropage.inc" .include "../inc/common.inc" .include "../inc/error.inc" .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 .bss 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 #IP65_ERROR_ABORTED_BY_USER sta ip65_error rts @no_abort: lda #tcp_cxn_state_listening cmp tcp_state beq @listen_loop 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 :- 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 #IP65_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 #IP65_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: 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 #IP65_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. ; outputs: ; carry flag is set if an error occured, clear otherwise tcp_send_string: stax tcp_send_data_ptr stax ptr1 lda #0 tay sta tcp_send_data_len sta tcp_send_data_len+1 lda (ptr1),y bne @find_end_of_string rts ; if the string is empty, don't send anything! @find_end_of_string: lda (ptr1),y beq @done inc tcp_send_data_len iny bne @find_end_of_string inc tcp_send_data_len+1 inc ptr1+1 jmp @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 (up to 1460 bytes) ; 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 #IP65_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 #IP65_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 #IP65_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 #$b405 ; $05b4 (1460) 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 ; 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 check_current_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 #IP65_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 jsr tcp_connection_established 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 ; send an ACK with the sequence number we expect @not_expected_seq_number: ; 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 @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 --