diff --git a/client/examples/dumb_telnet.asm b/client/examples/dumb_telnet.asm index aee7a5d..d32b2e5 100644 --- a/client/examples/dumb_telnet.asm +++ b/client/examples/dumb_telnet.asm @@ -93,7 +93,7 @@ found_nb65_signature nb65call #NB65_INPUT_HOSTNAME bcc .host_entered ;if no host entered, then bail. - jmp reset_after_keypress + rts .host_entered stax nb65_param_buffer print_cr @@ -133,16 +133,30 @@ found_nb65_signature beq .petscii_mode jmp .char_mode_input .ascii_mode + lda #14 + jsr print_a ;switch to lower case + lda #0 jmp .character_mode_set .petscii_mode + lda #142 + jsr print_a ;switch to upper case lda #1 .character_mode_set sta character_mode - + lda #147 ; 'CLR/HOME' + jsr print_a ldaxi #tcp_callback stax nb65_param_buffer+NB65_TCP_CALLBACK print #connecting + lda character_mode + beq .a_mode + print #petscii + jmp .c_mode +.a_mode + print #ascii +.c_mode + print #mode ldaxi #nb65_param_buffer nb65call #NB65_TCP_CONNECT bcc .connect_ok @@ -165,25 +179,41 @@ found_nb65_signature ;is there anything in the input buffer? lda $c6 ;NDX - chars in keyboard buffer beq .main_polling_loop - tay - dey - ldx #0 - stax nb65_param_buffer+NB65_TCP_PAYLOAD_LENGTH - ldaxi #output_buffer - stax nb65_param_buffer+NB65_TCP_PAYLOAD_POINTER -.copy_char_from_KEYD - lda $277,y ;read direct from keyboard buffer - tax + lda #0 + sta nb65_param_buffer+NB65_TCP_PAYLOAD_LENGTH + sta nb65_param_buffer+NB65_TCP_PAYLOAD_LENGTH+1 +.get_next_char + jsr $ffe4 ;getkey - 0 means no input + tax + beq .no_more_input + cmp #$03 ;RUN/STOP + bne .not_runstop + lda #0 + sta $cb ;overwrite "current key pressed" else it's seen by the tcp stack and the close aborts + + print #closing_connection + nb65call #NB65_TCP_CLOSE_CONNECTION + bcs .error_on_disconnect + print #disconnected + jmp .get_hostname +.error_on_disconnect + jsr print_errorcode + print_cr + jmp .get_hostname +.not_runstop lda character_mode bne .no_conversion_required lda petscii_to_ascii_table,x tax -.no_conversion_required +.no_conversion_required txa + ldy nb65_param_buffer+NB65_TCP_PAYLOAD_LENGTH sta output_buffer,y - dey - bne .copy_char_from_KEYD - sty $c6 ;set length of keyboard buffer back to 0 + inc nb65_param_buffer+NB65_TCP_PAYLOAD_LENGTH + jmp .get_next_char +.no_more_input + ldaxi #output_buffer + stax nb65_param_buffer+NB65_TCP_PAYLOAD_POINTER ldaxi #nb65_param_buffer nb65call #NB65_SEND_TCP_PACKET bcs .error_on_send @@ -209,18 +239,33 @@ tcp_callback ldax nb65_param_buffer+NB65_PAYLOAD_POINTER stax temp_ptr - ldx nb65_param_buffer+NB65_PAYLOAD_LENGTH ;assumes length of inbound data is < 255 + lda nb65_param_buffer+NB65_PAYLOAD_LENGTH ;assumes length of inbound data is < 255 + sta buffer_length + dec buffer_length ldy #0 .next_byte +; tya +; pha +; lda (temp_ptr),y +; nb65call #NB65_PRINT_HEX +; pla +; tay lda (temp_ptr),y + tax + lda character_mode + bne .no_conversion_req + lda ascii_to_petscii_table,x + tax +.no_conversion_req + tya + pha + txa jsr print_a + pla + tay iny - dex - bpl .next_byte - - print_cr - - + dec buffer_length + bpl .next_byte rts ;look for NB65 signature at location pointed at by AX @@ -280,11 +325,16 @@ nb65_signature dc.b $4E,$42,$36,$35 ; "NB65" - API signature initializing dc.b "INITIALIZING ",13,0 error_code dc.b "ERROR CODE: $",0 resolving dc.b "RESOLVING ",0 -connecting dc.b "CONNECTING ",0 +closing_connection dc.b "CLOSING CONNECTION",13,0 +connecting dc.b "CONNECTING IN ",0 + +ascii dc.b "ASCII",0 +petscii dc.b "PETSCII",0 +mode dc.b " MODE",13,0 disconnected dc.b 13,"CONNECTION CLOSED",13,0 remote_host dc.b "REMOTE HOST - BLANK TO QUIT",13,": ",0 remote_port dc.b "REMOTE PORT - BLANK FOR TELNET DEFAULT",13,": ",0 -char_mode_prompt dc.b "CHARACTER MODE - A=ASCII, P=PETSCII",13,": ",0 +char_mode_prompt dc.b "CHARACTER MODE - A=ASCII, P=PETSCII",13,0 press_a_key_to_continue dc.b "PRESS A KEY TO CONTINUE",13,0 failed dc.b "FAILED ", 0 ok dc.b "OK ", 0 @@ -329,5 +379,8 @@ petscii_to_ascii_table ;variables connection_closed ds.b 1 character_mode ds.b 1 +buffer_offset: ds.b 1 nb65_param_buffer DS.B $20 +buffer_length: ds.b 2 + output_buffer: DS.B $100 diff --git a/client/inc/gopher.i b/client/inc/gopher.i index 3fd0d8b..b5287fa 100644 --- a/client/inc/gopher.i +++ b/client/inc/gopher.i @@ -32,7 +32,6 @@ .import ip65_error .import cls .import get_filtered_input - .import filter_text .import filter_dns .segment "IP65ZP" : zeropage @@ -86,7 +85,7 @@ RESOURCE_HISTORY_ENTRIES=8 resource_history: .res $100*RESOURCE_HISTORY_ENTRIES -input_buffer: +scratch_buffer: .res 16000 .code @@ -94,7 +93,7 @@ input_buffer: ;display whatever is in the buffer either as plain text or gopher text display_resource_in_buffer: - ldax #input_buffer + ldax #scratch_buffer stax get_next_byte+1 lda #0 @@ -449,7 +448,7 @@ show_history: ;load the 'current_resource' into the buffer load_resource_into_buffer: - ldax #input_buffer + ldax #scratch_buffer stax tcp_buffer_ptr ldax #resolving jsr print @@ -603,6 +602,7 @@ print_resource_description: prompt_for_gopher_resource: ldax #gopher_server jsr print + ldy #40 ldax #filter_dns jsr get_filtered_input bcs @no_server_entered diff --git a/client/ip65/function_dispatcher.s b/client/ip65/function_dispatcher.s index d300680..f8d6aa3 100644 --- a/client/ip65/function_dispatcher.s +++ b/client/ip65/function_dispatcher.s @@ -483,6 +483,14 @@ ip_configured: : + +.import tcp_close + cpy #NB65_TCP_CLOSE_CONNECTION + bne :+ + jmp tcp_close +: + + .import filter_dns .import get_filtered_input .import filter_number diff --git a/client/ip65/tcp.s b/client/ip65/tcp.s index 0632b10..f4716f1 100644 --- a/client/ip65/tcp.s +++ b/client/ip65/tcp.s @@ -23,6 +23,7 @@ MAX_TCP_PACKETS_SENT=8 ;timeout after sending 8 messages will be about 7 sec .export tcp_connect_ip .export tcp_send_data_len .export tcp_send +.export tcp_close .export tcp_inbound_data_ptr .export tcp_inbound_data_length @@ -121,7 +122,7 @@ 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_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 @@ -169,7 +170,8 @@ tcp_connect: 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 @@ -224,7 +226,6 @@ tcp_connect: dec tcp_loop_count bne @outer_delay_loop -@break_polling_loop: inc tcp_packet_sent_count lda tcp_packet_sent_count @@ -257,6 +258,80 @@ tcp_connect: clc rts +tcp_close: +;close the current connection +;inputs: +; none +;outputs: +; carry flag is set if an error occured, clear otherwise + + ;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 +@connection_closed: + clc + rts +@too_many_messages_sent: +@failed: + lda #tcp_cxn_state_closed + sta tcp_state + lda #NB65_ERROR_TIMEOUT_ON_RECEIVE + sta ip65_error + sec ;signal an error + rts + + +tcp_send: ;send tcp data ;inputs: ; tcp connection should already be opened @@ -264,8 +339,8 @@ tcp_connect: ; 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 + + stax tcp_send_data_ptr lda tcp_state cmp #tcp_cxn_state_established beq @connection_established @@ -273,6 +348,8 @@ tcp_send: sta ip65_error sec rts + lda #0 ;reset the "packet sent" counter + sta tcp_packet_sent_count @connection_established: ;increment the expected sequence number @@ -337,7 +414,6 @@ tcp_send: dec tcp_loop_count bne @outer_delay_loop -@break_polling_loop: inc tcp_packet_sent_count lda tcp_packet_sent_count @@ -716,24 +792,41 @@ tcp_process: sta tcp_inbound_data_length+1 jsr jmp_to_callback ;let the caller see the connection has closed - ; move ack ptr along - ldax #tcp_connect_ack_number - stax acc32 - ldax #$01 - jsr add_16_32 lda #tcp_cxn_state_closed sta tcp_state ;send a FIN/ACK -; jsr @send_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 - ;send a FIN + ;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 - jsr @send_packet + jmp @send_packet - rts @not_fin: diff --git a/client/nb65/Makefile b/client/nb65/Makefile index 8422e36..0d6e2cc 100644 --- a/client/nb65/Makefile +++ b/client/nb65/Makefile @@ -27,10 +27,10 @@ all: nb65_std_cart.bin nb65_tcp_cart.bin nb65_c64_ram.o: nb65_c64.s $(INCFILES) $(AS) -DBANKSWITCH_SUPPORT=0 $(AFLAGS) -o $@ $< -nb65_std_cart.o: nb65_c64.s $(INCFILES) +nb65_std_cart.o: nb65_c64.s $(INCFILES) $(AS) -DBANKSWITCH_SUPPORT=1 $(AFLAGS) -o $@ $< -nb65_tcp_cart.o: nb65_c64.s $(INCFILES) +nb65_tcp_cart.o: nb65_c64.s $(INCFILES) ../inc/gopher.i ../inc/telnet.i $(AS) -DBANKSWITCH_SUPPORT=3 $(AFLAGS) -o $@ $< nb65_rrnet.o: nb65_c64.s $(INCFILES) diff --git a/client/nb65/nb65_c64.s b/client/nb65/nb65_c64.s index 4fa1e5e..6a1b95d 100644 --- a/client/nb65/nb65_c64.s +++ b/client/nb65/nb65_c64.s @@ -47,6 +47,7 @@ .if (BANKSWITCH_SUPPORT=$03) .include "../inc/char_conv.i" .include "../inc/gopher.i" + .include "../inc/telnet.i" .endif .import cls .import beep @@ -215,6 +216,7 @@ main_menu: jsr print_cr @get_key: + jsr ip65_process jsr get_key cmp #KEYCODE_F1 bne @not_tftp @@ -224,11 +226,7 @@ main_menu: cmp #KEYCODE_F3 .if (BANKSWITCH_SUPPORT=$03) bne @not_f3 - jsr cls - lda #14 - jsr print_a ;switch to lower case - jsr prompt_for_gopher_resource ;only returns if no server was entered. - jmp exit_gopher + jmp net_apps_menu .else beq @exit_to_basic .endif @@ -257,7 +255,8 @@ main_menu: jsr print jsr print_ip_config jsr print_cr -@get_key_config_menu: +@get_key_config_menu: + jsr ip65_process jsr get_key cmp #KEYCODE_F1 bne @not_ip @@ -267,6 +266,7 @@ main_menu: jsr print jsr print_cr ldax #filter_ip + ldy #20 jsr get_filtered_input bcs @no_ip_address_entered jsr parse_dotted_quad @@ -291,6 +291,7 @@ main_menu: jsr print jsr print_cr ldax #filter_ip + ldy #20 jsr get_filtered_input bcs @no_netmask_entered jsr parse_dotted_quad @@ -315,6 +316,7 @@ main_menu: jsr print jsr print_cr ldax #filter_ip + ldy #20 jsr get_filtered_input bcs @no_gateway_entered jsr parse_dotted_quad @@ -341,6 +343,7 @@ main_menu: jsr print jsr print_cr ldax #filter_ip + ldy #20 jsr get_filtered_input bcs @no_dns_server_entered jsr parse_dotted_quad @@ -366,6 +369,7 @@ main_menu: jsr print jsr print_cr ldax #filter_dns + ldy #40 jsr get_filtered_input bcs @no_server_entered stax nb65_param_buffer @@ -533,6 +537,39 @@ exit_cart_via_ax: stx call_downloaded_prg+2 jmp exit_cart +.if (BANKSWITCH_SUPPORT=$03) +net_apps_menu: + jsr cls + ldax #netboot65_msg + jsr print + ldax #net_apps_menu_msg + jsr print +@get_key: + jsr ip65_process + jsr get_key + cmp #KEYCODE_F1 + bne @not_telnet + jsr cls + lda #14 + jsr print_a ;switch to lower case + jmp telnet_main_entry +@not_telnet: + cmp #KEYCODE_F3 + bne @not_gopher + jsr cls + lda #14 + jsr print_a ;switch to lower case + jsr prompt_for_gopher_resource ;only returns if no server was entered. + jmp exit_gopher +@not_gopher: + cmp #KEYCODE_F7 + bne @not_main + jmp main_menu +@not_main: + jmp @get_key + +.endif + print_errorcode: ldax #error_code jsr print @@ -594,6 +631,7 @@ cfg_get_configuration_ptr: rts .if (BANKSWITCH_SUPPORT=$03) +exit_telnet: exit_gopher: lda #142 jsr print_a ;switch to upper case @@ -602,14 +640,14 @@ exit_gopher: .rodata netboot65_msg: -.byte 13," NETBOOT65 - C64 CLIENT VERSION " +.byte 13," NETBOOT65 - C64 CLIENT VERSION " .include "nb65_version.i" .byte 13,0 main_menu_msg: .byte 13," MAIN MENU",13,13 .byte "F1: TFTP BOOT" .if (BANKSWITCH_SUPPORT=$03) -.byte " F3: GOPHER" +.byte " F3: NET APPS" .else .byte " F3: BASIC" .endif @@ -617,6 +655,7 @@ main_menu_msg: .byte "F5: ARP TABLE F7: CONFIG",13,13 .byte 0 + config_menu_msg: .byte 13," CONFIGURATION",13,13 .byte "F1: IP ADDRESS F2: NETMASK",13 @@ -625,6 +664,14 @@ config_menu_msg: .byte "F7: MAIN MENU",13,13 .byte 0 + +.if (BANKSWITCH_SUPPORT=$03) +net_apps_menu_msg: +.byte 13," NET APPS",13,13 +.byte "F1: TELNET F3: GOPHER",13 +.byte "F5: F7: MAIN MENU",13,13 +.byte 0 +.endif downloading_msg: .asciiz "DOWNLOADING " getting_dir_listing_msg: .byte "FETCHING DIRECTORY",13,0