mirror of
https://github.com/bobbimanners/emailler.git
synced 2024-09-01 05:29:18 +00:00
363 lines
7.8 KiB
ArmAsm
363 lines
7.8 KiB
ArmAsm
;minimal telnet implementation (dumb terminal emulation only)
|
|
;to use:
|
|
;set the following variables - telnet_local_echo, telnet_line_mode,telnet_use_native_charset,telnet_port,telnet_ip
|
|
;then call telnet_connect
|
|
;sensible combinations of telnet_local_echo, telnet_line_mode,telnet_use_native_charset are:
|
|
;for interacting with 'line at time' servers (smtp/pop3/http/gopher): telnet_local_echo=1, telnet_line_mode=1,telnet_use_native_charset=0
|
|
;for logging in to a normal telnet server: telnet_local_echo=0, telnet_line_mode=0,telnet_use_native_charset=0
|
|
;for logging in to a PETSCII BBS on a C64 : telnet_local_echo=0, telnet_line_mode=0,telnet_use_native_charset=1
|
|
|
|
|
|
.include "../inc/common.i"
|
|
|
|
.import tcp_connect
|
|
.import tcp_callback
|
|
.import tcp_connect_ip
|
|
.import tcp_listen
|
|
.importzp KEYCODE_ABORT
|
|
.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 ip65_process
|
|
.import get_key_ip65
|
|
.import get_filtered_input
|
|
.import ok_msg
|
|
.import failed_msg
|
|
.import print
|
|
.import print_errorcode
|
|
.import native_to_ascii
|
|
.import ascii_to_native
|
|
|
|
.export telnet_connect
|
|
.export telnet_local_echo
|
|
.export telnet_line_mode
|
|
.export telnet_use_native_charset
|
|
.export telnet_port
|
|
.export telnet_ip
|
|
|
|
.segment "IP65ZP" : zeropage
|
|
|
|
; pointer for moving through buffers
|
|
buffer_ptr: .res 2 ; source pointer
|
|
|
|
.code
|
|
telnet_connect:
|
|
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_errorcode
|
|
rts
|
|
@connect_ok:
|
|
ldax #ok_msg
|
|
jsr print
|
|
jsr print_cr
|
|
lda #0
|
|
sta connection_closed
|
|
@main_polling_loop:
|
|
jsr ip65_process
|
|
lda connection_closed
|
|
beq @not_disconnected
|
|
ldax #disconnected
|
|
jsr print
|
|
rts
|
|
@not_disconnected:
|
|
lda telnet_line_mode
|
|
beq @not_line_mode
|
|
|
|
ldy #40 ;max chars
|
|
ldax #$0000
|
|
jsr get_filtered_input
|
|
stax buffer_ptr
|
|
ldy #0
|
|
@copy_one_char:
|
|
lda (buffer_ptr),y
|
|
jsr native_to_ascii
|
|
beq @end_of_input_string
|
|
sta scratch_buffer,y
|
|
iny
|
|
bne @copy_one_char
|
|
@end_of_input_string:
|
|
lda #$0d
|
|
sta scratch_buffer,y
|
|
iny
|
|
lda #$0a
|
|
sta scratch_buffer,y
|
|
iny
|
|
sty tcp_send_data_len
|
|
lda #0
|
|
sta tcp_send_data_len+1
|
|
jsr print_cr
|
|
jmp @send_char
|
|
|
|
@not_line_mode:
|
|
|
|
jsr get_key_ip65
|
|
tax
|
|
; beq @send_char
|
|
cmp #KEYCODE_ABORT
|
|
bne @not_abort
|
|
|
|
ldax #closing_connection
|
|
jsr print
|
|
jsr tcp_close
|
|
bcs @error_on_disconnect
|
|
ldax #disconnected
|
|
jsr print
|
|
rts
|
|
@error_on_disconnect:
|
|
jsr print_errorcode
|
|
jsr print_cr
|
|
rts
|
|
@not_abort:
|
|
lda #0
|
|
sta tcp_send_data_len
|
|
sta tcp_send_data_len+1
|
|
|
|
lda telnet_use_native_charset
|
|
bne @no_conversion_required
|
|
txa
|
|
cmp #$0d
|
|
bne @not_cr
|
|
|
|
;if we get a CR in ascii mode, send CR/LF
|
|
ldy tcp_send_data_len
|
|
sta scratch_buffer,y
|
|
inc tcp_send_data_len
|
|
ldx #$0a
|
|
jmp @no_conversion_required
|
|
@not_cr:
|
|
txa
|
|
jsr native_to_ascii
|
|
tax
|
|
@no_conversion_required:
|
|
txa
|
|
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
|
|
jsr print_errorcode
|
|
rts
|
|
|
|
;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
|
|
|
|
;since we don't check the buffer length till the end of the loop, set 'buffer length' to be 1 less than the actual number of bytes
|
|
dec buffer_length
|
|
bpl :+
|
|
dec buffer_length+1
|
|
:
|
|
ldy #0
|
|
sty iac_response_buffer_length
|
|
@next_byte:
|
|
lda (buffer_ptr),y
|
|
tax
|
|
lda telnet_use_native_charset
|
|
beq :+
|
|
jmp @no_conversion_req
|
|
:
|
|
|
|
lda telnet_line_mode
|
|
beq :+
|
|
jmp@convert_to_native
|
|
:
|
|
;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
|
|
beq @waiting_for_option
|
|
cmp #telnet_state_got_iac
|
|
beq @waiting_for_command
|
|
; 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_command:
|
|
txa
|
|
sta telnet_command
|
|
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
|
|
@option:
|
|
lda #telnet_state_got_command
|
|
sta telnet_state
|
|
jmp @byte_processed
|
|
|
|
@waiting_for_option:
|
|
;we have now got IAC, <command>, <option>
|
|
txa
|
|
sta telnet_option
|
|
lda telnet_command
|
|
|
|
cmp #$fb
|
|
beq @iac_will
|
|
|
|
cmp #$fc
|
|
beq @iac_wont
|
|
|
|
;if we get here, then it's a "do" or "don't", both of which we should send a "wont" back for
|
|
;(since there are no "do" options we actually honour)
|
|
lda #$fc ;wont
|
|
@add_iac_response:
|
|
ldx iac_response_buffer_length
|
|
sta iac_response_buffer+1,x
|
|
lda #255
|
|
sta iac_response_buffer,x
|
|
lda telnet_option
|
|
sta iac_response_buffer+2,x
|
|
|
|
inc iac_response_buffer_length
|
|
inc iac_response_buffer_length
|
|
inc iac_response_buffer_length
|
|
|
|
lda #telnet_state_normal
|
|
sta telnet_state
|
|
jmp @byte_processed
|
|
@iac_will:
|
|
lda telnet_option
|
|
cmp #$01 ;ECHO
|
|
beq @will_echo
|
|
cmp #$03 ;DO SUPPRESS GA
|
|
beq @will_suppress_ga
|
|
|
|
@iac_wont:
|
|
lda #$fe ;dont
|
|
jmp @add_iac_response
|
|
|
|
@will_echo:
|
|
lda #0
|
|
sta telnet_local_echo
|
|
lda #$fd ;DO
|
|
jmp @add_iac_response
|
|
|
|
@will_suppress_ga:
|
|
lda #0
|
|
sta telnet_line_mode
|
|
lda #$fd ;DO
|
|
jmp @add_iac_response
|
|
|
|
@not_iac:
|
|
@convert_to_native:
|
|
txa
|
|
cmp #$0a ;suppress LF (since it probably follows a CR which will have done the LF as well)
|
|
beq @byte_processed
|
|
jsr ascii_to_native
|
|
tax
|
|
@no_conversion_req:
|
|
tya
|
|
pha
|
|
txa
|
|
; pha
|
|
; jsr print_hex
|
|
; pla
|
|
jsr print_a
|
|
pla
|
|
tay
|
|
@byte_processed:
|
|
iny
|
|
bne :+
|
|
inc buffer_ptr+1
|
|
:
|
|
dec buffer_length
|
|
lda #$ff
|
|
cmp buffer_length
|
|
beq :+
|
|
jmp @next_byte
|
|
:
|
|
|
|
dec buffer_length+1
|
|
bmi @finished
|
|
jmp @next_byte
|
|
@finished:
|
|
|
|
lda iac_response_buffer_length
|
|
beq @no_iac_response
|
|
ldx #0
|
|
stax tcp_send_data_len
|
|
ldax #iac_response_buffer
|
|
jsr tcp_send
|
|
@no_iac_response:
|
|
rts
|
|
|
|
;constants
|
|
closing_connection: .byte "CLOSING CONNECTION",13,0
|
|
disconnected: .byte 13,"CONNECTION CLOSED",13,0
|
|
transmission_error: .byte "ERROR WHILE SENDING ",0
|
|
|
|
;variables
|
|
.segment "APP_SCRATCH"
|
|
telnet_ip: .res 4 ;ip address of remote server
|
|
telnet_port: .res 2 ;port number to connect to
|
|
|
|
connection_closed: .res 1
|
|
telnet_use_native_charset: .res 1 ; 0 means all data is translated to/from NVT ASCII
|
|
buffer_offset: .res 1
|
|
telnet_local_echo: .res 1 ;0 should mean local echo is disabled - in fact at the moment we never do local echo except in 'line mode'
|
|
telnet_line_mode: .res 1 ;do characters get sent after each keypress, or can a line be created/edited and then sent only when return is pressed?
|
|
telnet_state: .res 1
|
|
telnet_command: .res 1
|
|
telnet_option: .res 1
|
|
|
|
telnet_state_normal = 0
|
|
telnet_state_got_iac = 1
|
|
telnet_state_got_command = 2
|
|
|
|
buffer_length: .res 2
|
|
|
|
iac_response_buffer: .res 64
|
|
iac_response_buffer_length: .res 1
|
|
scratch_buffer : .res 40 |