mirror of
https://github.com/bobbimanners/emailler.git
synced 2024-11-05 00:04:46 +00:00
09c718b74f
It's the very purpose of the DATA segment to consolidate all items that potentially need to be copied from ROM to RAM. So there's no need to / point in doing that on a per-item-base.
988 lines
22 KiB
ArmAsm
988 lines
22 KiB
ArmAsm
; Ethernet driver for W5100 W5100 chip
|
|
|
|
.ifndef KPR_API_VERSION_NUMBER
|
|
.define EQU =
|
|
.include "../inc/kipper_constants.i"
|
|
.endif
|
|
|
|
.include "../inc/common.i"
|
|
|
|
.include "w5100.i"
|
|
|
|
WIZNET_BASE = $DE04
|
|
|
|
WIZNET_MODE_REG = WIZNET_BASE
|
|
WIZNET_ADDR_HI = WIZNET_BASE+1
|
|
WIZNET_ADDR_LO = WIZNET_BASE+2
|
|
WIZNET_DATA_REG = WIZNET_BASE+3
|
|
|
|
; DEBUG = 1
|
|
.export eth_init
|
|
.export eth_rx
|
|
.export eth_tx
|
|
.export eth_driver_name
|
|
.export eth_driver_io_base
|
|
.import eth_inp
|
|
.import eth_inp_len
|
|
.import eth_outp
|
|
.import eth_outp_len
|
|
|
|
.import timer_init
|
|
.import timer_read
|
|
|
|
.import arp_init
|
|
.import ip_init
|
|
|
|
.importzp eth_dest
|
|
.importzp eth_src
|
|
.importzp eth_type
|
|
.importzp eth_data
|
|
.importzp copy_src
|
|
.importzp copy_dest
|
|
|
|
.export w5100_ip65_init
|
|
.export w5100_read_register
|
|
.export w5100_select_register
|
|
|
|
.export w5100_write_register
|
|
.export w5100_set_ip_config
|
|
.export tcp_connect
|
|
.export tcp_connect_ip
|
|
.export tcp_callback
|
|
.export tcp_send_data_len
|
|
.export tcp_send_string
|
|
.export tcp_send
|
|
.export tcp_send_keep_alive
|
|
.export tcp_close
|
|
.export tcp_state
|
|
|
|
.export tcp_connect_remote_port
|
|
.export tcp_remote_ip
|
|
.export tcp_listen
|
|
|
|
.export tcp_inbound_data_ptr
|
|
.export tcp_inbound_data_length
|
|
|
|
.import cfg_mac
|
|
.import cfg_ip
|
|
.import cfg_netmask
|
|
.import cfg_gateway
|
|
|
|
.import ip65_error
|
|
.import ip65_process
|
|
.import check_for_abort_key
|
|
|
|
|
|
.code
|
|
|
|
; initialize the ethernet adaptor
|
|
; inputs: none
|
|
; outputs: carry flag is set if there was an error, clear otherwise
|
|
; this implementation uses a default address for the w5100, and can be
|
|
; called as a 'generic' eth driver init function
|
|
eth_init:
|
|
lda $de01
|
|
ora #1 ; turn on clockport
|
|
sta $de01
|
|
|
|
lda #$80 ; reset
|
|
sta WIZNET_MODE_REG
|
|
lda WIZNET_MODE_REG
|
|
bne @error ; writing a byte to the MODE register with bit 7 set should reset.
|
|
; after a reset, mode register is zero
|
|
; therefore, if there is a real W5100 at the specified address,
|
|
; we should be able to write a $80 and read back a $00
|
|
lda #$13 ; set indirect mode, with autoinc, no auto PING
|
|
sta WIZNET_MODE_REG
|
|
lda WIZNET_MODE_REG
|
|
cmp #$13
|
|
bne @error ; make sure if we write to mode register without bit 7 set,
|
|
; the value persists.
|
|
lda #$00
|
|
sta WIZNET_ADDR_HI
|
|
lda #$16
|
|
sta WIZNET_ADDR_LO
|
|
|
|
ldx #$00 ; start writing to reg $0016 - Interrupt Mask Register
|
|
@loop:
|
|
lda w5100_config_data,x
|
|
sta WIZNET_DATA_REG
|
|
inx
|
|
cpx #$06
|
|
bne @loop
|
|
|
|
lda #$09
|
|
sta WIZNET_ADDR_LO
|
|
ldx #$00 ; start writing to reg $0009 - MAC address
|
|
|
|
@mac_loop:
|
|
lda cfg_mac,x
|
|
sta WIZNET_DATA_REG
|
|
inx
|
|
cpx #$06
|
|
bne @mac_loop
|
|
|
|
; set up socket 0 for MAC RAW mode
|
|
|
|
ldax #W5100_RMSR ; rx memory size (each socket)
|
|
stx WIZNET_ADDR_HI
|
|
sta WIZNET_ADDR_LO
|
|
|
|
lda #$0A ; sockets 0 & 1 4KB each, other sockets 0KB
|
|
; if this is changed, change the mask in eth_rx as well!
|
|
sta WIZNET_DATA_REG
|
|
|
|
ldax #W5100_TMSR ; rx memory size (each socket)
|
|
stx WIZNET_ADDR_HI
|
|
sta WIZNET_ADDR_LO
|
|
|
|
lda #$0A ; sockets 0 & 1 4KB each, other sockets 0KB
|
|
; if this is changed, change the mask in eth_tx as well!
|
|
sta WIZNET_DATA_REG
|
|
|
|
ldax #W5100_S0_MR
|
|
stx WIZNET_ADDR_HI
|
|
sta WIZNET_ADDR_LO
|
|
lda #W5100_MODE_MAC_RAW
|
|
sta WIZNET_DATA_REG
|
|
|
|
; open socket 0
|
|
jsr w5100_write_register
|
|
ldax #W5100_S0_CR
|
|
stx WIZNET_ADDR_HI
|
|
sta WIZNET_ADDR_LO
|
|
lda #W5100_CMD_OPEN
|
|
sta WIZNET_DATA_REG
|
|
|
|
lda #tcp_cxn_state_closed
|
|
sta tcp_state
|
|
clc
|
|
rts
|
|
|
|
@error:
|
|
sec
|
|
rts
|
|
|
|
; initialize the ip65 stack for the w5100 ethernet adaptor
|
|
; inputs: none
|
|
; outputs: carry flag is set if there was an error, clear otherwise
|
|
w5100_ip65_init:
|
|
jsr eth_init
|
|
|
|
bcc @ok
|
|
lda #KPR_ERROR_DEVICE_FAILURE
|
|
sta ip65_error
|
|
rts
|
|
@ok:
|
|
jsr timer_init ; initialize timer
|
|
jsr arp_init ; initialize arp
|
|
jsr ip_init ; initialize ip, icmp, udp, and tcp
|
|
clc
|
|
rts
|
|
|
|
; receive a packet
|
|
; inputs: none
|
|
; outputs:
|
|
; if there was an error receiving the packet (or no packet was ready) then carry flag is set
|
|
; if packet was received correctly then carry flag is clear,
|
|
; eth_inp contains the received packet,
|
|
; and eth_inp_len contains the length of the packet
|
|
eth_rx:
|
|
; eth_rx will get called in the main polling loop
|
|
; we shoe horn a check for data on the TCP socket here
|
|
; if we do get TCP data, we will call the TCP callback routine
|
|
; but we hide all of this from the ip65 stack proper.
|
|
lda tcp_state
|
|
beq @no_tcp
|
|
|
|
jsr tcp_rx
|
|
bcc @no_tcp ; if we didn't get any TCP traffic, go check for a raw ethernet packet
|
|
; eth_inp and eth_inp_len are not valid, so leave carry flag set to indicate no ethernet frame data
|
|
rts
|
|
|
|
@no_tcp:
|
|
ldax #W5100_S0_RX_RSR0
|
|
jsr w5100_read_register
|
|
sta eth_inp_len+1
|
|
ldax #W5100_S0_RX_RSR1
|
|
jsr w5100_read_register
|
|
sta eth_inp_len
|
|
bne @got_data
|
|
lda eth_inp_len+1
|
|
bne @got_data
|
|
sec
|
|
rts
|
|
@got_data:
|
|
lda #$8D ; opcode for STA
|
|
sta next_eth_packet_byte
|
|
ldax #eth_inp
|
|
stax copy_dest
|
|
|
|
lda #2
|
|
sta byte_ctr_lo
|
|
lda #0
|
|
sta byte_ctr_hi
|
|
|
|
; read the 2 byte frame length
|
|
jsr @get_current_rx_rd
|
|
jsr @mask_and_adjust_rx_read
|
|
|
|
ldax rx_rd_ptr
|
|
jsr w5100_read_register
|
|
sta eth_inp_len+1 ; high byte of frame length
|
|
jsr @inc_rx_rd_ptr
|
|
ldax rx_rd_ptr
|
|
jsr w5100_read_register
|
|
sta eth_inp_len ; lo byte of frame length
|
|
|
|
; now copy the rest of the frame to the eth_inp buffer
|
|
; we keep our own copy of RX_RD_PTR in sync, rather than read WIZNET_ADDR registers
|
|
; because of issue where reads to WIZNET_ADDR can cause the autoinc ptr to advance erroneously
|
|
; when WizNet cart used in a cartridge expander
|
|
ldy #0
|
|
@get_next_byte:
|
|
inc rx_rd_ptr
|
|
bne :+
|
|
inc rx_rd_ptr+1
|
|
lda rx_rd_ptr+1
|
|
and #$0F
|
|
clc
|
|
adc #$60
|
|
sta rx_rd_ptr+1
|
|
sta WIZNET_ADDR_HI
|
|
: lda WIZNET_DATA_REG
|
|
sta (copy_dest),y
|
|
iny
|
|
bne :+
|
|
inc copy_dest+1
|
|
: inc byte_ctr_lo
|
|
bne :+
|
|
inc byte_ctr_hi
|
|
: lda byte_ctr_lo
|
|
cmp eth_inp_len
|
|
bne @get_next_byte
|
|
lda byte_ctr_hi
|
|
cmp eth_inp_len+1
|
|
bne @get_next_byte
|
|
|
|
; update the RX RD pointer past the frame we just read
|
|
jsr @get_current_rx_rd
|
|
clc
|
|
lda rx_rd_ptr
|
|
adc eth_inp_len
|
|
sta rx_rd_ptr
|
|
lda rx_rd_ptr+1
|
|
adc eth_inp_len+1
|
|
tay
|
|
ldax #W5100_S0_RX_RD0
|
|
jsr w5100_write_register
|
|
ldy rx_rd_ptr
|
|
|
|
ldax #W5100_S0_RX_RD1
|
|
jsr w5100_write_register
|
|
ldax #W5100_S0_CR
|
|
ldy #W5100_CMD_RECV
|
|
jsr w5100_write_register
|
|
|
|
; now adjust the input length to remove the 2 byte header length
|
|
sec
|
|
lda eth_inp_len
|
|
sbc #2
|
|
sta eth_inp_len
|
|
bcs :+
|
|
dec eth_inp_len
|
|
: clc
|
|
rts
|
|
|
|
@inc_rx_rd_ptr:
|
|
inc rx_rd_ptr
|
|
bne :+
|
|
inc rx_rd_ptr+1
|
|
@mask_and_adjust_rx_read:
|
|
lda rx_rd_ptr+1
|
|
and #$0F
|
|
clc
|
|
adc #$60
|
|
sta rx_rd_ptr+1
|
|
: rts
|
|
|
|
@get_current_rx_rd:
|
|
ldax #W5100_S0_RX_RD0
|
|
jsr w5100_read_register
|
|
sta rx_rd_ptr+1
|
|
ldax #W5100_S0_RX_RD1
|
|
jsr w5100_read_register
|
|
sta rx_rd_ptr
|
|
rts
|
|
|
|
; send a packet
|
|
; inputs:
|
|
; eth_outp: packet to send
|
|
; eth_outp_len: length of packet to send
|
|
; outputs:
|
|
; if there was an error sending the packet then carry flag is set
|
|
; otherwise carry flag is cleared
|
|
eth_tx:
|
|
lda #$AD ; opcode for LDA
|
|
sta next_eth_packet_byte
|
|
ldax #eth_outp
|
|
sta eth_ptr_lo
|
|
stx eth_ptr_hi
|
|
lda #0
|
|
sta byte_ctr_lo
|
|
sta byte_ctr_hi
|
|
|
|
jsr @get_current_tx_wr
|
|
jmp @calculate_tx_wr_ptr
|
|
@send_next_byte:
|
|
jsr next_eth_packet_byte
|
|
tay
|
|
ldax tx_wr_ptr
|
|
jsr w5100_write_register
|
|
|
|
inc byte_ctr_lo
|
|
bne :+
|
|
inc byte_ctr_hi
|
|
: inc tx_wr_ptr
|
|
bne :+
|
|
inc tx_wr_ptr+1
|
|
@calculate_tx_wr_ptr:
|
|
lda tx_wr_ptr+1
|
|
and #$0F
|
|
clc
|
|
adc #$40
|
|
sta tx_wr_ptr+1
|
|
: lda byte_ctr_lo
|
|
cmp eth_outp_len
|
|
bne @send_next_byte
|
|
lda byte_ctr_hi
|
|
cmp eth_outp_len+1
|
|
bne @send_next_byte
|
|
|
|
; all bytes copied, now adjust the tx write ptr and SEND
|
|
jsr @get_current_tx_wr
|
|
clc
|
|
lda tx_wr_ptr
|
|
adc eth_outp_len
|
|
sta tx_wr_ptr
|
|
lda tx_wr_ptr+1
|
|
adc eth_outp_len+1
|
|
tay
|
|
ldax #W5100_S0_TX_WR0
|
|
jsr w5100_write_register
|
|
ldy tx_wr_ptr
|
|
ldax #W5100_S0_TX_WR1
|
|
jsr w5100_write_register
|
|
ldax #W5100_S0_CR
|
|
ldy #W5100_CMD_SEND
|
|
jsr w5100_write_register
|
|
clc
|
|
rts
|
|
|
|
@get_current_tx_wr:
|
|
ldax #W5100_S0_TX_WR0
|
|
jsr w5100_read_register
|
|
sta tx_wr_ptr+1
|
|
ldax #W5100_S0_TX_WR1
|
|
jsr w5100_read_register
|
|
sta tx_wr_ptr
|
|
rts
|
|
|
|
advance_eth_ptr:
|
|
inc eth_ptr_lo
|
|
bne :+
|
|
inc eth_ptr_hi
|
|
: rts
|
|
|
|
; read one of the W5100 registers
|
|
; inputs: AX = register number to read
|
|
; outputs: A = value of nominated register
|
|
; y is overwritten
|
|
w5100_read_register:
|
|
jsr w5100_select_register
|
|
lda WIZNET_DATA_REG
|
|
rts
|
|
|
|
; write to one of the W5100 registers
|
|
; inputs: AX = register number to write
|
|
; Y = value to write to register
|
|
; outputs: none
|
|
w5100_write_register:
|
|
jsr w5100_select_register
|
|
tya
|
|
sta WIZNET_DATA_REG
|
|
rts
|
|
|
|
; listen for an inbound tcp connection
|
|
; this is a 'blocking' call, i.e. it will not return until a connection has been made
|
|
; inputs:
|
|
; AX: destination port (2 bytes)
|
|
; tcp_callback: vector to call when data arrives on this connection
|
|
; outputs:
|
|
; carry flag is set if an error occured, clear otherwise
|
|
tcp_listen:
|
|
stax tcp_local_port
|
|
jsr setup_tcp_socket
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_LISTEN
|
|
jsr w5100_write_register
|
|
|
|
; now wait for the status to change to 'established'
|
|
@listen_loop:
|
|
; inc $d020
|
|
jsr ip65_process
|
|
jsr check_for_abort_key
|
|
bcc @no_abort
|
|
lda #KPR_ERROR_ABORTED_BY_USER
|
|
sta ip65_error
|
|
sec
|
|
rts
|
|
@no_abort:
|
|
ldax #W5100_S1_SR
|
|
jsr w5100_read_register
|
|
cmp #W5100_STATUS_SOCK_ESTABLISHED
|
|
bne @listen_loop
|
|
|
|
lda #tcp_cxn_state_established
|
|
sta tcp_state
|
|
|
|
; copy the remote IP address & port number
|
|
ldax #W5100_S1_DIPR0
|
|
jsr w5100_select_register
|
|
ldx #0
|
|
@ip_loop:
|
|
lda WIZNET_DATA_REG
|
|
sta tcp_remote_ip,x
|
|
inx
|
|
cpx #$04
|
|
bne @ip_loop
|
|
|
|
ldax #W5100_S1_DPORT0
|
|
jsr w5100_select_register
|
|
lda WIZNET_DATA_REG
|
|
sta tcp_connect_remote_port+1
|
|
lda WIZNET_DATA_REG
|
|
sta tcp_connect_remote_port
|
|
clc
|
|
rts
|
|
|
|
; make outbound tcp connection
|
|
; inputs:
|
|
; tcp_connect_ip: destination ip address (4 bytes)
|
|
; AX: destination port (2 bytes)
|
|
; tcp_callback: vector to call when data arrives on this connection
|
|
; outputs:
|
|
; carry flag is set if an error occured, clear otherwise
|
|
tcp_connect:
|
|
stax tcp_remote_port
|
|
jsr timer_read ; get a pseudo random value
|
|
sta tcp_local_port+1
|
|
inc tcp_local_port
|
|
|
|
jsr setup_tcp_socket
|
|
|
|
; set the destination IP address
|
|
ldax #W5100_S1_DIPR0
|
|
jsr w5100_select_register
|
|
ldx #0
|
|
@remote_ip_loop:
|
|
lda tcp_connect_ip,x
|
|
sta WIZNET_DATA_REG
|
|
inx
|
|
cpx #$04
|
|
bne @remote_ip_loop
|
|
ldx #0
|
|
|
|
; W5100 register address is now W5100_S1_DPORT0, so set the destination port
|
|
lda tcp_remote_port+1
|
|
sta WIZNET_DATA_REG
|
|
lda tcp_remote_port
|
|
sta WIZNET_DATA_REG
|
|
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_CONNECT
|
|
jsr w5100_write_register
|
|
|
|
; now wait for the status to change to 'established'
|
|
@connect_loop:
|
|
ldax #W5100_S1_SR
|
|
jsr w5100_read_register
|
|
cmp #W5100_STATUS_SOCK_CLOSED
|
|
beq @error
|
|
cmp #W5100_STATUS_SOCK_ESTABLISHED
|
|
beq @ok
|
|
|
|
jsr check_for_abort_key
|
|
bcc @connect_loop
|
|
lda #KPR_ERROR_ABORTED_BY_USER
|
|
jmp @set_error_and_exit
|
|
|
|
@ok:
|
|
lda #tcp_cxn_state_established
|
|
sta tcp_state
|
|
clc
|
|
rts
|
|
|
|
@error:
|
|
lda #KPR_ERROR_CONNECTION_CLOSED
|
|
@set_error_and_exit:
|
|
sta ip65_error
|
|
sec
|
|
rts
|
|
|
|
; send a string over the current tcp connection
|
|
; inputs:
|
|
; tcp connection should already be opened
|
|
; AX: pointer to buffer - data up to (but not including)
|
|
; the first nul byte will be sent. max of 255 bytes will be sent.
|
|
; outputs:
|
|
; carry flag is set if an error occured, clear otherwise
|
|
tcp_send_string:
|
|
stax tcp_send_data_ptr
|
|
stax copy_src
|
|
lda #0
|
|
tay
|
|
sta tcp_send_data_len
|
|
sta tcp_send_data_len+1
|
|
lda (copy_src),y
|
|
bne @find_end_of_string
|
|
rts ; if the string is empty, don't send anything!
|
|
@find_end_of_string:
|
|
lda (copy_src),y
|
|
beq @done
|
|
inc tcp_send_data_len
|
|
iny
|
|
bne @find_end_of_string
|
|
@done:
|
|
ldax tcp_send_data_ptr
|
|
; now we can fall through into tcp_send
|
|
|
|
; send tcp data
|
|
; inputs:
|
|
; tcp connection should already be opened
|
|
; tcp_send_data_len: length of data to send (exclusive of any headers)
|
|
; 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
|
|
|
|
; are we connected?
|
|
ldax #W5100_S1_SR
|
|
jsr w5100_read_register
|
|
cmp #W5100_STATUS_SOCK_ESTABLISHED
|
|
beq @ok
|
|
|
|
lda #KPR_ERROR_CONNECTION_CLOSED
|
|
sta ip65_error
|
|
sec
|
|
rts
|
|
@ok:
|
|
lda #$AD ; opcode for LDA
|
|
sta next_eth_packet_byte
|
|
|
|
lda #0
|
|
sta byte_ctr_lo
|
|
sta byte_ctr_hi
|
|
|
|
jsr @get_current_tx_wr
|
|
jmp @calculate_tx_wr_ptr
|
|
@send_next_byte:
|
|
jsr next_eth_packet_byte
|
|
tay
|
|
ldax tx_wr_ptr
|
|
jsr w5100_write_register
|
|
|
|
inc byte_ctr_lo
|
|
bne :+
|
|
inc byte_ctr_hi
|
|
: inc tx_wr_ptr
|
|
bne :+
|
|
inc tx_wr_ptr+1
|
|
@calculate_tx_wr_ptr:
|
|
lda tx_wr_ptr+1
|
|
and #$0F
|
|
clc
|
|
adc #$50
|
|
sta tx_wr_ptr+1
|
|
: lda byte_ctr_lo
|
|
cmp tcp_send_data_len
|
|
bne @send_next_byte
|
|
lda byte_ctr_hi
|
|
cmp tcp_send_data_len+1
|
|
bne @send_next_byte
|
|
|
|
; all bytes copied, now adjust the tx write ptr and SEND
|
|
jsr @get_current_tx_wr
|
|
clc
|
|
lda tx_wr_ptr
|
|
adc tcp_send_data_len
|
|
sta tx_wr_ptr
|
|
lda tx_wr_ptr+1
|
|
adc tcp_send_data_len+1
|
|
tay
|
|
ldax #W5100_S1_TX_WR0
|
|
jsr w5100_write_register
|
|
ldy tx_wr_ptr
|
|
ldax #W5100_S1_TX_WR1
|
|
jsr w5100_write_register
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_SEND
|
|
jsr w5100_write_register
|
|
clc
|
|
rts
|
|
|
|
@get_current_tx_wr:
|
|
ldax #W5100_S1_TX_WR0
|
|
jsr w5100_read_register
|
|
sta tx_wr_ptr+1
|
|
ldax #W5100_S1_TX_WR1
|
|
jsr w5100_read_register
|
|
sta tx_wr_ptr
|
|
rts
|
|
|
|
; send an empty ACK packet on the current connection
|
|
; inputs:
|
|
; none
|
|
; outputs:
|
|
; carry flag is set if an error occured, clear otherwise
|
|
tcp_send_keep_alive:
|
|
; are we connected?
|
|
ldax #W5100_S1_SR
|
|
jsr w5100_read_register
|
|
cmp #W5100_STATUS_SOCK_ESTABLISHED
|
|
beq @ok
|
|
|
|
lda #KPR_ERROR_CONNECTION_CLOSED
|
|
sta ip65_error
|
|
sec
|
|
rts
|
|
@ok:
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_SEND_KEEP
|
|
jsr w5100_write_register
|
|
clc
|
|
rts
|
|
|
|
; close the current connection
|
|
; inputs:
|
|
; none
|
|
; outputs:
|
|
; carry flag is set if an error occured, clear otherwise
|
|
tcp_close:
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_DISCONNECT
|
|
jsr w5100_write_register
|
|
clc
|
|
rts
|
|
|
|
; poll the TCP socket
|
|
; if there is data available, call the user supplied TCP callback
|
|
; inputs:
|
|
; none
|
|
; outputs:
|
|
; carry flag is set if there was data, clear otherwise
|
|
tcp_rx:
|
|
; is there data?
|
|
ldax #W5100_S1_RX_RSR0
|
|
jsr w5100_read_register
|
|
sta tcp_inbound_data_length+1
|
|
ldax #W5100_S1_RX_RSR1
|
|
jsr w5100_read_register
|
|
sta tcp_inbound_data_length
|
|
bne @got_data
|
|
lda tcp_inbound_data_length+1
|
|
bne @got_data
|
|
|
|
; are we connected?
|
|
ldax #W5100_S1_SR
|
|
jsr w5100_read_register
|
|
cmp #W5100_STATUS_SOCK_ESTABLISHED
|
|
beq @connected_but_no_data
|
|
; no longer connected
|
|
lda #tcp_cxn_state_closed
|
|
sta tcp_state
|
|
|
|
lda #$ff
|
|
sta tcp_inbound_data_length
|
|
sta tcp_inbound_data_length+1
|
|
jsr @make_fake_eth_header
|
|
jsr jmp_to_callback ; let the caller see the connection has closed
|
|
sec ; don't poll the MAC RAW socket, else it may clobber the output buffer
|
|
rts
|
|
@connected_but_no_data:
|
|
clc ; no data - go check the MAC RAW socket
|
|
rts
|
|
@got_data:
|
|
lda #$8D ; opcode for STA
|
|
sta next_eth_packet_byte
|
|
|
|
ldax #eth_inp+$36 ; we will write to the location that TCP data would appear if this was a raw eth frame,
|
|
; 14 bytes of ethernet header
|
|
; 20 bytes of IP header
|
|
; 20 bytes of TCP header
|
|
stax tcp_inbound_data_ptr
|
|
|
|
sta eth_ptr_lo
|
|
stx eth_ptr_hi
|
|
|
|
lda #0
|
|
sta byte_ctr_lo
|
|
sta byte_ctr_hi
|
|
|
|
lda tcp_inbound_data_length+1
|
|
cmp #4 ; don't allow more than $4FF bytes at once ($1279) since we are writing to a 1500 byte
|
|
bmi :+
|
|
lda #4
|
|
sta tcp_inbound_data_length+1
|
|
: ; now copy the data just arrived to the eth_inp buffer
|
|
jsr @get_current_rx_rd
|
|
jsr @mask_and_adjust_rx_read
|
|
@get_next_byte:
|
|
ldax rx_rd_ptr
|
|
jsr w5100_read_register
|
|
jsr next_eth_packet_byte
|
|
|
|
jsr @inc_rx_rd_ptr
|
|
|
|
inc byte_ctr_lo
|
|
bne :+
|
|
inc byte_ctr_hi
|
|
: lda byte_ctr_lo
|
|
cmp tcp_inbound_data_length
|
|
bne @get_next_byte
|
|
lda byte_ctr_hi
|
|
cmp tcp_inbound_data_length+1
|
|
bne @get_next_byte
|
|
|
|
; update the RX RD pointer past the frame we just read
|
|
jsr @get_current_rx_rd
|
|
clc
|
|
lda rx_rd_ptr
|
|
adc tcp_inbound_data_length
|
|
sta rx_rd_ptr
|
|
lda rx_rd_ptr+1
|
|
adc tcp_inbound_data_length+1
|
|
tay
|
|
ldax #W5100_S1_RX_RD0
|
|
jsr w5100_write_register
|
|
ldy rx_rd_ptr
|
|
|
|
ldax #W5100_S1_RX_RD1
|
|
jsr w5100_write_register
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_RECV
|
|
jsr w5100_write_register
|
|
|
|
jsr @make_fake_eth_header
|
|
jsr jmp_to_callback ; let the caller see the connection has closed
|
|
sec ; don't poll the MAC RAW socket, else it may clobber the output buffer
|
|
rts
|
|
|
|
@inc_rx_rd_ptr:
|
|
inc rx_rd_ptr
|
|
bne :+
|
|
inc rx_rd_ptr+1
|
|
@mask_and_adjust_rx_read:
|
|
lda rx_rd_ptr+1
|
|
and #$0F
|
|
clc
|
|
adc #$70
|
|
sta rx_rd_ptr+1
|
|
: rts
|
|
|
|
@get_current_rx_rd:
|
|
ldax #W5100_S1_RX_RD0
|
|
jsr w5100_read_register
|
|
sta rx_rd_ptr+1
|
|
ldax #W5100_S1_RX_RD1
|
|
jsr w5100_read_register
|
|
sta rx_rd_ptr
|
|
rts
|
|
|
|
.import ip_inp
|
|
.import udp_inp
|
|
|
|
; the function dispatcher (and possibly other parts of the ip65 stack) expect to find valid values in the eth_inp frame
|
|
; when processing tcp data
|
|
@make_fake_eth_header:
|
|
; first set the TCP protocol value
|
|
lda #6 ; TCP protocol number
|
|
sta ip_inp+9 ; proto number
|
|
|
|
; now copy the remote IP address
|
|
ldx #0
|
|
@ip_loop:
|
|
lda tcp_remote_ip,x
|
|
sta ip_inp+12,x ; src IP
|
|
inx
|
|
cpx #$04
|
|
bne @ip_loop
|
|
|
|
; now the local & remote ports
|
|
lda tcp_connect_remote_port
|
|
sta udp_inp+1 ; remote port (lo byte)
|
|
lda tcp_connect_remote_port+1
|
|
sta udp_inp+0 ; remote port (high byte)
|
|
lda tcp_local_port
|
|
sta udp_inp+3 ; local port (lo byte)
|
|
lda tcp_local_port+1
|
|
sta udp_inp+2 ; local port (high byte)
|
|
rts
|
|
|
|
jmp_to_callback:
|
|
jmp (tcp_callback)
|
|
|
|
; copy the IP65 configuration to the the w5100 onchip configuration
|
|
; we assume MAC has been configured already via eth_init, but IP
|
|
; address etc may not be known when the w5100 was initialised (e.g.
|
|
; if using DHCP).
|
|
w5100_set_ip_config:
|
|
ldax #W5100_GAR0
|
|
jsr w5100_select_register
|
|
ldx #0
|
|
@gateway_loop:
|
|
lda cfg_gateway,x
|
|
sta WIZNET_DATA_REG
|
|
inx
|
|
cpx #$04
|
|
bne @gateway_loop
|
|
ldx #0
|
|
@netmask_loop:
|
|
lda cfg_netmask,x
|
|
sta WIZNET_DATA_REG
|
|
inx
|
|
cpx #$04
|
|
bne @netmask_loop
|
|
|
|
ldax #W5100_SIPR0
|
|
jsr w5100_select_register
|
|
ldx #0
|
|
@ip_loop:
|
|
lda cfg_ip,x
|
|
sta WIZNET_DATA_REG
|
|
inx
|
|
cpx #$04
|
|
bne @ip_loop
|
|
rts
|
|
|
|
setup_tcp_socket:
|
|
jsr w5100_set_ip_config
|
|
ldax #W5100_S1_PORT0
|
|
jsr w5100_select_register
|
|
lda tcp_local_port+1
|
|
sta WIZNET_DATA_REG
|
|
lda tcp_local_port
|
|
sta WIZNET_DATA_REG
|
|
|
|
lda #0
|
|
sta tcp_state
|
|
|
|
ldax #W5100_S1_MR
|
|
ldy #W5100_MODE_TCP
|
|
jsr w5100_write_register
|
|
|
|
; open socket 1
|
|
ldax #W5100_S1_CR
|
|
ldy #W5100_CMD_OPEN
|
|
jsr w5100_write_register
|
|
rts
|
|
|
|
|
|
.rodata
|
|
|
|
eth_driver_name:
|
|
.asciiz "RR-NET MK3 (WIZNET 5100)"
|
|
|
|
eth_driver_io_base:
|
|
.word WIZNET_BASE
|
|
|
|
w5100_config_data:
|
|
.byte $00 ; no interrupts
|
|
.byte $0f ; 400ms retry (default)
|
|
.byte $a0
|
|
.byte $08 ; # of timeouts
|
|
.byte $55 ; 4 sockets @2K each, tx/rx
|
|
.byte $55
|
|
|
|
; select one of the W5100 registers for subsequent read or write
|
|
; inputs: AX = register number to select
|
|
; outputs: none
|
|
w5100_select_register:
|
|
set_hi:
|
|
stx WIZNET_ADDR_HI
|
|
set_lo:
|
|
sta WIZNET_ADDR_LO
|
|
rts
|
|
|
|
; return which W5100 register the next read or write will access
|
|
; inputs: none
|
|
; outputs: AX = selected register number
|
|
w5100_get_current_register:
|
|
get_hi:
|
|
ldx WIZNET_ADDR_HI
|
|
get_lo:
|
|
lda WIZNET_ADDR_LO
|
|
rts
|
|
|
|
|
|
.data
|
|
|
|
next_eth_packet_byte:
|
|
lda $FFFF ; eth_packet
|
|
jmp advance_eth_ptr
|
|
|
|
eth_ptr_lo = next_eth_packet_byte+1
|
|
eth_ptr_hi = next_eth_packet_byte+2
|
|
|
|
|
|
; .bss
|
|
|
|
w5100_addr: .res 2
|
|
byte_ctr_lo: .res 1
|
|
byte_ctr_hi: .res 1
|
|
|
|
tx_wr_ptr: .res 2
|
|
rx_rd_ptr: .res 2
|
|
tcp_local_port: .res 2
|
|
|
|
tcp_state: .res 1
|
|
|
|
tcp_connect_ip: .res 4 ; ip address of remote server to connect to
|
|
tcp_callback: .res 2 ; vector to routine to be called when data is received over tcp connection
|
|
|
|
tcp_remote_port: .res 2 ; temp space for holding port to listen on or connect to
|
|
tcp_send_data_len: .res 2
|
|
tcp_send_data_ptr = eth_ptr_lo
|
|
|
|
tcp_inbound_data_length: .res 2
|
|
tcp_inbound_data_ptr: .res 2
|
|
|
|
tcp_connect_remote_port: .res 2
|
|
tcp_remote_ip = tcp_connect_ip
|
|
|
|
tcp_cxn_state_closed = 0
|
|
tcp_cxn_state_listening = 1 ; (waiting for an inbound SYN)
|
|
tcp_cxn_state_syn_sent = 2 ; (waiting for an inbound SYN/ACK)
|
|
tcp_cxn_state_established = 3
|
|
|
|
|
|
|
|
; -- LICENSE FOR w5100a.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 is Copyright (C) 2010
|
|
; Jonno Downes. All Rights Reserved.
|
|
; -- LICENSE END --
|