;minimal telnet implementation (dumb terminal emulation only) ;to use: ;set the following variables - telnet_use_native_charset,telnet_port,telnet_ip ;then call telnet_connect ;you must also define (and export) these function ; telnet_menu - called whenever the F1 key is pressed. ; telnet_on_connection - called after succesful connection .include "../inc/common.i" .import tcp_connect .import tcp_callback .import tcp_connect_ip .import tcp_listen .importzp KEYCODE_F1 .import tcp_inbound_data_ptr .import tcp_inbound_data_length .import tcp_send .import tcp_send_data_len .import tcp_close .import print_a .import print_cr .import vt100_init_terminal .import vt100_process_inbound_char .import vt100_transform_outbound_char .import tcp_send_keep_alive .import timer_read .import ip65_process .import get_key_if_available .import get_filtered_input .import check_for_abort_key .import ok_msg .import failed_msg .import print .import print_errorcode .import native_to_ascii .import ascii_to_native .export telnet_connect .export telnet_use_native_charset .export telnet_port .export telnet_ip .import telnet_menu .import telnet_on_connection .segment "IP65ZP" : zeropage ; pointer for moving through buffers buffer_ptr: .res 2 ; source pointer .code ;connect to a remote telnet server ;inputs: ;telnet_use_native_charset: set to 0 if remote server uses standard ASCII, 1 if remote server uses the 'native' charset (i.e. PETSCII) ;telnet_port: port number to connect to ;telnet_ip: ip address of remote server telnet_connect: lda telnet_use_native_charset bne :+ jsr vt100_init_terminal : ldax #telnet_callback stax tcp_callback ldx #3 @copy_dest_ip: lda telnet_ip,x sta tcp_connect_ip,x dex bpl @copy_dest_ip ldax telnet_port jsr tcp_connect bcc @connect_ok jsr print_cr ldax #failed_msg jsr print jsr print_cr jsr print_errorcode rts @connect_ok: jsr telnet_on_connection ldax #ok_msg jsr print jsr print_cr lda #0 sta connection_closed sta iac_response_buffer_length @main_polling_loop: jsr check_for_abort_key bcc @no_abort jsr tcp_close jmp @disconnected @no_abort: jsr timer_read txa adc #$20 ;32 x 1/4 = ~ 8seconds sta telnet_timeout @wait_for_keypress: jsr timer_read cpx telnet_timeout bne @no_timeout jsr tcp_send_keep_alive jmp @main_polling_loop @no_timeout: jsr ip65_process lda connection_closed beq @not_disconnected @disconnected: ldax #disconnected jsr print rts @not_disconnected: lda iac_response_buffer_length beq @no_iac_response ldx #0 stax tcp_send_data_len stx iac_response_buffer_length ldax #iac_response_buffer jsr tcp_send @no_iac_response: jsr get_key_if_available beq @wait_for_keypress cmp #KEYCODE_F1 bne @not_telnet_menu jsr telnet_menu jmp @main_polling_loop @not_telnet_menu: ldx #0 stx tcp_send_data_len stx tcp_send_data_len+1 ldx telnet_use_native_charset bne @no_conversion_required jsr vt100_transform_outbound_char sta temp_a tya bne :+ jmp @main_polling_loop ;Y=0 means nothing to send : cmp #2 beq :+ lda temp_a jmp @no_conversion_required : lda temp_a stax buffer_ptr ldy #0 : lda (buffer_ptr),y beq @send_char sta scratch_buffer,y inc tcp_send_data_len iny bne :- jmp @send_char @no_conversion_required: ldy tcp_send_data_len sta scratch_buffer,y inc tcp_send_data_len @send_char: ldax #scratch_buffer jsr tcp_send bcs @error_on_send jmp @main_polling_loop @error_on_send: ldax #transmission_error jsr print jmp print_errorcode ;tcp callback - will be executed whenever data arrives on the TCP connection telnet_callback: lda tcp_inbound_data_length+1 cmp #$ff bne @not_eof lda #1 sta connection_closed rts @not_eof: ldax tcp_inbound_data_ptr stax buffer_ptr lda tcp_inbound_data_length sta buffer_length lda tcp_inbound_data_length+1 sta buffer_length+1 @next_byte: ldy #0 lda (buffer_ptr),y tax lda telnet_use_native_charset beq :+ jmp @no_conversion_req : ;if we get here, we are in ASCII 'char at a time' mode, so look for (and process) Telnet style IAC bytes lda telnet_state cmp #telnet_state_got_command bne :+ jmp @waiting_for_option : cmp #telnet_state_got_iac beq @waiting_for_command cmp #telnet_state_got_suboption beq @waiting_for_suboption_end ; we must be in 'normal' mode txa cmp #255 beq :+ jmp @not_iac : lda #telnet_state_got_iac sta telnet_state jmp @byte_processed @waiting_for_suboption_end: txa ldx iac_suboption_buffer_length sta iac_suboption_buffer,x inc iac_suboption_buffer_length cmp #$f0 ;SE - suboption end bne @exit_suboption lda #telnet_state_normal sta telnet_state lda iac_suboption_buffer cmp #$18 bne @not_terminal_type ldx #0 : lda terminal_type_response,x ldy iac_response_buffer_length inc iac_response_buffer_length sta iac_response_buffer,y inx txa cmp #terminal_type_response_length bne :- @not_terminal_type: @exit_suboption: jmp @byte_processed @waiting_for_command: txa sta telnet_command cmp #$fa ; SB - suboption begin beq @suboption cmp #$fb ;WILL beq @option cmp #$fc ;WONT beq @option cmp #$fd ; DO beq @option cmp #$fe ;DONT beq @option ;we got a command we don't understand - just ignore it lda #telnet_state_normal sta telnet_state jmp @byte_processed @suboption: lda #telnet_state_got_suboption sta telnet_state lda #0 sta iac_suboption_buffer_length jmp @byte_processed @option: lda #telnet_state_got_command sta telnet_state jmp @byte_processed @waiting_for_option: ;we have now got IAC, ,