mirror of
https://github.com/bobbimanners/emailler.git
synced 2024-10-31 13:08:40 +00:00
4577c2ab19
So far the base address of the Ethernet chip was a general property of all Ethernet drivers. It served two purposes: 1. Allowing to use a single Ethernet driver for a certain Ethernet chip, no matter what machine was connected to the chip. 2. Allowing use an Ethernet card in all Apple II slots. However, we now use customized Ethernet drivers for the individual machines so 1.) isn't relevant anymore. In fact one wants to omit the overhead of a runtime-adjustable base address where it isn't needed. So only the Apple II slots are left. But this should rather be a driver-internal approach then. We should just hand the driver the slot number the user wants to use and have the driver do its thing. Independently from the aspect if the driver parameter is a base address or a slot number the parameter handling was changed too. For asm programs there was so far a specific init function to be called prior to the main init function if it was desired to chnage the parameter default. This was done to keep the main init function backward compatible. But now that the parameter (now the slot number) is only used on the Apple II anyhow it seems reasonable to drop the specific init function again and just provide the parameter to the main init function. All C64-only user code can stay as-is. Only Apple II user code needs to by adjusted. Please note that this change only affects asm programs, C programs always used a single init function with the Apple II slot number as parameter.
608 lines
14 KiB
ArmAsm
608 lines
14 KiB
ArmAsm
; minimal telnet implementation (dumb terminal emulation only)
|
|
|
|
.include "../inc/common.inc"
|
|
.include "../inc/commonprint.inc"
|
|
.include "../inc/net.inc"
|
|
.include "../inc/error.inc"
|
|
|
|
.export start
|
|
|
|
.import abort_key
|
|
.importzp abort_key_default
|
|
.importzp abort_key_disable
|
|
.importzp eth_init_default
|
|
.import get_filtered_input
|
|
.import get_key
|
|
.import get_key_if_available
|
|
.import exit_to_basic
|
|
.import filter_dns
|
|
.import filter_number
|
|
.import dns_hostname_is_dotted_quad
|
|
.import dns_ip
|
|
.import dns_resolve
|
|
.import dns_set_hostname
|
|
.import ip65_process
|
|
.import native_to_ascii
|
|
.import parse_integer
|
|
.import print_cr
|
|
.import tcp_callback
|
|
.import tcp_close
|
|
.import tcp_connect
|
|
.import tcp_connect_ip
|
|
.import tcp_inbound_data_ptr
|
|
.import tcp_inbound_data_length
|
|
.import tcp_send
|
|
.import tcp_send_data_len
|
|
.import tcp_send_keep_alive
|
|
.import timer_read
|
|
.import vt100_init_terminal
|
|
.import vt100_exit_terminal
|
|
.import vt100_process_inbound_char
|
|
.import vt100_process_outbound_char
|
|
.importzp vt100_screen_cols
|
|
.importzp vt100_screen_rows
|
|
|
|
.export telnet_close
|
|
.export telnet_send_char
|
|
.export telnet_send_string
|
|
|
|
buffer_ptr = sreg
|
|
|
|
|
|
; keep LD65 happy
|
|
.segment "INIT"
|
|
.segment "ONCE"
|
|
|
|
|
|
.segment "STARTUP"
|
|
|
|
jmp start
|
|
eth_init_value:
|
|
.byte eth_init_default
|
|
|
|
|
|
.code
|
|
|
|
start:
|
|
jsr vt100_init_terminal
|
|
|
|
; initialize stack
|
|
ldax #welcome_1
|
|
jsr print_vt100
|
|
ldax #welcome_2
|
|
jsr print_vt100
|
|
ldax #initializing
|
|
jsr print_ascii_as_native
|
|
lda eth_init_value
|
|
jsr ip65_init
|
|
bcc :+
|
|
ldax #device_not_found
|
|
jsr print_ascii_as_native
|
|
jmp error_exit
|
|
: ldax #eth_name
|
|
jsr print_ascii_as_native
|
|
jsr print_cr
|
|
|
|
; get IP addr
|
|
ldax #obtaining
|
|
jsr print_ascii_as_native
|
|
jsr dhcp_init
|
|
bcc :+
|
|
jsr print_error
|
|
jmp error_exit
|
|
: ldax #cfg_ip
|
|
jsr print_dotted_quad
|
|
jsr print_cr
|
|
|
|
telnet_main_entry:
|
|
; enter host name
|
|
ldax #remote_host
|
|
jsr print_ascii_as_native
|
|
ldy #40 ; max chars
|
|
ldax #filter_dns
|
|
jsr get_filtered_input
|
|
bcc :+
|
|
jmp exit
|
|
|
|
; set host name
|
|
: stax buffer_ptr
|
|
ldy #$ff
|
|
: iny
|
|
lda (buffer_ptr),y
|
|
jsr native_to_ascii
|
|
cmp #'a'
|
|
bcs :+
|
|
cmp #'A'
|
|
bcc :+
|
|
clc
|
|
adc #'a'-'A'
|
|
: sta (buffer_ptr),y
|
|
tax ; set Z flag
|
|
bne :--
|
|
ldax buffer_ptr
|
|
jsr dns_set_hostname
|
|
bcc :+
|
|
jsr print_error
|
|
jmp telnet_main_entry
|
|
|
|
; resolve host name
|
|
: lda dns_hostname_is_dotted_quad
|
|
bne :++
|
|
ldax #resolving
|
|
jsr print_ascii_as_native
|
|
jsr dns_resolve
|
|
bcc :+
|
|
jsr print_error
|
|
jmp telnet_main_entry
|
|
: ldax #dns_ip
|
|
jsr print_dotted_quad
|
|
|
|
; enter port
|
|
: ldax #remote_port
|
|
jsr print_ascii_as_native
|
|
ldy #5 ; max chars
|
|
ldax #filter_number
|
|
jsr get_filtered_input
|
|
bcs :+ ; empty -> default
|
|
jsr parse_integer
|
|
bcc :++
|
|
jmp telnet_main_entry
|
|
: ldax #23 ; default
|
|
: stax telnet_port
|
|
|
|
; connect
|
|
ldax #connecting
|
|
jsr print_ascii_as_native
|
|
ldax #dns_ip
|
|
jsr print_dotted_quad
|
|
ldax #blank
|
|
jsr print_ascii_as_native
|
|
ldax #telnet_callback
|
|
stax tcp_callback
|
|
ldx #3
|
|
: lda dns_ip,x
|
|
sta tcp_connect_ip,x
|
|
dex
|
|
bpl :-
|
|
ldax telnet_port
|
|
jsr tcp_connect
|
|
bcc :+
|
|
jsr print_error
|
|
jmp telnet_main_entry
|
|
|
|
; connected
|
|
: ldax #ok
|
|
jsr print_ascii_as_native
|
|
lda #0
|
|
sta connection_close_requested
|
|
sta connection_closed
|
|
sta data_received
|
|
sta iac_response_buffer_length
|
|
lda #abort_key_disable
|
|
sta abort_key
|
|
ldax #on_connect
|
|
jsr print_vt100
|
|
|
|
@main_polling_loop:
|
|
jsr timer_read
|
|
txa ; 1/1000 * 256 = ~ 1/4 seconds
|
|
adc #$20 ; 32 x 1/4 = ~ 8 seconds
|
|
sta telnet_timeout
|
|
@check_timeout:
|
|
lda data_received
|
|
bne :+
|
|
jsr timer_read
|
|
cpx telnet_timeout
|
|
bne :+
|
|
jsr tcp_send_keep_alive
|
|
jmp @main_polling_loop
|
|
: lda #0
|
|
sta data_received
|
|
jsr ip65_process
|
|
lda connection_close_requested
|
|
beq :+
|
|
jsr tcp_close
|
|
jmp :++
|
|
: lda connection_closed
|
|
beq :++
|
|
: lda #abort_key_default
|
|
sta abort_key
|
|
ldax #on_disconnect
|
|
jsr print_vt100
|
|
ldax #disconnected
|
|
jsr print_ascii_as_native
|
|
jmp telnet_main_entry
|
|
: lda iac_response_buffer_length
|
|
beq :+
|
|
ldx #0
|
|
stax tcp_send_data_len
|
|
stx iac_response_buffer_length
|
|
ldax #iac_response_buffer
|
|
jsr tcp_send
|
|
: jsr get_key_if_available
|
|
bcc @check_timeout
|
|
ldx #0
|
|
stx tcp_send_data_len
|
|
stx tcp_send_data_len+1
|
|
tay
|
|
jsr vt100_process_outbound_char
|
|
jmp @main_polling_loop
|
|
|
|
print_vt100:
|
|
stax buffer_ptr
|
|
lda #0
|
|
sta buffer_offset
|
|
: ldy buffer_offset
|
|
lda (buffer_ptr),y
|
|
bne :+
|
|
rts
|
|
: tay
|
|
jsr vt100_process_inbound_char
|
|
inc buffer_offset
|
|
jmp :--
|
|
|
|
print_error:
|
|
lda ip65_error
|
|
cmp #IP65_ERROR_ABORTED_BY_USER
|
|
bne :+
|
|
ldax #abort
|
|
jmp print_ascii_as_native
|
|
: cmp #IP65_ERROR_TIMEOUT_ON_RECEIVE
|
|
bne :+
|
|
ldax #timeout
|
|
jmp print_ascii_as_native
|
|
: ldax #error_prefix
|
|
jsr print_ascii_as_native
|
|
lda ip65_error
|
|
jsr print_hex
|
|
jmp print_cr
|
|
|
|
error_exit:
|
|
ldax #press_a_key_to_continue
|
|
jsr print_ascii_as_native
|
|
jsr get_key
|
|
exit:
|
|
jsr vt100_exit_terminal
|
|
jmp exit_to_basic
|
|
|
|
|
|
; vt100 callback - will be executed when the user requests to close the connection
|
|
telnet_close:
|
|
lda #1
|
|
sta connection_close_requested
|
|
rts
|
|
|
|
|
|
; vt100 callback - will be executed when sending a char string
|
|
telnet_send_string:
|
|
stx buffer_ptr
|
|
sty buffer_ptr+1
|
|
ldy #0
|
|
: lda (buffer_ptr),y
|
|
beq send_char
|
|
sta scratch_buffer,y
|
|
inc tcp_send_data_len
|
|
iny
|
|
bne :-
|
|
jmp send_char
|
|
|
|
|
|
; vt100 callback - will be executed when sending a single char
|
|
telnet_send_char:
|
|
ldy tcp_send_data_len
|
|
sta scratch_buffer,y
|
|
inc tcp_send_data_len
|
|
|
|
send_char:
|
|
ldax #scratch_buffer
|
|
jsr tcp_send
|
|
bcs :+
|
|
rts
|
|
: lda ip65_error
|
|
cmp #IP65_ERROR_CONNECTION_CLOSED
|
|
bne :+
|
|
lda #1
|
|
sta connection_closed
|
|
rts
|
|
: ldax #send_error
|
|
jsr print_ascii_as_native
|
|
jmp print_error
|
|
|
|
|
|
; tcp callback - will be executed whenever data arrives on the TCP connection
|
|
telnet_callback:
|
|
lda #1
|
|
ldx tcp_inbound_data_length+1
|
|
cpx #$ff
|
|
bne :+
|
|
sta connection_closed
|
|
rts
|
|
: sta data_received
|
|
lda tcp_inbound_data_length
|
|
stax buffer_length
|
|
ldax tcp_inbound_data_ptr
|
|
stax buffer_ptr
|
|
|
|
@next_byte:
|
|
ldy #0
|
|
lda (buffer_ptr),y
|
|
tax
|
|
; 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, <command>, <option>
|
|
txa
|
|
sta telnet_option
|
|
lda telnet_command
|
|
cmp #$fb
|
|
beq @iac_will
|
|
cmp #$fc
|
|
beq @iac_wont
|
|
cmp #$fe
|
|
beq @iac_dont
|
|
; if we get here, then it's a "do"
|
|
lda telnet_option
|
|
cmp #$18 ; terminal type
|
|
beq @do_terminaltype
|
|
cmp #$1f
|
|
beq @do_naws
|
|
|
|
; if we get here, then it's a "do" command we don't honour
|
|
@iac_dont:
|
|
lda #$fc ; WONT
|
|
@add_iac_response:
|
|
ldx iac_response_buffer_length
|
|
sta iac_response_buffer+1,x
|
|
lda #$ff
|
|
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
|
|
@after_set_iac_response:
|
|
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 #$fd ; DO
|
|
jmp @add_iac_response
|
|
@will_suppress_ga:
|
|
lda #$fd ; DO
|
|
jmp @add_iac_response
|
|
|
|
@do_naws:
|
|
ldx #0
|
|
: lda naws_response,x
|
|
ldy iac_response_buffer_length
|
|
inc iac_response_buffer_length
|
|
sta iac_response_buffer,y
|
|
inx
|
|
txa
|
|
cmp #naws_response_length
|
|
bne :-
|
|
jmp @after_set_iac_response
|
|
@do_terminaltype:
|
|
lda #$fb ; WILL
|
|
jmp @add_iac_response
|
|
|
|
@not_iac:
|
|
txa
|
|
tay
|
|
jsr vt100_process_inbound_char
|
|
|
|
@byte_processed:
|
|
inc buffer_ptr
|
|
bne :+
|
|
inc buffer_ptr+1
|
|
: lda buffer_length+1
|
|
beq :++
|
|
lda buffer_length
|
|
bne :+
|
|
dec buffer_length+1
|
|
: dec buffer_length
|
|
jmp @next_byte
|
|
: dec buffer_length
|
|
beq :+
|
|
jmp @next_byte
|
|
: rts
|
|
|
|
|
|
.rodata
|
|
|
|
blank: .byte " ",0
|
|
initializing: .byte 10,"Initializing ",0
|
|
obtaining: .byte "Obtaining IP address ",0
|
|
resolving: .byte 10,"Resolving to address ",0
|
|
connecting: .byte 10,"Connecting to ",0
|
|
ok: .byte "Ok",10,10,0
|
|
device_not_found: .byte "- Device not found",10,0
|
|
abort: .byte "- User abort",10,0
|
|
timeout: .byte "- Timeout",10,0
|
|
error_prefix: .byte "- Error $",0
|
|
remote_host: .byte 10,"Hostname (leave blank to quit)",10,"? ",0
|
|
remote_port: .byte 10,10,"Port Num (leave blank for default)",10,"? ",0
|
|
disconnected: .byte 10,"Disconnected",10,0
|
|
send_error: .byte "Sending ",0
|
|
|
|
welcome_1: .byte 27,")0"
|
|
.byte 14,"lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk"
|
|
.byte 15,13,10
|
|
.byte 14,"x x"
|
|
.byte 15,13,10
|
|
.byte 14,"x",15," ",27,"[1m","Telnet65 v1.2",27,"[0m"," based on: ",14,"x"
|
|
.byte 15,13,10
|
|
.byte 14,"x x"
|
|
.byte 15,13,10,0
|
|
welcome_2: .byte 14,"x",15," - IP65 (github.com/cc65/ip65) ",14,"x"
|
|
.byte 15,13,10
|
|
.byte 14,"x",15," - CaTer (www.opppf.de/Cater) ",14,"x"
|
|
.byte 15,13,10
|
|
.byte 14,"x x"
|
|
.byte 15,13,10
|
|
.byte 14,"mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj"
|
|
.byte 15,13,10
|
|
.byte 27,")A"
|
|
.byte 27,"[?25l",0
|
|
on_connect: .byte 27,"[?25h",0
|
|
on_disconnect: .byte 27,"[?25l",27,"[0m",27,"(A",15,0
|
|
|
|
; initial_telnet_options:
|
|
; .byte $ff,$fb,$1F ; IAC WILL NAWS
|
|
; .byte $ff,$fb,$18 ; IAC WILL TERMINAL TYPE
|
|
; initial_telnet_options_length = *-initial_telnet_options
|
|
|
|
terminal_type_response:
|
|
.byte $ff ; IAC
|
|
.byte $fa ; SB
|
|
.byte $18 ; TERMINAL TYPE
|
|
.byte $0 ; IS
|
|
.byte "vt100" ; what we pretend to be
|
|
.byte $ff ; IAC
|
|
.byte $f0 ; SE
|
|
terminal_type_response_length = *-terminal_type_response
|
|
|
|
naws_response:
|
|
.byte $ff,$fb,$1f ; IAC WILL NAWS
|
|
.byte $ff ; IAC
|
|
.byte $fa ; SB
|
|
.byte $1f ; NAWS
|
|
.byte $00 ; width (high byte)
|
|
.byte vt100_screen_cols ; width (low byte)
|
|
.byte $00 ; height (high byte)
|
|
.byte vt100_screen_rows ; height (low byte)
|
|
.byte $ff ; IAC
|
|
.byte $f0 ; SE
|
|
naws_response_length = *-naws_response
|
|
|
|
|
|
.bss
|
|
|
|
; variables
|
|
telnet_port: .res 2 ; port number to connect to
|
|
telnet_timeout: .res 1
|
|
connection_close_requested: .res 1
|
|
connection_closed: .res 1
|
|
data_received: .res 1
|
|
buffer_offset: .res 1
|
|
telnet_command: .res 1
|
|
telnet_option: .res 1
|
|
|
|
telnet_state_normal = 0
|
|
telnet_state_got_iac = 1
|
|
telnet_state_got_command = 2
|
|
telnet_state_got_suboption = 3
|
|
|
|
buffer_length: .res 2
|
|
|
|
telnet_state: .res 1
|
|
iac_response_buffer: .res 64
|
|
iac_response_buffer_length: .res 1
|
|
scratch_buffer : .res 40
|
|
iac_suboption_buffer: .res 64
|
|
iac_suboption_buffer_length: .res 1
|
|
|
|
|
|
|
|
; -- LICENSE FOR telnet.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 --
|