jb flags2.TCP_SOCK, :lastackTcp2 ; checkk which tcp conn
; is current
mov tcp1State, w
call @DeleteSocket1 ; delete the tcp conn. socket
jmp :dumpy
:lastackTcp2 mov tcp2State, w
call @DeleteSocket2 ; delete the tcp conn. socket
:dumpy _bank NIC_BANK ; needed for NICDumpRxFrame, we came from
; an upper bank
jmp @NICDumpRxFrame
:CLOSED call @NICDumpRxFrame
jmp @TCPSendReset ; we shouldn't receive packets
; while closed
:LISTEN call @NICDumpRxFrame ; discard received packet
jb flags2.TCP_SOCK, :listen2Check
bank TCB1_BANK
snb tcb1Flags.TCP_FLAG_RST ; check for an RST
retp ; ignore a packet with RST
snb tcb1Flags.TCP_FLAG_ACK ; check for an ACK
jmp @TCPSendReset ; bad ACK, send a RST
jmp :contListen
:listen2Check bank TCB2_BANK
snb tcb2Flags.TCP_FLAG_RST ; check for an RST
retp ; ignore a packet with RST
snb tcb2Flags.TCP_FLAG_ACK ; check for an ACK
jmp @TCPSendReset ; bad ACK, send a RST
:contListen call @TCPCopySeqToNxt
call @TCPSendSynAck
bank TCP_BANK
mov w, #TCP_ST_SYNRCVED ; change state
sb flags2.TCP_SOCK
mov tcp1State, w
snb flags2.TCP_SOCK
mov tcp2State, w
retp
:SYNSENT call @NICDumpRxFrame
jb flags2.TCP_SOCK, :synsentCheck2
bank TCB1_BANK
jnb tcb1Flags.TCP_FLAG_ACK, :noAck ; is the ACK bit set?
jb tcb1Flags.TCP_FLAG_RST, :rst ; is the reset bit set?
jnb tcb1Flags.TCP_FLAG_SYN, :noAck ; if SYN bit not set,
; ignore packet
jmp :contSynsent
:synsentCheck2 bank TCB2_BANK
jnb tcb2Flags.TCP_FLAG_ACK, :noAck ; is the ACK bit set?
jb tcb2Flags.TCP_FLAG_RST, :rst ; is the reset bit set?
jnb tcb2Flags.TCP_FLAG_SYN, :noAck ; if SYN bit not set,
; ignore packet
:contSynsent bank TCP_BANK
mov w, #TCP_ST_ESTABED ; the connection is now estabished
sb flags2.TCP_SOCK
mov tcp1State, w
snb flags2.TCP_SOCK
mov tcp2State, w
bank IP_BANK
clr ipLengthMSB ; set the received data length to 1
mov ipLengthLSB, #1 ; "
call @TCPAckUpdate
jmp @TCPSendAck
:rst bank TCP_BANK
mov w, #TCP_ST_CLOSED ; close the TCP
sb flags2.TCP_SOCK
mov tcp1State, w
snb flags2.TCP_SOCK
mov tcp2State, w
retp
; the peer wants us to raise the precedence. We can't.
; We are not happy about not being Acked. Send a Reset.
:noAck jmp @TCPSendReset
ORG $800 ; Page4
TCPRxHeader jmp _TCPRxHeader
TCPStartPktOut jmp _TCPStartPktOut
TCPTxByte jmp _TCPTxByte
TCPReTransmit jmp _TCPReTransmit
Compare4Inc jmp _Compare4Inc
TCPAppTxDone jmp _TCPAppTxDone
; ******************************************************************************
NICReadAgain_4
; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page4)
; ******************************************************************************
jmp @NICReadAgain
; ******************************************************************************
NICWriteAgain_4
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page4)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
NICDumpRxFrame_4
; Shortform for calling NICDumpRxFrame(), which is in Page1 (This is in Page4)
; ******************************************************************************
jmp @NICDumpRxFrame
; ******************************************************************************
TCPAddRcvNxt
; Add an 16-bit number to RCV.NXT
; INPUT: {ipLengthMSB,ipLengthLSB} = number to add
; OUTPUT: {tcb1RcvNxt1-4,tcb2RcvNxt1-4}
; ******************************************************************************
; check for which tcp connection to add
sb flags2.TCP_SOCK
mov globTemp1, #tcb1RcvNxt1 ; make pointer to TCB1_BANK
snb flags2.TCP_SOCK
mov globTemp1, #tcb2RcvNxt1 ; make pointer to TCB2_BANK
bank IP_BANK
mov w, ipLengthLSB
mov globTemp2, w ; store ipLengthLSB in globTemp2
mov fsr, globTemp1 ; point to TCB variable
add indf, globTemp2 ; add ipLengthLSB to tcbRcvNxt1
bank IP_BANK
mov w, ipLengthMSB
snc
mov w, ++ipLengthMSB
mov globTemp2, w ; store in globTemp2
dec globTemp1
mov fsr, globTemp1 ; point to TCB variable
add indf, globTemp2 ; add to tcb1(2)RcvNxt2
sc
retp
dec fsr
incsz indf ; tcb1(2)RcvNxt3
retp
dec fsr
inc indf ; tcb1(2)RcvNxt4
retp
; ******************************************************************************
TCPIncRcvNxt
; Increment RCV.NXT by one
; INPUT: none
; OUTPUT: {tcb1RcvNxt1-4,tcb2RcvNxt1-4}
; ******************************************************************************
; set pointer to correct TCB (TCB1_BANK for tcp1,
; TCB2_BANK for tcp2)
mov globTemp1, #(tcb1RcvNxt1-TCB1_BANK)
call @SetTCBPointer
incsz indf ; 1
retp
dec fsr ; 2
incsz indf
retp
dec fsr ; 3
incsz indf
retp
dec fsr ; 4
inc indf
retp
; ******************************************************************************
TCPIncSndUna
; Increment SND.UNA by one
; INPUT: none
; OUTPUT: {tcb1SndUna1-4,tcb2SndUna1-4}
; ******************************************************************************
; set pointer to correct TCB (TCB1_BANK for tcp1,
; TCB2_BANK for tcp2)
mov globTemp1, #(tcb1SndUna1-TCB1_BANK)
call @SetTCBPointer
incsz indf ; 1
retp
dec fsr ; 2
incsz indf
retp
dec fsr ; 3
incsz indf
retp
dec fsr ; 4
inc indf
retp
; ******************************************************************************
TCPCopySeqToNxt
; Copy {tcpTmpSeq4-1} -> {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1}
; INPUT: {tcpTmpSeq4-1}
; OUTPUT: {tcb1RcvNxt4-1,tcb2RcvNxt4-1}
; ******************************************************************************
sb flags2.TCP_SOCK
mov globTemp1, #tcb1RcvNxt4 ; make pointer to TCB1_BANK
snb flags2.TCP_SOCK
mov globTemp1, #tcb2RcvNxt4 ; make pointer to TCB2_BANK
mov globTemp3, #tcpTmpSeq4 ; make pointer to TCP BANK
call @Copy4Inc ; copy 4 TCP variables to TCB
retp ; by incrementing the pointers
; ******************************************************************************
TCPCopyAckToUna
; Copy {tcpTmpAck4-1} -> {tcb1SndUna4-1} or {tcb2SndUna4-1}
; INPUT: {tcpTmpAck4-1}
; OUTPUT: {tcb1SndUna4-1,{tcb2SndUna4-1}}
; ******************************************************************************
sb flags2.TCP_SOCK
mov globTemp1, #tcb1SndUna4 ; make pointer to TCB1_BANK
snb flags2.TCP_SOCK
mov globTemp1, #tcb2SndUna4 ; make pointer to TCB2_BANK
mov globTemp3, #tcpTmpAck4 ; make pointer to TCP BANK
call @Copy4Inc ; copy 4 TCP variables to TCB
retp
; ******************************************************************************
TCPAckUpdate
; Update SND.UNA and RCV.NXT
; INPUT: {tcpTmpAck4-1}
; {tcpTmpSeq4-1}
; {ipLengthMSB,ipLengthLSB} = length of received TCP Data
; OUTPUT: {tcpSndUna4-1}
; {tcpRcvNxt4-1}
; ******************************************************************************
call @TCPCopyAckToUna ; set SND.UNA = SEG.ACK
call @TCPCopySeqToNxt ; set RCV.NXT = SEG.SEQ
jmp @TCPAddRcvNxt ; add the length of the received
; packet to the ACK
; ******************************************************************************
TCPCmpNxtSeq
; Check if RCV.NXT == SEG.SEQ
; INPUT: {tcpTmpSeq4-1} = SEG.SEQ
; {tcb1RcvNxt4-1} = RCV.NXT or {tcb2RcvNxt4-1} = RCV.NXT
; OUTPUT: z is set if RCV.NXT == SEG.SEQ
; ******************************************************************************
sb flags2.TCP_SOCK
mov globTemp1, #tcb1RcvNxt1 ; make pointer to TCB1_BANK
snb flags2.TCP_SOCK
mov globTemp1, #tcb2RcvNxt1 ; make pointer to TCB2_BANK
mov globTemp3, #tcpTmpSeq1 ; make pointer to TCP BANK
bank IP_BANK
mov counter1, #4 ; load the counter
:loop
mov fsr, globTemp3 ; move tcp pointer to fsr
mov w, indf ; move tcp var in w
mov globTemp2, w ; store tcp var in global2
mov fsr, globTemp1 ; move tcb pointer to fsr
mov w, indf ; move tcb var into w
xor w, globTemp2 ; xor tcp var with tcb var
jnz :CmpEnd ; test if equal
dec globTemp1 ; dec pointer to tcb
dec globTemp3 ; dec pointer to tcp
_bank IP_BANK ; check loop counter until
; finished
dec counter1
sz
jmp :loop
:CmpEnd retp
; ******************************************************************************
TCPSendEmptyPkt
; Constructs and sends a TCP packet containing no Data
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1SndUna4-1} or {tcb2SndUna4-1} = sequence number
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; tcb1Flags or tcb2Flags = code flags
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
call @TCPCheckSumInit
bank TCP_BANK
clr tcpLengthMSB
clr tcpLengthLSB
call TCPStartPktOut
call @NICSendTxFrame
bank TIMER_BANK
; clear correct tcp connection's re-tx counters
jb flags2.TCP_SOCK, :clrTcp2timer
; tcp conn1
clr tcp1TimerMSB
clr tcp1TimerLSB
retp
; tcp conn2
:clrTcp2timer clr tcp2TimerMSB
clr tcp2TimerLSB
retp
; ******************************************************************************
TCPSendReset
; Send a reset packet with <SEQ=SEG.ACK><CTL=RST> and Discard the received
; packet
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
; point to correct tcb for current tcp connection
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, #(1<<TCP_FLAG_RST)
call @TCPCopyAckToUna ; copy the acknowledgement number.
call @TCPSendEmptyPkt
jmp NICDumpRxFrame_4 ; discard the received pkt
; ******************************************************************************
TCPSendSyn
; Send a SYN packet
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT: {tcpSndUna4-1} = new sequence number
; ******************************************************************************
; point to correct tcb for current tcp connection
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, #(1<<TCP_FLAG_SYN)
jmp TCPSendISN
; ******************************************************************************
TCPSendISN
; Send the TCP initial sequence number
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT: {tcpSndUna4-1} = new sequence number
; ******************************************************************************
; obtain a random number for starting sequence number
bank NIC_BANK
mov w, nicCurrPktPtr
xor w, nicRemoteEth0
bank IP_BANK
xor w, ipIdentLSB
mov globTemp2, w ; store
; point to the correct tcb for current tcp connection
mov globTemp1, #(tcb1SndUna4-TCB1_BANK)
call @SetTCBPointer
mov w, globTemp2 ; restore
mov indf, w ; 1
inc fsr
mov indf, w ; 2
inc fsr
mov indf, w ; 3
inc fsr
mov indf, w ; 4
call @TCPIncRcvNxt
call @TCPSendEmptyPkt
jmp @TCPIncSndUna
; ******************************************************************************
TCPSendSynAck
; Send an SYN-ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=SYN>
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT: {tcpSndUna4-1} = new sequence number
; ******************************************************************************
; point to correct tcb for current tcp connection
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, #((1<<TCP_FLAG_SYN)|(1<<TCP_FLAG_ACK))
jmp @TCPSendISN
; ******************************************************************************
TCPSendAck
; Send an ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> and Discard the
; received packet
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1SndUna4-1} or {tcb2SndUna4-1} = sequence number
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
; point to correct tcb for current tcp connection
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, #(1<<TCP_FLAG_ACK)
call @TCPSendEmptyPkt
jmp NICDumpRxFrame_4 ; discard the received pkt
; ******************************************************************************
TCPSendFin
; Send a FIN packet and discard the received packet
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcb1SndUna4-1} or {tcb2SndUna4-1} = sequence number
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; tcb1Flags or tcb2Flags = code flags
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
; point to correct tcb for current tcp connection
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, #(1<<TCP_FLAG_FIN)|(1<<TCP_FLAG_ACK)
call @TCPSendEmptyPkt
jmp NICDumpRxFrame_4 ; discard the received pkt
; ******************************************************************************
TCPCheckSumInit
; Clear TCP checksum value to prepare for new checksum calculation
; INPUT: none
; OUTPUT: {tcpCheckSumMSB,tcpCheckSumLSB}
; ******************************************************************************
bank TCP_BANK
clr tcpCheckSumMSB
clr tcpCheckSumLSB
clrb flags.TCP_CHKSUM_LSB ; next byte is MSB
retp
; ******************************************************************************
TCPCheckSumAcc
; Accumulate the TCP checksum. Checksum is computed by Doing the one's
; complement of the one's complement sum of 16-bit numbers
; INPUT: w = byte to accumulate
; flags.TCP_CHKSUM_LSB = set if processing LSB, clear if processing MSB
; OUTPUT: {tcpCheckSumMSB,tcpCheckSumLSB}
; ******************************************************************************
bank TCP_BANK
jnb flags.TCP_CHKSUM_LSB, :msb ; are we processing an MSB?
:lsb add tcpCheckSumLSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc tcpCheckSumMSB ; yes
snz
inc tcpCheckSumLSB
jmp :done
:msb add tcpCheckSumMSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc tcpCheckSumLSB ; yes, this time it is
; added to the LSB
snz
inc tcpCheckSumMSB
:done xor flags, #(1<<TCP_CHKSUM_LSB)
retp
; ******************************************************************************
TCPCheckSumAddHdr
; Add to the TCP checksum, the pseudo-header fields
; INPUT: {myIP0-3} = source IP addr
; {remoteIP0-3} = Destination IP addr
; {tcpLengthMSB,tcpLengthLSB} = length of TCP header and Data
; OUTPUT: {tcpCheckSumMSB,tcpCheckSumLSB}
; ******************************************************************************
bank TCP_BANK
; <TCP_length>
mov w, tcpLengthMSB
call TCPCheckSumAcc
mov w, tcpLengthLSB
call TCPCheckSumAcc
; <zero>,<protocol>
mov w, #0
call TCPCheckSumAcc
mov w, #6
call TCPCheckSumAcc
; <source_IP>
bank IP_BANK
mov w, myIP3
call TCPCheckSumAcc
bank IP_BANK
mov w, myIP2
call TCPCheckSumAcc
bank IP_BANK
mov w, myIP1
call TCPCheckSumAcc
bank IP_BANK
mov w, myIP0
call TCPCheckSumAcc
; <destination_IP>
bank IP_BANK
mov w, remoteIP3
call TCPCheckSumAcc
bank IP_BANK
mov w, remoteIP2
call TCPCheckSumAcc
bank IP_BANK
mov w, remoteIP1
call TCPCheckSumAcc
bank IP_BANK
mov w, remoteIP0
call TCPCheckSumAcc
retp
; ******************************************************************************
_TCPTxByte
; Transmit a TCP byte accumulating the checksum each time
; INPUT: w = byte to send
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w
call @TCPCheckSumAcc
mov w, globTemp1
call NICWriteAgain_4
retp
; ******************************************************************************
_TCPStartPktOut
; Constructs the TCP and IP headers
; INPUT: {remoteIP0-3} = Destination IP addr for TCP pkt
; {tcpLengthMSB,tcpLengthLSB} = length of TCP Data (just Data)
; {tcpCheckSumMSB,tcpCheckSumLSB} = TCP checksum computed over just Data
; {tcb1SndUna4-1} or {tcb2SndUna4-1} = sequence number
; {tcb1RcvNxt4-1} or {tcb2RcvNxt4-1} = acknowledgement number
; tcb1Flags or tcb2Flags = code flags
; {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP Source Port
; or {tcb2LocalPortMSB,tcb2LocalPortLSB} = TCP Source Port
; {tcb1RemotePortMSB,tcb1RemotePortLSB} = TCP Destination Port
; or {tcb2RemotePortMSB,tcb2RemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
; tcpLength += <TCP header length>
_bank TCP_BANK
mov w, #(TCP_HDR_LENGTH<<2) ; add in size of TCP hdr (20)
add tcpLengthLSB, w
snc
inc tcpLengthMSB ; tcpLength now is the length of
; TCP hdr and Data
; IP <total_length> = tcpLength + <IP header length>
mov w, #20 ; add in size of IP hdr (20)
add w, tcpLengthLSB
bank IP_BANK
mov ipLengthLSB, w
bank TCP_BANK
mov w, tcpLengthMSB
bank IP_BANK
mov ipLengthMSB, w
snc
inc ipLengthMSB
; update IP <identifier>
inc ipIdentLSB
snz
inc ipIdentMSB
; set IP <protocol> for TCP
mov ipProtocol, #6
; We should rather load remoteIP with tcp socketIP here, but Due
; to limited code space it is Done in IPGenCheckSum.
; compute IP <header_checksum>
call @IPGenCheckSum
; now we're ready to construct the IP header
call @IPStartPktOut
; then construct the TCP header
; point to correct tcb for current tcp connection
mov globTemp1, #(tcb1Offset-TCB1_BANK)
call @SetTCBPointer
; TCP <source_port>,<destination_port>,<sequence_number>,
; <acknowledgement_number>,<hlen>,<code>,<window>
mov indf, #(TCP_HDR_LENGTH<<4)
inc fsr
inc fsr
mov indf, #((TCP_WINDOW_SIZE&$FF00)>>8)
inc fsr
mov indf, #(TCP_WINDOW_SIZE&$00FF)
; make a pointer to the tcb for the current tcp connection
sb flags2.TCP_SOCK
mov globTemp3, #TCB1_BANK
snb flags2.TCP_SOCK
mov globTemp3, #TCB2_BANK
:loop mov fsr, globTemp3
mov w, indf ; load the value
call @TCPTxByte ; transmit and accumulate header
; checksum
inc globTemp3
jb flags2.TCP_SOCK, :check2
cse globTemp3, #TCB1_END ; is the loop finished?
jmp :loop
jmp :outloop
:check2 cse globTemp3, #TCB2_END ; is the loop finished?
jmp :loop
:outloop ; TCP <checksum>
call @TCPCheckSumAddHdr
bank TCP_BANK
mov w, /tcpCheckSumMSB
call NICWriteAgain_4
mov w, /tcpCheckSumLSB
call NICWriteAgain_4
; TCP <urgent_ptr>
mov w, #0
call NICWriteAgain_4
call NICWriteAgain_4
retp
; ******************************************************************************
_TCPRxHeader
; Process the TCP header of a received TCP packet
; INPUT: none
; OUTPUT: Z is set to 1 if the packet is invalid, 0 otherwise
; {tcb1RemotePortMSB,tcb1RemotePortLSB}
; or {tcb2RemotePortMSB,tcb2RemotePortLSB}
; {tcb1SendWinMSB,tcb1SendWinLSB} or {tcb2SendWinMSB,tcb2SendWinLSB}
; tcb1Offset or tcb2Offset
; tcb1Flags or tcb2Flags
; tcpRxFlags
; {tcpTmpSeq4-1} = <sequence_number>
; {tcpTmpAck4-1} = <acknowledgement_number>
; {tcpLengthMSB,tcpLengthLSB} = length of TCP Data
; {ipLengthMSB,ipLengthLSB} = length of TCP Data
; ******************************************************************************
bank TCPPORT_BANK
; <remote_port>
call NICReadAgain_4
mov tcpRemotePortMSB, w ; store remote port MSB
call NICReadAgain_4
mov tcpRemotePortLSB, w ; store remote port LSB
; <destination_port>
call NICReadAgain_4
mov tcpLocalPortMSB, w ; store dest port MSB
call NICReadAgain_4 ;
mov tcpLocalPortLSB, w ; store dest port LSB
call @TCPConnectionManager ; start the tcp conn/socket
; manager
snz ; is the packet OK?
retp ; no, return
; <sequence_number>
bank TCP_BANK
call NICReadAgain_4
mov tcpTmpSeq4, w
call NICReadAgain_4
mov tcpTmpSeq3, w
call NICReadAgain_4
mov tcpTmpSeq2, w
call NICReadAgain_4
mov tcpTmpSeq1, w
_bank TCPTMP_BANK
; <acknowledgement_number>
call NICReadAgain_4
mov tcpTmpAck4, w
call NICReadAgain_4
mov tcpTmpAck3, w
call NICReadAgain_4
mov tcpTmpAck2,w
call NICReadAgain_4
mov tcpTmpAck1, w
call NICReadAgain_4 ; receive the Data offset.
; Used to skip the options
and w, #TCP_OFFSET_MASK ; mask out the offset
mov globTemp2, w ; store w
; point to the correct tcb for the current tcp connection
mov globTemp1, #(tcb1Offset-TCB1_BANK)
call @SetTCBPointer
mov indf, globTemp2
clc
rr indf
rr indf
; ipLength = tcpLength = length of TCP Data
mov w, indf
_bank IP_BANK
sub ipLengthLSB, w ; subtract out size of TCP header
mov w, ipLengthLSB
bank TCP_BANK
mov tcpLengthLSB, w
bank IP_BANK
sc
dec ipLengthMSB
mov w, ipLengthMSB
bank TCP_BANK
mov tcpLengthMSB, w
; <code>
call NICReadAgain_4 ; receive the flags
mov tcpRxFlags, w
mov globTemp2, w
; point to the correct tcb for the current tcp connection
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, globTemp2
; <window>
call NICReadAgain_4
mov globTemp2, w
; point to the correct tcb for the current tcp connection
mov globTemp1, #(tcb1SendWinMSB-TCB1_BANK)
call @SetTCBPointer
mov indf, globTemp2 ; receive the window
call NICReadAgain_4
mov globTemp2, w
; point to the correct tcb for the current tcp connection
mov globTemp1, #(tcb1SendWinLSB-TCB1_BANK)
call @SetTCBPointer
mov indf, globTemp2
; point to the correct tcb for the current tcp connection
mov globTemp1, #(tcb1Offset-TCB1_BANK)
call @SetTCBPointer
sub indf, #((TCP_HDR_LENGTH<<2)-4)
mov w, indf
mov globTemp3, w
clr indf
:loop call NICReadAgain_4
decsz globTemp3
jmp :loop
clz
retp
; ******************************************************************************
_TCPReTransmit
; This is called to retransmit the TCP packet that's already setup in the NIC's
; TCP transmit buffer. Remember that a UDP/ICMP/ARP packet may have been sent
; after the TCP packet was initially sent, so we have to re-setup the NIC
; carefully.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; re-initialize the NIC's TPSR
bank NIC_BANK
clr nicIOAddr ; CR
:wait call @NICRead
jb wreg.2, :wait ; wait for prior transmission to
; complete
mov w, #%00100010 ; Page0, abort DMA
call @NICWrite
mov nicIOAddr, #$04 ; TPSR
; point to correct tcp tx buffer for current tcp connection
sb flags2.TCP_SOCK
mov w, #TXBUF2_START
snb flags2.TCP_SOCK
mov w, #TXBUF3_START
call @NICWrite
; read the NIC's TCP transmit buffer to find out the IP <length>
; so that we can re-initialize the NIC's TBCR
sb flags2.TCP_SOCK
mov w, #TXBUF2_START
snb flags2.TCP_SOCK
mov w, #TXBUF3_START
mov nicCopySrcMSB, w
mov nicCopySrcLSB, #(6+6+2+2) ; IP <length> (MSB)
call @NICBufRead
bank IP_BANK
mov ipLengthMSB, w
bank NIC_BANK
inc nicCopySrcLSB ; IP <length> (LSB)
call @NICBufRead
bank IP_BANK
mov ipLengthLSB, w
jmp @NICSendTxFrame ; re-transmit the ethernet frame
; ******************************************************************************
_Compare4Inc
; Compares 4 variables in Databanks
; INPUT: globTemp1 = pointer1, globTemp3 = pointer2
; OUTPUT: Z is set on a full match
; ******************************************************************************
bank IP_BANK
mov counter1, #4 ; load the counter
:loop
mov fsr, globTemp3 ; move pointer to fsr
mov w, indf ; move var in w
mov globTemp2, w ; store var in global2
mov fsr, globTemp1 ; move pointer to fsr
mov w, indf ; move var into w
xor w, globTemp2 ; xor tcp var with tcb var
jnz :CmpEnd ; test if equal
inc globTemp1 ; increment pointer
inc globTemp3 ; increment pointer
_bank IP_BANK ; check loop counter until finished
dec counter1
sz
jmp :loop
:CmpEnd retp
; ******************************************************************************
_TCPAppTxDone
; This is called following the last call to TCPAppTxData(). It signifies the
; transmitted Data has successfully reached the remote host
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; jump to current tcp connection code
jb flags2.TCP_SOCK, :TCPAppTxDone2
; tcp1
retp
; tcp2
:TCPAppTxDone2 retp
ORG $A00 ; Page5
TCPAppRxData jmp _TCPAppRxData
; ******************************************************************************
TCPAppRxBytes
; Indicator to the application that a packet has been received and that
; TCPAppRxByte is about to be called as many times as they are bytes of data
; [TCP API Function]
; INPUT: {tcpAppRxBytesMSB,tcpAppRxBytesLSB} = number of received Data bytes
; OUTPUT: none
; ******************************************************************************
; jump to current tcp connection code
jb flags2.TCP_SOCK, :TCPAppRxBytes2
; tcp1
IF DYNDNS
mov globTemp2, #15 ; set rcv byte counter
mov globTemp3, #DYNDNSReply ; set pointer to rcv buffer
ENDIF
retp
; tcp2
:TCPAppRxBytes2 retp
; ******************************************************************************
TCPAppTxBytes
; Called before transmitting a TCP packet to see if the application has any
; Data it wishes to send. The application cannot send more than TCP_SEG_SIZE
; bytes at one go.
; [TCP API Function]
; INPUT: none
; OUTPUT: {tcp1UnAckMSB,tcp1UnAckLSB}
; or {tcp2UnAckMSB,tcp2UnAckLSB} = number of bytes to transmit
; ******************************************************************************
; check if tcp1 or tcp2's chance to transmit
jb flags2.TCP_TXSEMA, :tcpConn2Tx
; tcp1
clrb flags2.TCP_SOCK ; indicate tcp1 conn.
cse tcp1State, #TCP_ST_ESTABED ; check if in
; established state
retp ; no, return
IF DYNDNS
sb flags2.DYNDNS_TXEN ; check if we may tx
retp ; no
clrb flags2.DYNDNS_TXEN ; yes, clear for next tx
_bank DYNDNS_BANK
; DYNDNS_CLOSE = 0 ; DYNDNS closing or closed
; DYNDNS_CONNECT = 1 ; state machine in connection establishment
; DYNDNS_GET = 2 ; GET command sent, waiting for good/!good reply
; DYNDNS_RPLY = 3 ; Got reply.
; DYNDNS_WAIT = 4 ; Got WAIT response, now delay.
; DYNDNS_STOP = 5 ; Got 911 reply, now stop.
cje DYNDNSState, #DYNDNS_GET, :DYNDNSSndGET ; chk if have to
; tx GET URL
; nothing cje DYNDNSState, #DYNDNS_RPLY, :DYNDNSWait ; chk if have to
; rx reply word
cje DYNDNSState, #DYNDNS_CLOSE, :DYNDNSClose ; chk if have to
; close
;retp ; nothing?
:DYNDNSSndGet mov DYNDNSStrPointer, #(DYNDNS_URL) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(DYNDNS_URL_END-DYNDNS_URL)
retp
:DYNDNSClose ; Will the remote host will initiate the close?
; retp
ENDIF
retp
; tcp2
:tcpConn2Tx setb flags2.TCP_SOCK ; indicate tcp2 conn.
cse tcp2State, #TCP_ST_ESTABED ; check if in
; established state
retp ; no, return
IF HTTP
snb flags2.BROWSER_TYPE ; do not send anything to netscape
; type browser on a post.
retp
bank HTTP_BANK
cje httpParseState, #2, :state2
cje httpParseState, #3, :state3
cje httpParseState, #4, :state4
retp
:state2 ; check how much there is to send
_bank EEPROM_BANK
csae e2FileLenMSB, #(HTTP_SEG_SIZE>>8)
jmp :lastSegment ; msb#1 < msb#2
cse e2FileLenMSB, #(HTTP_SEG_SIZE>>8)
jmp :notLast ; msb#1 > msb#2
csa e2FileLenLSB, #(HTTP_SEG_SIZE&$00FF)
jmp :lastSegment ; #1 <= #2
:notLast ; not the last segment so send as much as possible
; (i.e. full segment)
sub e2FileLenLSB, #(HTTP_SEG_SIZE&$00FF) ; e2FileLen
; -= HTTP_SEG_SIZE
sc ;
dec e2FileLenMSB ;
sub e2FileLenMSB, #(HTTP_SEG_SIZE>>8) ;
_bank TCP_BANK
mov tcp2UnAckMSB, #(HTTP_SEG_SIZE>>8)
mov tcp2UnAckLSB, #(HTTP_SEG_SIZE&$00FF)
retp
:lastSegment ; last segment so send whatever is leftover
mov w, e2FileLenMSB
_bank TCP_BANK
mov tcp2UnAckMSB, w
_bank EEPROM_BANK
mov w, e2FileLenLSB
_bank TCP_BANK
mov tcp2UnAckLSB, w
bank HTTP_BANK
inc httpParseState ; next-state = 3
retp
; no more to send so we close
:state3 call @TCPAppClose
retp
:state4
ENDIF
retp
; ******************************************************************************
TCPAppTxData
; This routine is called once for each byte the application has says it wishes
; to transmit.
; [TCP API Function]
; INPUT: none
; OUTPUT: w = Data byte to transmit
; ******************************************************************************
; jump to current tcp connection code
jb flags2.TCP_SOCK, :TCPAppTxData2
; tcp1
IF DYNDNS
_bank DYNDNS_BANK
cje DYNDNSState, #DYNDNS_GET, :DYNDNSTxGet ; chk if we have
; to tx GET URL
retp ; nothing?
; we have to send a GET URL <CRLF>
:DYNDNSTxGet mov w, #(DYNDNS_URL >> 8) ; upper nibble
jmp DYNDNSRdPrgMemData
ENDIF
retp
; tcp2
:TCPAppTxData2
IF HTTP
bank HTTP_BANK
cje httpURIHash, #URI1, :specialFile ; resource.htm
cje httpURIHash, #URI2, :specialFile ; temperature.htm
cje httpURIHash, #URI3, :specialFile2 ; postctrl.htm
call @E2Read8Ack
retp
:specialFile call @E2Read8Ack
mov globTemp1, w ; save temporarily
csb globTemp1, #$F0
jmp :match
mov w, globTemp1
retp
; look for magic number in file indicating Dynamic content
:specialFile2 setb flags3.LED_LOCK ; lock access to LED
call @E2Read8Ack
mov globTemp1, w ; save temporarily
csb globTemp1, #$F0
jmp :match2
mov w, globTemp1
retp
:match2 ; look if led is on or off now
jb LED_PORT.LED, :ledOffChar
; it is on, return last char of image for ledon.gif
mov w, #'n'
retp
; it is off, return last char of image for ledof.gif
:ledOffChar mov w, #'f'
retp
:match cjne globTemp1, #$F0, :subMatch ; 0xF0 is the magic number
:firstMatch bank HTTP_BANK
mov globTemp2, httpURIHash
mov fsr, #bcd3
cjne globTemp2, #URI1, :here
mov w, pageCount
inc pageCount
jmp :here1
:here _bank ADC_BANK
mov w, adc
:here1 call @Bin8ToBCD
mov w, bcd3+0
call @BCDToASCII
retp
:subMatch sub globTemp1, #$F0
_bank MISC_BANK
mov fsr, #bcd3
add fsr, globTemp1
mov w, indf
call @BCDToASCII
ENDIF
retp
; ******************************************************************************
_TCPAppRxData
; Called once for each byte received in a packet. Will only be called when
; tcpState is established.
; [TCP API Function]
; INPUT: w = received Data byte
; OUTPUT: none
; ******************************************************************************
; jump to current tcp connection code
jb flags2.TCP_SOCK, :TCPAppRxData2
; tcp1
IF DYNDNS
mov globTemp1, w ; store the rcvd byte
test globTemp2 ; check if we got the reply code,
; the rest is junk
snz
retp ; yes, we got it, just return on junk
; bytes
; receive the reply code
mov fsr, globTemp3 ; set pointer
mov indf, globTemp1 ; move rcvd byte to rcv buffer
inc globTemp3 ; advance pointer
dec globTemp2 ; receive until counter done
retp ; return to rcv some more bytes
ENDIF
retp
; tcp2
:TCPAppRxData2
IF HTTP
mov globTemp1, w
_bank HTTP_BANK
; check if this is 2nd packet from Netscape type browser, post
jb flags2.BROWSER_TYPE, :fieldSrch
; look if we got the method already
sb flags3.GOT_HTTP_METHOD
jmp :methodSrch1 ; no, go to method search
; yes, we have the method, jump to correct method parser
jb flags3.HTTP_METHOD, :postMethod
jmp :defaultMethod
:methodSrch1 cje globTemp1, #'P', :isPost ; is it a P ?
clrb flags3.HTTP_METHOD ; it's GET
setb flags3.GOT_HTTP_METHOD ; indicate we have the method
jmp :defaultMethod
:isPost setb flags3.HTTP_METHOD ; yes, so it must be POST or PUT
setb flags3.GOT_HTTP_METHOD ; indicate we have the method
setb flags3.LED_LOCK ; lock access to the LED
clr httpParseState
clr httpParseState2
retp
:postMethod ; have to get the requested resource now to set file pointer
jb flags3.GOT_URI, :fieldSrch ; check if we have to go
test httpParseState ; directly to field srch
jz :stateN0
cje httpParseState, #1, :stateN1
:stateN0 cse globTemp1, #' ' ; search for the space after the method
retp ; keyword
inc httpParseState ; got it, next-state = 1
retp
:stateN1 cje globTemp1, #' ', :gotURI ; make URI until space after
; requested resource
add httpURIHash, globTemp1
retp
; we have to search for the input field now. It's after 2 CRLFs
:fieldSrch cje httpParseState, #1, :gotCtrl
cje httpParseState, #2, :gotLf
cje httpParseState, #3, :gotBlkLine
cje httpParseState, #4, :gotField
csne globTemp1, #$0d ; check for first ctrl
inc httpParseState ; yes, got it
retp ; no
:gotCtrl cjne globTemp1, #$0a, :fldSrchRst
inc httpParseState ; yes, got it
retp ; no
:gotLf cjne globTemp1, #$0d, :fldSrchRst ; check for blank line
inc httpParseState ; yes, got to blank line
retp ; no
:gotBlkLine cjne globTemp1, #$0a, :fldSrchRst ; check for last char of
; blank line
inc httpParseState ; yes, we are at the field now
retp
:gotField cje httpParseState2, #1, :gotl
cje httpParseState2, #2, :gote
cje httpParseState2, #3, :gotd
cje httpParseState2, #4, :gotequal
csne globTemp1, #'l' ; look for 'l'
inc httpParseState2 ; yes, got it
retp ; no
:gotl csne globTemp1, #'e' ; look for 'e'
inc httpParseState2 ; yes, got it
retp ; no
:gote csne globTemp1, #'d' ; look for 'd'
inc httpParseState2 ; yes, got it
retp ; no
:gotd csne globTemp1, #'=' ; look for '='
inc httpParseState2 ; yes, got it
retp ; no
; now the next byte is the control byte for the led control
:gotequal cje globTemp1, #'1', :ledOn
cje globTemp1, #'0', :ledOff
cje globTemp1, #'t', :ledToggle
retp
:ledOn clrb LED_PORT.LED ; switch led on
retp
:ledOff setb LED_PORT.LED ; switch led off
retp
:ledToggle mov w, #(1<<LED) ; toggle led status
xor LED_PORT, w
retp
:fldSrchRst clr httpParseState ; no, gotta start over.
retp
:defaultMethod test httpParseState
jz :state0
cje httpParseState, #1, :state1
cjae httpParseState, #2, :state2
:state0 cse globTemp1, #' ' ; search for the space after the method
retp
inc httpParseState ; got it, next-state = 1
retp
:state1 cje globTemp1, #' ', :gotURI; make URI until space after
; requested resource
add httpURIHash, globTemp1
retp
:gotURI ; got the requested resource, obtain pointer to file
; check if coming from post method
jnb flags3.HTTP_METHOD, :gotURICont
; bit is set, so we came from post method
clr httpParseState ; clear for field parser
setb flags3.GOT_URI ; indicate we got URI for post field parser
:gotURICont mov w, httpURIHash
_bank EEPROM_BANK
mov e2AddrLSB, w
clr e2AddrMSB
clc ; e2Addr = httpURIHash * 2
rl e2AddrLSB ;
rl e2AddrMSB ;
call @E2Init ; reset EEPROM
call @E2SetAddr
call @E2SendRdCmd
call @E2Read8Ack
mov e2AddrMSB, w
call @E2Read8NoAckStop
mov e2AddrLSB, w
; start reading from file
call @E2SetAddr
call @E2SendRdCmd
; file length
call @E2Read8Ack
mov e2FileLenMSB, w
call @E2Read8Ack
mov e2FileLenLSB, w
; file checksum (ignore)
call @E2Read8Ack
call @E2Read8Ack
_bank HTTP_BANK
sb flags3.HTTP_METHOD ; do not increment if post
inc httpParseState ; next-state = 2
retp
:state2 ; here we receive bytes from after the requested resource
ENDIF
retp
IF DYNDNS
; ******************************************************************************
DYNDNSRdPrgMemData ; not relocatable. Must be in same page as caller
; Reads DYNDNS Data stored in program memory.
; INPUT: w contains upper nibble of prog memory location.
; DYNDNSPointer contains lower byte of prog mem location.
; OUTPUT: Data in w register
; ******************************************************************************
mov m, w ; into MODE register
mov w, DYNDNSStrPointer ; lower byte (2 nibbles)
inc DYNDNSStrPointer ; increment pointer
iread ; read data from prog memory into w register
retp
ENDIF
ORG $C00 ; Page6
TCPTransmit jmp _TCPTransmit
TCPAppRxDone jmp _TCPAppRxDone
TCPApp1Init jmp _TCPApp1Init
IF DYNDNS
DYNDNSCompareGood jmp _DYNDNSCompareGood
DYNDNSCompareWait jmp _DYNDNSCompareWait
DYNDNSCompare911 jmp _DYNDNSCompare911
DYNDNSCompare354 jmp _DYNDNSCompare354
ENDIF
; ******************************************************************************
SetTCBPointer
; Sets the fsr register to point to the correct TCB
; INPUT: globTemp1=offset into TCB1_BANK or TCB2_BANK
; OUTPUT: fsr contains pointer to variable in TCB bank
; ******************************************************************************
; check if pointer to be set for tcp1 or 2 connection.
jb flags2.TCP_SOCK, :tcb2pointer
; tcp1
add globTemp1, #TCB1_BANK
mov fsr, globTemp1 ; make pointer to TCB1_BANK for tcp1
retp
; tcp2
:tcb2pointer add globTemp1, #TCB2_BANK
mov fsr, globTemp1 ; make pointer to TCB2_BANK for tcp2
retp
; ******************************************************************************
CheckSocket1IP
; Checks if remoteIP is in tcp socket1
; INPUT: remoteIP3-0, sock1RemoteIP3-0
; OUTPUT: Z set if in there
; ******************************************************************************
mov globTemp1, #remoteIP3
mov globTemp3, #sock1RemoteIP3
call @Compare4Inc
retp
; ******************************************************************************
CheckSocket2IP
; Checks if remoteIP is in tcp socket2
; INPUT: remoteIP3-0, sock2RemoteIP3-0
; OUTPUT: Z set if in there
; ******************************************************************************
mov globTemp1, #remoteIP3
mov globTemp3, #sock2RemoteIP3
call @Compare4Inc
retp
; ******************************************************************************
CopyRemotePortTCB1
; Copies tcpRemotePortMSB,LSB into tcb1RemotePortMSB,LSB
; INPUT: tcpRemotePortMSB,LSB
; OUTPUT: tcb1RemotePortMSB,LSB
; ******************************************************************************
bank TCPPORT_BANK
mov w, tcpRemotePortMSB
bank TCB1_BANK
mov tcb1RemotePortMSB, w
bank TCPPORT_BANK
mov w, tcpRemotePortLSB
bank TCB1_BANK
mov tcb1RemotePortLSB, w
retp
; ******************************************************************************
CopyRemotePortTCB2
; Copies tcpRemotePortMSB,LSB into tcb2RemotePortMSB,LSB
; INPUT: tcpRemotePortMSB,LSB
; OUTPUT: tcb2RemotePortMSB,LSB
; ******************************************************************************
bank TCPPORT_BANK
mov w, tcpRemotePortMSB
bank TCB2_BANK
mov tcb2RemotePortMSB, w
bank TCPPORT_BANK
mov w, tcpRemotePortLSB
bank TCB2_BANK
mov tcb2RemotePortLSB, w
retp
; ******************************************************************************
CheckLocalPortTCB1
; Compares tcpLocalPortLSB, tcpLocalPortMSB against tcb1LocalPortLSB,
; tcb1LocalPortMSB
; INPUT: tcpLocalPortLSB, tcpLocalPortMSB, tcb1LocalPortLSB, tcb1LocalPortMSB
; OUTPUT: Z set if match
; ******************************************************************************
bank TCPPORT_BANK
mov w, tcpLocalPortMSB
bank TCB1_BANK
xor w, tcb1LocalPortMSB
sz
retp
mov w, tcb1LocalPortLSB
bank TCPPORT_BANK
xor w, tcpLocalPortLSB
retp
; ******************************************************************************
CheckLocalPortTCB2
; Compares tcpLocalPortLSB, tcpLocalPortMSB against tcb2LocalPortLSB,
; tcb2LocalPortMSB
; INPUT: tcpLocalPortLSB, tcpLocalPortMSB, tcb2LocalPortLSB, tcb2LocalPortMSB
; OUTPUT: Z set if match
; ******************************************************************************
bank TCPPORT_BANK
mov w, tcpLocalPortMSB
bank TCB2_BANK
xor w, tcb2LocalPortMSB
sz
retp
mov w, tcb2LocalPortLSB
bank TCPPORT_BANK
xor w, tcpLocalPortLSB
retp
; ******************************************************************************
TCPConnectionManager
; Manages TCP connections. Checks incoming tcp packets against listening ports,
; makes new sockets and indicates for which tcp connection the packet is.
; INPUT: none
; OUTPUT: flags2.TCP_SOCK
; ******************************************************************************
; check if we are waiting for an ARP response.
; have to Dump & ignore all incoming tcp packets until ARP
; response received or ARP timed out. The tcp receive functions
; can respond with sending tcp packets which will overwrite
; the stalled tcp packet. If we recorded which tcp conn. buffer
; has a stalled packet, we can accept tcp packets for the other
; tcp conn. but let's keep it simple now
jb flags3.ARP_REQ_SENT, :ignorePacket
; pre-screening to check if the local port=service is offered
call @CheckLocalPortTCB1 ; check if for a port
; we're listening on
jz :TCPSocket1 ; yes, service is bound
; to TCB1_BANK
call @CheckLocalPortTCB2 ; check if for a port
; we're listening on
jz :TCPSocket2 ; yes, service is bound
; to TCB2_BANK
jmp :ignorePacket ; no, we Don't offer the service
:TCPSocket1 call @CheckSocket1IP ; check if remote IP
; is in socket1
jnz :CreateTCPSocket1 ; no, make a new conn./socket
call @CheckRemotePortTCB1 ; yes, check if remote port
; is in TCB1_BANK
jz :TCPConnection1 ; yes, packet is for tcp conn. 1
jmp :CreateTCPSocket1 ; no, but make a new conn.
; if we're listening
:TCPSocket2 call @CheckSocket2IP ; check if remote IP
; is in socket2
jnz :CreateTCPSocket2 ; no, make a new conn./socket
call @CheckRemotePortTCB2 ; yes, check if remote port
; is in TCB2_BANK
jz :TCPConnection2 ; yes, packet is for tcp conn. 2
jmp :CreateTCPSocket2 ; no, but make a new conn.
; if we're listening
:CreateTCPSocket1
; we may only create a socket when tcp1 connection is in
; LISTEN state
_bank TCP_BANK
cjne tcp1State, #TCP_ST_LISTEN, :ignorePacket
; create tcp1 socket
call @CopyRemoteIPSocket1 ; store remote IP in socket1
call @CopyRemotePortTCB1 ; store remote port in socket1
jmp :TCPConnection1
:CreateTCPSocket2
; we may only create a socket when tcp2 connection is in
; LISTEN state
_bank TCP_BANK
cjne tcp2State, #TCP_ST_LISTEN, :ignorePacket
; create tcp2 socket
call @CopyRemoteIPSocket2 ; store remote IP in socket2
call @CopyRemotePortTCB2 ; store remote port in socket2
jmp :TCPConnection2
:TCPConnection1 clrb flags2.TCP_SOCK ; indicate tcp packet is for tcp1
clz ; indicate packet is ok
retp
:TCPConnection2 setb flags2.TCP_SOCK ; indicate tcp packet is for tcp2
clz ; indicate packet is ok
retp
:ignorePacket call @NICDumpRxFrame ; discard the tcp packet
stz ; indicate packet is not ok -
; don't process it
retp
; ******************************************************************************
_TCPTransmit
; See if the application has any Data to transmit. If there are no outstanding
; packets and the application has Data, then transmit a packet.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK ; _bank cus we called
; TCPAppInit priorly
; check which tcp connection has chance to tx
jb flags2.TCP_TXSEMA, :tcp2Tx
; tcp1 has tx chance
cjae tcp1State, #TCP_ST_ESTABED, :ok1 ; is tcp1
; connection estab?
retp ; exit, tcp1 connection
; not established
; yes, tcp1 is est.
:ok1 test tcp1UnAckMSB ; are there any bytes
; unacknowledged?
jnz :timeout1
test tcp1UnAckLSB
jnz :timeout1
cje tcp1State, #TCP_ST_FINWAIT1, :finTimeout ; check for
; FIN timeout
jmp :askAppTxData
; tcp2 has tx chance
:tcp2Tx cjae tcp2State, #TCP_ST_ESTABED, :ok2 ; is tcp2
; connection estab?
retp ; exit, tcp2 connection
; not established
; yes, tcp2 is est.
:ok2 test tcp2UnAckMSB ; are there any bytes
; unacknowledged?
jnz :timeout2
test tcp2UnAckLSB
jnz :timeout2
cje tcp2State, #TCP_ST_FINWAIT1, :finTimeout ; check for
; FIN timeout
:askAppTxData call @TCPAppTxBytes ; ask the application if
; it wants to tx
_bank TCP_BANK ; _bank cus we called
; TCPAppTxBytes priorly
; check if unack bytes from tcp1 or 2
jb flags2.TCP_SOCK, :chkAppTxDataCon2
; tcp1
mov w, tcp1UnAckMSB
or w, tcp1UnAckLSB
jnz :appHasTxData
retp ; nope, it Doesn't; so we're Done here then
; tcp2
:chkAppTxDataCon2
mov w, tcp2UnAckMSB
or w, tcp2UnAckLSB
jnz :appHasTxData
retp ; nope, it Doesn't; so we're Done here then
:appHasTxData ; start a TCP packet
; check if for tcp1 or 2
jb flags2.TCP_SOCK, :loadUnackTcp2
; tcp1
mov tcpLengthLSB, tcp1UnAckLSB ; tcpLength =
; # bytes to transmit
mov tcpLengthMSB, tcp1UnAckMSB
jmp :contAppHasTxData
; tcp2
; tcpLength = # bytes to transmit
:loadUnackTcp2 mov tcpLengthLSB, tcp2UnAckLSB
mov tcpLengthMSB, tcp2UnAckMSB
:contAppHasTxData
mov globTemp1, #(tcb1Flags-TCB1_BANK)
; set pointer to correct tcb for current tcp connection
call @SetTCBPointer
mov indf, #((1<TCP_FLAG_PSH)|(1<<TCP_FLAG_ACK))
call @TCPCheckSumInit
call @TCPStartPktOut ; send the header
; insert the packet Data while computing the checksum
; over the Data
bank TCP_BANK
; check if copy for tcp1 or 2
jb flags2.TCP_SOCK, :cpyUnAckTCP2
; tcp1
mov tcpTmpLSB, tcp1UnAckLSB
mov tcpTmpMSB, tcp1UnAckMSB
jmp :dataLoopBgn
; tcp2
:cpyUnAckTCP2 mov tcpTmpLSB, tcp2UnAckLSB
mov tcpTmpMSB, tcp2UnAckMSB
:dataLoopBgn inc tcpTmpMSB ; so that we can loop easier later
:dataLoop call @TCPAppTxData
_banky TCP_BANK ; cus we called TCPAppTxData
; priorly
call @NICWriteAgain ; which Doesn't clobber w,
; which is nice
call @TCPCheckSumAcc ; accumulate the checksum
decsz tcpTmpLSB
jmp :dataLoop
decsz tcpTmpMSB
jmp :dataLoop
; now go back and fill in the TCP checksum
bank NIC_BANK
; use correct tx buffer for current tcp connection
sb flags2.TCP_SOCK
mov w, #TXBUF2_START
snb flags2.TCP_SOCK
mov w, #TXBUF3_START
mov nicCopySrcMSB, w
mov nicCopySrcLSB, #(6+6+2+20+16) ; TCP <checksum> (MSB)
bank TCP_BANK
mov w, /tcpCheckSumMSB
call @NICBufWrite
bank NIC_BANK
inc nicCopySrcLSB
bank TCP_BANK
mov w, /tcpCheckSumLSB
call @NICBufWrite
call @NICSendTxFrame ; end and send the packet
bank TIMER_BANK ; initialise the restart timer
; check if tcp1 or 2 timer to be cleared
jb flags2.TCP_SOCK, :initTcp2Timer
; tcp1
clr tcp1TimerMSB
clr tcp1TimerLSB
retp
; tcp2
:initTcp2Timer clr tcp2TimerMSB
clr tcp2TimerLSB
retp
:finTimeout bank TIMER_BANK
; check if tcp1 or 2 timer to be checked
jb flags2.TCP_TXSEMA, :chkClrTcp2Tmr
; tcp1
csae tcp1TimerMSB, #TCP_RESTART_EXP ; has the restart timer
; expired?
retp ; no
clr tcp1TimerMSB ; yes, initialise the
; restart timer
clr tcp1TimerLSB
jmp @TCPSendFin
; tcp2
:chkClrTcp2Tmr csae tcp2TimerMSB, #TCP_RESTART_EXP ; has the timer expired?
retp ; no
clr tcp2TimerMSB ; yes, initialise the
; restart timer
clr tcp2TimerLSB
jmp @TCPSendFin
; check if timed out on waiting for ack on tcp connection1
:timeout1 bank TIMER_BANK
csae tcp1TimerMSB, #TCP_RESTART_EXP ; has the restart timer
; expired?
retp ; no
clr tcp1TimerMSB ; yes, initialise the
; restart timer
clr tcp1TimerLSB
clrb flags2.TCP_SOCK ; indicate conn1.
jmp @TCPReTransmit ; transmit the packet
; again
:timeout2 bank TIMER_BANK
csae tcp2TimerMSB, #TCP_RESTART_EXP ; has the restart timer
; expired?
retp ; no
clr tcp2TimerMSB ; yes, initialise the
; restart timer
clr tcp2TimerLSB
setb flags2.TCP_SOCK ; indicate conn2.
jmp @TCPReTransmit ; transmit the packet
; again
IF DYNDNS
; ******************************************************************************
_DYNDNSCompareGood
; Compares the DYNDNS reply code in memory against Good.
; INPUT: DYNDNSReplyCode
; OUTPUT: Z = set if reply code
; ******************************************************************************
_bank DYNDNSREPLY_BANK
mov w, #'g'
xor w, DYNDNSReplyCode
sz
retp
mov w, #'o'
xor w, DYNDNSReplyCode+1
sz
retp
mov w, #'o'
xor w, DYNDNSReplyCode+2
retp
; ******************************************************************************
_DYNDNSCompareWait
; Compares the DYNDNS reply code in memory against Wait.
; INPUT: DYNDNSReplyCode
; OUTPUT: Z = set if reply code
; ******************************************************************************
_bank DYNDNSREPLY_BANK
mov w, #'w'
xor w, DYNDNSReplyCode
sz
retp
mov w, #'a'
xor w, DYNDNSReplyCode+1
sz
retp
mov w, #'i'
xor w, DYNDNSReplyCode+2
retp
; ******************************************************************************
_DYNDNSCompare911
; Compares the DYNDNS reply code in memory against 911
; INPUT: DYNDNSReplyCode
; OUTPUT: Z = set if reply code is 911
; ******************************************************************************
_bank DYNDNSREPLY_BANK
mov w, #'9'
xor w, DYNDNSReplyCode
sz
retp
mov w, #'1'
xor w, DYNDNSReplyCode+1
sz
retp
mov w, #'1'
xor w, DYNDNSReplyCode+2
retp
; ******************************************************************************
_DYNDNSCompare354
; Compares the DYNDNS reply code in memory against 354
; INPUT: DYNDNSReplyCode
; OUTPUT: Z = set if reply code is 220
; ******************************************************************************
_bank DYNDNSREPLY_BANK
mov w, #'3'
xor w, DYNDNSReplyCode3
sz
retp
mov w, #'5'
xor w, DYNDNSReplyCode2
sz
retp
mov w, #'4'
xor w, DYNDNSReplyCode1
retp
ENDIF
; ******************************************************************************
_TCPAppRxDone
; This is called following the last call to TCPAppRxData(). It signifies the
; end of the received packet
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; jump to current tcp connection code
jb flags2.TCP_SOCK, :TCPAppRxDone2
; tcp1
IF DYNDNS
_bank DYNDNS_BANK
cje DYNDNSState, #DYNDNS_GET, :DYNDNSGet ; check if we
; sent a GETR URL
retp ; nothing?
; we have established a tcp conn with a remote DYNDNS
:DYNDNSGet
call @DYNDNSCompareGood ; check if we got Good.
; sz
; jmp :DYNDNSWeQuit ; no, we got something else
; we got a 220 service ready reply
; jmp :DYNDNSNxtState
; we have received a response to our HELO sent.
; We expect it to be 250
:DYNDNSNxtState _bank DYNDNS_BANK
inc DYNDNSState
setb flags2.DYNDNS_TXEN ; enable DYNDNS transmit so that
; a new command
; or text can be sent
ENDIF
retp
; tcp2
:TCPAppRxDone2
IF HTTP
; check if end of 2nd packet rcvd from Netscape type browser
_bank HTTP_BANK
jnb flags2.BROWSER_TYPE, :methodChk
clrb flags2.BROWSER_TYPE ; yes, clear flag
jmp :getM ; reset other flags
; check if we rcvd a packet with http post and did not get 'led'
; field meaning it came from a Netscape browser.
:methodChk jnb flags3.HTTP_METHOD, :getM ; skip chk if method get
; it is post. check if we got the 'led' field in this packet
cje httpParseState2, #4, :getM
setb flags2.BROWSER_TYPE ; no, netscape type browser
clr httpParseState ; clear for parser in 2nd pkt
:getM clrb flags3.GOT_HTTP_METHOD ; clear have method for nxt pkt
clrb flags3.GOT_URI
mov w, #2 ; restore the state of the
mov httpParseState, w ; httpParseState variable we
ENDIF ; borrowed for the POST parser
retp
; ******************************************************************************
_TCPApp1Init
; Called repeatedly as long as TCP connection1 state is closed
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
IF DYNDNS
;
; DYNDNS - Check if 8 byte timer has expired, or if button pressed
;
;
; check if this is the first time sw is pressed, or if it is
; still held in from the first time it was pressed.
; If email goes through before switch is released this will
; prevent another.
jnb flags2.SW_PRESSED, :firstPressChk
mov w, #(1<<SW) ; look if SW1 is pressed
and w, SW_PORT
snz ; no, so it must be released now
retp ; yes, have to wait until released
clrb flags2.SW_PRESSED
; check if the prev email was accepted by the remote DYNDNS, so we
; can try again.
jnb flags3.DYNDNS_OK, :sendMailAgain
; check here if we want to send email.
:firstPressChk mov w, #(1<<SW) ; look if SW1 is pressed
and w, SW_PORT
sz ; yes
retp ; no, just return
setb flags2.SW_PRESSED ; indicate switch is pressed
; yes, so we need to send an email now or again
:sendMailAgain clrb flags3.DYNDNS_OK ; clear the 'mail went through' indicator
; before we send it
_bank DYNDNS_BANK
mov w, #DYNDNS_CONNECT ; start DYNDNS state machine to connect
mov DYNDNSState, w
; set up local & remote ports for tcp1 connection
_bank TCB1_BANK
mov tcb1LocalPortLSB, #100
mov tcb1LocalPortMSB, #100
mov tcb1RemotePortLSB, #DYNDNS_PORT_LSB
mov tcb1RemotePortMSB, #DYNDNS_PORT_MSB
; fill in remote IP to connect with in tcp socket
_bank TCPSOCKET_BANK
mov sock1RemoteIP3, #DYNDNS_SERVER_IP3
mov sock1RemoteIP2, #DYNDNS_SERVER_IP2
mov sock1RemoteIP1, #DYNDNS_SERVER_IP1
mov sock1RemoteIP0, #DYNDNS_SERVER_IP0
; indicate tcp1 connection
clrb flags2.TCP_SOCK
jmp @TCPAppActiveOpen ; open a tcp connection on socket1
ENDIF
retp
ORG $E00 ; Page7
TCPApp2Init jmp _TCPApp2Init
DeleteSocket1 jmp _DeleteSocket1
DeleteSocket2 jmp _DeleteSocket2
IF HTTP
; jump table can be moved with all E2 functions if neccessary.
E2Delay600ns jmp _E2Delay600ns
E2Delay900ns jmp _E2Delay900ns
E2Delay1300ns jmp _E2Delay1300ns
E2SDAInput jmp _E2SDAInput
E2SDAOutputHi jmp _E2SDAOutputHi
E2SDAOutputLo jmp _E2SDAOutputLo
E2GenStartCond jmp _E2GenStartCond
E2GenStopCond jmp _E2GenStopCond
E2Write8 jmp _E2Write8
E2Read8 jmp _E2Read8
E2Read8Ack jmp _E2Read8Ack
E2Read8NoAckStop jmp _E2Read8NoAckStop
E2RecvAck jmp _E2RecvAck
E2SendAck jmp _E2SendAck
E2SendNotAck jmp _E2SendNotAck
E2SendRdCmd jmp _E2SendRdCmd
E2SendWrCmd jmp _E2SendWrCmd
E2SetAddr jmp _E2SetAddr
Bin8ToBCD jmp _Bin8ToBCD
BCDToASCII jmp _BCDToASCII
ENDIF
IF DHCP
DHCPREQUESTSend jmp _DHCPREQUESTSend
DHCPDISCOVERSend jmp _DHCPDISCOVERSend
DHCPSendCommon2 jmp _DHCPSendCommon2
UDPProcBcstPktIn jmp _UDPProcBcstPktIn
ENDIF
; ******************************************************************************
NICReadAgain_7
; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page7)
; ******************************************************************************
jmp @NICReadAgain
; ******************************************************************************
NICWriteAgain_7
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page7)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
NICDumpRxFrame_7
; Shortform for calling NICDumpRxFrame(), which is in Page1 (This is in Page7)
; ******************************************************************************
jmp @NICDumpRxFrame
; ******************************************************************************
UDPEndPktOut
; Wraps up and transmits the UDP packet
; [UDP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank NIC_BANK
jmp @NICSendTxFrameNow
; ******************************************************************************
UDPAppInit
; Application UDP Initialization code (Example)
; This function is called automatically once by the stack During startup
; [UDP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank UDP_BANK
mov udpRxDestPortMSB, #UDP_RX_DEST_MSB
mov udpRxDestPortLSB, #UDP_RX_DEST_LSB
retp
; ******************************************************************************
UDPAppProcPktIn
; Application Incoming UDP packet handler (Example)
; This function is called whenever an application (matches udpRxDestPortxSB)
; packet is received. The appplication can call NICReadAgain() to extract
; sequentially extract each byte of the <data> field in the UDP packet.
; [UDP API Function]
; INPUT: {udpRxDataLenMSB,udpRxDataLenLSB} = number of bytes in UDP <data>
; {udpRxSrcPortMSB,udpRxSrcPortLSB} = UDP <source_port>
; OUTPUT: none
; ******************************************************************************
call NICReadAgain_7
and w, #%01000000
xor ra, w ; toggle I/O pins
_bank UDP_BANK
clr udpTxSrcPortMSB
clr udpTxSrcPortLSB
mov udpTxDestPortMSB, udpRxSrcPortMSB
mov udpTxDestPortLSB, udpRxSrcPortLSB
clr udpTxDataLenMSB ; send 2 bytes of data
mov udpTxDataLenLSB, #2 ;
call UDPStartPktOut
mov w, ra ; send new port state
call NICWriteAgain_7
mov w, #$00 ; one-byte padding
call NICWriteAgain_7
jmp UDPEndPktOut
; ******************************************************************************
UDPStartPktOut
; Starts an outgoing UDP packet by constructing an IP and UDP packet header
; [UDP API Function]
; INPUT: {remoteIP0-3} = Destination IP addr for UDP pkt
; {udpTxSrcPortMSB,udpTxSrcPortLSB} = UDP Source Port
; {udpTxDestPortMSB,udpTxDestPortLSB} = UDP Destination Port
; {udpTxDataLenMSB,udpTxDataLenLSB} = UDP Data Length (just Data)
; OUTPUT: none
; ******************************************************************************
; compute IP <total_length>
_bank UDP_BANK
mov w, udpTxDataLenLSB
_bank IP_BANK
mov ipLengthLSB, w
_bank UDP_BANK
mov w, udpTxDataLenMSB
_bank IP_BANK
mov ipLengthMSB, w
add ipLengthLSB, #(20+8) ; add in size of UDP hdr (8)
; and IP hdr (20)
snc
inc ipLengthMSB
; update IP <identifier>
inc ipIdentLSB
snz
inc ipIdentMSB
; set IP <protocol> for UDP
mov ipProtocol, #17
; compute IP <header_checksum>
call @IPGenCheckSum
; now we're ready to construct the IP header
call @IPStartPktOut
; then construct the UDP header
_bank UDP_BANK
; UDP <source_port>
mov w, udpTxSrcPortMSB
call NICWriteAgain_7
mov w, udpTxSrcPortLSB
call NICWriteAgain_7
; UDP <destination_port>
mov w, udpTxDestPortMSB
call NICWriteAgain_7
mov w, udpTxDestPortLSB
call NICWriteAgain_7
; UDP <length>
mov w, #8
add w, udpTxDataLenLSB
mov w, udpTxDataLenMSB
snc
inc wreg
call NICWriteAgain_7
mov w, #8
add w, udpTxDataLenLSB
call NICWriteAgain_7
; UDP <checksum> = 0
mov w, #$0
call NICWriteAgain_7
jmp NICWriteAgain_7
; ******************************************************************************
UDPProcPktIn
; Processes an Incoming UDP packet
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: none
; ******************************************************************************
_bank UDP_BANK
; UDP <source_port>
call NICReadAgain_7
mov udpRxSrcPortMSB, w
call NICReadAgain_7
mov udpRxSrcPortLSB, w
; UDP <destination_port>
call NICReadAgain_7
xor w, udpRxDestPortMSB
jnz :outtaHere
call NICReadAgain_7
xor w, udpRxDestPortLSB
jnz :outtaHere
; UDP <message_length>
call NICReadAgain_7
mov udpRxDataLenMSB, w
call NICReadAgain_7
mov udpRxDataLenLSB, w
; ignore UDP <checksum>
REPT 2
call NICReadAgain_7
ENDR
; UDP <data>
IF DHCP
snb flags.RX_IS_IP_BCST
call UDPProcBcstPktIn
; prevent UDP API func call when in config
_bank DHCP_BANK
jb dhcpFlags.DHCP_CONFIG, :outtaHere
ENDIF
sb flags.RX_IS_IP_BCST
call UDPAppProcPktIn
:outtaHere _bank IP_BANK
jmp NICDumpRxFrame_7
IF DHCP
; ******************************************************************************
CheckIPLeaseExpire
; Checks if our IP lease has expired. If it has, renew it. If we never got a
; lease in the first place, just return.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
sb flags2.GOT_IP_LEASE ; test if we got an IP lease
retp ; return, we never got an IP lease
; we got an IP lease previously
; check if it is expiring. We're comparing seconds against
; seconds here
_bank DHCP_BANK
mov w, dhcpIPLeaseTm3
xor w, dhcpTimer3
jnz :outtaLeaseChk
mov w, dhcpIPLeaseTm2
xor w, dhcpTimer2
jnz :outtaLeaseChk
mov w, dhcpIPLeaseTm1
xor w, dhcpTimer1
jnz :outtaLeaseChk
mov w, dhcpIPLeaseTm0
xor w, dhcpTimer0
jnz :outtaLeaseChk
; Lease has expired, renew it
clrb flags2.GOT_IP_LEASE ; reset IP lease.
setb flags2.RENEW_IP_LEASE ; indicate this is a lease
; renewal
jmp DHCPConfig ; start the DHCP configuration
:outtaLeaseChk retp
; ******************************************************************************
DHCPConfig
; This function uses DHCP, Dynamic Host Configuration Protocol, to configure the
; iSX. The result is an assigned IP address for a certain time.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank DHCP_BANK
setb dhcpFlags.DHCP_CONFIG ; indicate DHCP config in progress
; initialize UDP receive port for DHCP
_bank UDP_BANK
clr udpRxDestPortMSB ; DHCP(BOOTP) Client (Port 68)
mov udpRxDestPortLSB, #68 ;
; DHCPConfig will be called again and again for IP lease
; renewals. Check if this is a renewal or a first entry from
; startup
jb flags2.RENEW_IP_LEASE, :leaseRenew
; Send a DHCP Discovery message
:dhcpDiscSend call DHCPDISCOVERSend ; send DHCPDISCOVER message
_bank DHCP_BANK
clr dhcpTimer0 ; initialize receive timeout timer
; Wait with timeout for an incoming frame
; on each timeout, a new Discovery will be sent until max tries
; reached
:dhcpWaitOffer call @NICWaitRxFrame ; check if we received something
jb flags2.GOT_RX_FRAME, :dhcpGotRxFrame ; check if we
; got something
_bank DHCP_BANK
cje dhcpTimer0, #DHCP_DISC_EXP, :outtaDHCP ; check if
; timeout - Disc
jmp :dhcpWaitOffer ; not timed out yet, wait some more
; Check if rcvd frame is UDP (DHCP carrier).
; If it is UDP, process contents, else go back to
; waiting for an incoming frame.
:dhcpGotRxFrame call @CheckIPDatagram
jnb flags.RX_IS_UDP, :dhcpWaitOffer
clrb flags.RX_IS_UDP
call UDPProcPktIn
sb flags.GOT_DHCP_OFFER
jmp :dhcpWaitOffer
; If we got to here, we rcvd a valid DHCP offer.
; Send a DHCP request message
:leaseRenew clr globTemp3 ; initialize re-transmit counter
:dhcpReqSend cje globTemp3, #DHCP_REQ_TRIES, :outtaDHCP ; tried enough?
_bank DHCP_BANK
clr dhcpTimer0
call DHCPREQUESTSend
inc globTemp3
; Wait with timeout for an incoming frame
; on each timeout, a new request will be sent
; until max tries reached
:dhcpWaitAck call @NICWaitRxFrame
jb flags2.GOT_RX_FRAME, :dhcpGotAck
_bank DHCP_BANK
cje dhcpTimer0, #DHCP_REQ_EXP, :dhcpReqSend
jmp :dhcpWaitAck ; wait some more
; Check if rcvd frame is UDP (DHCP carrier).
; If it is UDP, process contents, else go back to waiting
; for an incoming frame.
:dhcpGotAck call @CheckIPDatagram ; check packet type
jnb flags.RX_IS_UDP, :dhcpWaitAck ; if not UDP
; go back to waiting
clrb flags.RX_IS_UDP
call UDPProcPktIn ; is UDP, process contents
sb flags2.GOT_IP_LEASE ; check if we got an IP lease
jmp :dhcpWaitAck ; no wait some more
; reset 1sec renewal timers
_bank DHCP_BANK
clr dhcpBaseTimer0
clr dhcpBaseTimer1
clr dhcpTimer0
clr dhcpTimer1
clr dhcpTimer2
clr dhcpTimer3
:outtaDHCP clrb dhcpFlags.DHCP_CONFIG ; reset
call @UDPAppInit ; restore the user UDP application port
; check if successful, otherwise rebind
jb flags2.GOT_IP_LEASE, :dhcpConfigExit
jmp @Main ; rebind
; success, exit normally
:dhcpConfigExit clrb flags2.RENEW_IP_LEASE ; reset IP lease renewal
retp
; ******************************************************************************
DHCPSendCommon1
; Helper function for DHCPDISCOVERSend and DHCPREQUESTSend
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; set ethernet addr to broadcast addr
_bank NIC_BANK
mov w, #$FF
mov nicRemoteEth0, w
mov nicRemoteEth1, w
mov nicRemoteEth2, w
mov nicRemoteEth3, w
mov nicRemoteEth4, w
mov nicRemoteEth5, w
; set IP addr to broadcast addr
bank IP_BANK
mov w, #$FF
mov remoteIP3, w
mov remoteIP2, w
mov remoteIP1, w
mov remoteIP0, w
_bank UDP_BANK
clr udpTxSrcPortMSB ; DHCP client
mov udpTxSrcPortLSB, #68 ;
clr udpTxDestPortMSB ; DHCP server
mov udpTxDestPortLSB, #67 ;
call @UDPStartPktOut
; <op>
mov w, #1
call NICWriteAgain_7
; <htype>
mov w, #1
call NICWriteAgain_7
; <hlen>
mov w, #6
call NICWriteAgain_7
; <hops>
mov w, #0
call NICWriteAgain_7
; <transaction_id> = 0xABABABAB
mov w, #$AB
REPT 4
call NICWriteAgain_7
ENDR
; <seconds> = 256
mov w, #1
REPT 2
call NICWriteAgain_7
ENDR
; <flags>
mov w, #$80
call NICWriteAgain_7
mov w, #0
jmp NICWriteAgain_7
; ******************************************************************************
_DHCPSendCommon2
; Helper function for DHCPDISCOVERSend and DHCPREQUESTSend
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; <client_hw_addr>
mov w, #SX_ETH_ADDR0
call NICWriteAgain_7
mov w, #SX_ETH_ADDR1
call NICWriteAgain_7
mov w, #SX_ETH_ADDR2
call NICWriteAgain_7
mov w, #SX_ETH_ADDR3
call NICWriteAgain_7
mov w, #SX_ETH_ADDR4
call NICWriteAgain_7
mov w, #SX_ETH_ADDR5
call NICWriteAgain_7
; <client_hw_addr>,<server_host_name>,<boot_filename>
mov globTemp1, #(10+64+128)
mov w, #0
:loop2 call NICWriteAgain_7
decsz globTemp1
jmp :loop2
; <option_magic_cookie>
mov w, #99
call NICWriteAgain_7
mov w, #130
call NICWriteAgain_7
mov w, #83
call NICWriteAgain_7
mov w, #99
jmp NICWriteAgain_7
; ******************************************************************************
_DHCPDISCOVERSend
; Send DHCPDISCOVER message
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank UDP_BANK
clr udpTxDataLenMSB
mov udpTxDataLenLSB, #(240+3+0+1) ; without req-IP option
;mov udpTxDataLenLSB, #(240+3+6+1) ; with req-IP option
call DHCPSendCommon1
; <client_IP>, <your_IP>,<server_IP>,<router_IP> = 0
mov globTemp1, #(4+4+4+4)
mov w, #0
:loop1 call NICWriteAgain_7
decsz globTemp1
jmp :loop1
call DHCPSendCommon2
; <option-message_type>
mov w, #53
call NICWriteAgain_7
mov w, #1
call NICWriteAgain_7
mov w, #1 ; DHCPDISCOVER
call NICWriteAgain_7
; <option-requested_IP> -- optional
;mov w, #50
;call NICWriteAgain_7
;mov w, #4
;call NICWriteAgain_7
;mov w, #SX_IP_ADDR3
;call NICWriteAgain_7
;mov w, #SX_IP_ADDR2
;call NICWriteAgain_7
;mov w, #SX_IP_ADDR1
;call NICWriteAgain_7
;mov w, #SX_IP_ADDR0
;call NICWriteAgain_7
; <option-end_option> -- not optional
mov w, #255
call NICWriteAgain_7
; and ... that should Do it!
jmp UDPEndPktOut
; ******************************************************************************
_DHCPREQUESTSend
; Send DHCPREQUEST message
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank UDP_BANK
mov udpTxDataLenMSB, #((240+3+6+6+1)>>8)
mov udpTxDataLenLSB, #((240+3+6+6+1)&$FF)
call DHCPSendCommon1
; <client_IP>
_bank IP_BANK
mov w, myIP3
call NICWriteAgain_7
mov w, myIP2
call NICWriteAgain_7
mov w, myIP1
call NICWriteAgain_7
mov w, myIP0
call NICWriteAgain_7
; <your_IP>,<server_IP>,<router_IP> = 0
mov globTemp1, #(4+4+4)
mov w, #0
:loop1 call NICWriteAgain_7
decsz globTemp1
jmp :loop1
call DHCPSendCommon2
; <option-message_type>
mov w, #53
call NICWriteAgain_7
mov w, #1
call NICWriteAgain_7
mov w, #3 ; DHCPREQUEST
call NICWriteAgain_7
; <option-server_id>
mov w, #54 ; option server identifier
call NICWriteAgain_7
mov w, #4 ; length
call NICWriteAgain_7
_bank DHCP_BANK
mov w, dhcpServerId3
call NICWriteAgain_7
mov w, dhcpServerId2
call NICWriteAgain_7
mov w, dhcpServerId1
call NICWriteAgain_7
mov w, dhcpServerId0
call NICWriteAgain_7
; <option-requested_IP> -- not optional
mov w, #50
call NICWriteAgain_7
mov w, #4
call NICWriteAgain_7
_bank IP_BANK
mov w, myIP3
call NICWriteAgain_7
mov w, myIP2
call NICWriteAgain_7
mov w, myIP1
call NICWriteAgain_7
mov w, myIP0
call NICWriteAgain_7
; <option-end_option> -- not optional
mov w, #255
call NICWriteAgain_7
; and ... that should do it!
jmp UDPEndPktOut
; ******************************************************************************
_UDPProcBcstPktIn
; The only kind of broadcast UDP packets accepted are DHCP messages: DHCPOFFER
; and DHCPACK
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call NICReadAgain_7
xor w, #2 ; check <op> = BOOTP reply
jnz :outtaHere
call NICReadAgain_7
xor w, #1 ; check <htype> = 1
jnz :outtaHere
call NICReadAgain_7
xor w, #6 ; check <hlen> = 6
jnz :outtaHere
; ignore <hops>
call NICReadAgain_7
; check <transaction_id> = 0xABABABAB
REPT 4
call NICReadAgain_7
xor w, #$AB
jnz :outtaHere
ENDR
; ignore <seconds>, <flags>, <client_IP>
mov globTemp1, #(2+2+4)
:loop1 call NICReadAgain_7
decsz globTemp1
jmp :loop1
; record <your_IP>
_bank IP_BANK
call NICReadAgain_7
mov myIP3, w
call NICReadAgain_7
mov myIP2, w
call NICReadAgain_7
mov myIP1, w
call NICReadAgain_7
mov myIP0, w
; check if it is non-zero
mov w, myIP3
or w, myIP2
or w, myIP1
or w, myIP0
jz :outtaHere
; skip <server_IP>, <router_IP>, <client_hw_addr>,
; <sever_host_name>, <boot_filename>, <option_magic_cookie>
mov globTemp1, #(4+4+16+64+128+4)
:loop2 call @NICPseudoRead
decsz globTemp1
jmp :loop2
; <option-message_type>
call NICReadAgain_7
xor w, #53 ; DHCP Message Type
jnz :outtaHere
call NICReadAgain_7
xor w, #1 ; length
jnz :outtaHere
call NICReadAgain_7
xor w, #2 ; DHCPOFFER
snz
setb flags.GOT_DHCP_OFFER
xor w, #2 ; get back value in w
xor w, #5 ; DHCPACK
jnz :loop4
setb flags.GOT_IP_ADDR ; indicate we got
; assigned an IP addr
setb flags2.GOT_IP_LEASE
; now search for that Dang(!) <option-server_id>
:loop4 call NICReadAgain_7
xor w, #54 ; Server Identifier
jz :foundServId
xor w, #54 ; restore value
xor w, #58 ; check for T1 (renewal time)
jnz :more01 ; go to routine to read and discard
call NICReadAgain_7
xor w, #4 ; length
jnz :outtaHere
_bank DHCP_BANK ; record T1
call NICReadAgain_7
mov dhcpIPLeaseTm3, w
call NICReadAgain_7
mov dhcpIPLeaseTm2, w
call NICReadAgain_7
mov dhcpIPLeaseTm1, w
call NICReadAgain_7
mov dhcpIPLeaseTm0, w
jmp :loop4
:more01
call NICReadAgain_7 ; length
mov globTemp1, w
:loop3 call @NICPseudoRead ; read Data but ignore
decsz globTemp1 ; read as many times as length
jmp :loop3
jmp :loop4
:foundServId call NICReadAgain_7 ; ignore length
_bank DHCP_BANK
call NICReadAgain_7
mov dhcpServerId3, w
call NICReadAgain_7
mov dhcpServerId2, w
call NICReadAgain_7
mov dhcpServerId1, w
call NICReadAgain_7
mov dhcpServerId0, w
:outtaHere retp
ENDIF
IF HTTP
; ******************************************************************************
_E2Delay600ns
; Delay 600ns
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #6
:loop decsz wreg
jmp :loop
retp
; ******************************************************************************
_E2Delay900ns
; Delay 900ns
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #8
:loop decsz wreg
jmp :loop
retp
; ******************************************************************************
_E2Delay1300ns
; Delay 1300ns
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #13
:loop decsz wreg
jmp :loop
retp
; ******************************************************************************
_E2SDAInput
_E2SDAOutputHi
; Set SDA as input
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov !E2_PORT, #E2_DDR_SDA_IN
retp
; ******************************************************************************
_E2SDAOutputLo
; Set SDA as output-low
; INPUT: none
; OUTPUT: none
; ******************************************************************************
clrb E2SDA_PIN
mov !E2_PORT, #E2_DDR_SDA_OUT
retp
; ******************************************************************************
_E2GenStartCond
; Generate START condition
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2SDAOutputHi
setb E2SCL_PIN
call E2Delay600ns
call E2SDAOutputLo
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay600ns
retp
; ******************************************************************************
_E2GenStopCond
; Generate STOP condition
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2SDAOutputLo
setb E2SCL_PIN
call E2Delay600ns
call E2SDAOutputHi
call E2Delay1300ns
retp
; ******************************************************************************
_E2Write8
; Write 8 bits out the I2C bus
; INPUT: w = Data to write
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w ; Data buffer
mov globTemp2, #8 ; bit counter
:loop call E2Delay900ns
sb globTemp1.7
call E2SDAOutputLo
snb globTemp1.7
call E2SDAOutputHi
call E2Delay900ns
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
rl globTemp1
decsz globTemp2
jmp :loop
retp
; ******************************************************************************
_E2Read8
; Read 8 bits from the I2C bus
; INPUT: none
; OUTPUT: w = Data read
; ******************************************************************************
call E2SDAInput
mov globTemp2, #8 ; bit counter
:loop call E2Delay900ns
sb E2SDA_PIN
clc
snb E2SDA_PIN
stc
rl globTemp1
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
decsz globTemp2
jmp :loop
mov w, globTemp1
retp
; ******************************************************************************
_E2Read8Ack
; Read 8 bits from the I2C bus and send ACK
; INPUT: none
; OUTPUT: w = Data read
; ******************************************************************************
call E2Read8
mov globTemp1, w
call E2SendAck
mov w, globTemp1
retp
; ******************************************************************************
_E2Read8NoAckStop
; Read 8 bits from the I2C bus and send a no-ACK and stop-condition
; (terminates sequential read mode on EEPROM)
; INPUT: none
; OUTPUT: w = Data read
; ******************************************************************************
call E2Read8
mov globTemp1, w
call E2SendNotAck
call E2GenStopCond
mov w, globTemp1
retp
; ******************************************************************************
_E2RecvAck
; Receive ACK bit from I2C receiver
; INPUT: none
; OUTPUT: z: 1 = received ACK, 0 = Didn't receive ACK
; ******************************************************************************
call E2SDAInput
call E2Delay900ns
setb E2SCL_PIN
sb E2SDA_PIN
stz
snb E2SDA_PIN
clz
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
retp
; ******************************************************************************
_E2SendAck
; Send ACK bit as acknowledge
; INPUT: none
; OUTPUT: z: 1 = received ACK, 0 = Didn't receive ACK
; ******************************************************************************
call E2SDAOutputLo
call E2Delay900ns
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
retp
; ******************************************************************************
_E2SendNotAck
; Send ACK bit as not-acknowledge
; INPUT: none
; OUTPUT: z: 1 = received ACK, 0 = Didn't receive ACK
; ******************************************************************************
call E2SDAOutputHi
call E2Delay900ns
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
retp
; ******************************************************************************
_E2SendRdCmd
; Tell I2C Device we wish to read from it for this transaction
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2GenStartCond
mov w, #E2_CMD_RD
call E2Write8
call E2RecvAck
retp
; ******************************************************************************
_E2SendWrCmd
; Tell I2C Device we wish to write to it for this transaction
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2GenStartCond
mov w, #E2_CMD_WR
call E2Write8
call E2RecvAck
retp
; ******************************************************************************
_E2SetAddr
; Set address pointer
; INPUT: {e2AddrMSB, e2AddrLSB} = address to set to
; OUTPUT: none
; ******************************************************************************
call E2SendWrCmd
_bank EEPROM_BANK
mov w, e2AddrMSB
call E2Write8
call E2RecvAck
mov w, e2AddrLSB
call E2Write8
call E2RecvAck
retp
; ******************************************************************************
_Bin8ToBCD
; Converts 8-bit binary number to unpacked BCD
; INPUT: w = binary number to convert
; fsr = pointer to MSD (lowest addr) of a 3-byte buffer
; OUTPUT: [fsr] = unpacked BCD
; ******************************************************************************
clr indf
inc fsr
clr indf
inc fsr ; LSD
mov indf, w
:loopHun mov w, #100
mov w, indf-w
jnc :loopTen
mov indf, w
dec fsr
dec fsr ; MSD
inc indf
inc fsr
inc fsr ; LSD
jmp :loopHun
:loopTen mov w, #10
mov w, indf-w
sc
jmp :exit
mov indf, w
dec fsr
inc indf
inc fsr
jmp :loopTen
:exit retp
; ******************************************************************************
_BCDToASCII
; Converts an unpacked BCD number to an ASCII character
; INPUT: w = unpacked BCD
; OUTPUT: w = ASCII character
; ******************************************************************************
mov globTemp1, w
mov w, #'0'
add w, globTemp1
retp
ENDIF
; ******************************************************************************
_TCPApp2Init
; Called repeatedly as long as TCP connection2 state is closed
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; set up local port for tcp2 connection
IF HTTP
_bank TCB2_BANK
mov tcb2LocalPortLSB, #HTTP_PORT_LSB
mov tcb2LocalPortMSB, #HTTP_PORT_MSB
bank HTTP_BANK
clr httpParseState
clr httpURIHash
; indicate tcp2 connection
setb flags2.TCP_SOCK
jmp @TCPAppPassiveOpen
ENDIF
retp
; ******************************************************************************
_DeleteSocket1
; Deletes TCP socket1
; INPUT: none
; OUTPUT: sock1RemoteIP3-0
; ******************************************************************************
_bank TCPSOCKET_BANK
clr sock1RemoteIP3
clr sock1RemoteIP2
clr sock1RemoteIP1
clr sock1RemoteIP0
retp
; ******************************************************************************
_DeleteSocket2
; Deletes TCP socket1
; INPUT: none
; OUTPUT: sock2RemoteIP3-0
; ******************************************************************************
_bank TCPSOCKET_BANK
clr sock2RemoteIP3
clr sock2RemoteIP2
clr sock2RemoteIP1
clr sock2RemoteIP0
retp
; ***********
; *** END ***
; ***********
END