ip65 technical reference

File : ip65/telnet.s

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

functions

functiondescription
telnet_connect
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

variables

variabledescriptionsize (bytes)
telnet_ipip address of remote server 4
telnet_portport number to connect to 2
telnet_use_native_charset 0 means all data is translated to/from NVT ASCII 1

implementation

;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, ,