; isx_2_3_5.src
; Wing Poon, Deon Roelofse, Chris Waters . V2.3.5 . 1/9/01 . (C) Scenix, Inc.
; SX Ethernet TCP/IP Stack. Protocols implemented: ARP, DHCP, IP/ICMP, UDP,
; 2TCP, HTTP, SMTP
; ******************************************************************************
; NOTES:
; 1. Will work only on SX48/52 Production Release silicon!
; 2. If using SX-Key, you need SXKEY52.EXE V1.19 or greater. Pls go to
; the "DEMO DEFINES" section for important information!
; 3. If using SX-ISD, you need SASM.EXE V1.46 and SXIDE.EXE V1.07.03 or
; greater. Pls go to the "DEMO DEFINES" section for important
; information!
; 4. The schematics for the board that runs this code is available, pls
; refer to the CD-ROM or our website: http://www.scenix.com
; 5. There is a Java application program available to demonstrate the
; use of the UDP protocol to allow the user to control the SX, pls
; refer to the CD-ROM. You'll need the Microsoft Java Virtual Machine
; Build 3240, or greater (installed by Default with IE5).
; 6. Refer to the User's Guide for instructions on setting up your PC to
; communicate with the Scenix Ethernet Demo Board.
; 7. Refer to Scenix Application Note #37, and the Scenix SX-Stack User's
; Manual, for information on how to customize this code
; ******************************************************************************
; This program implements an embedded web-server and a send email client. It is
; written to run on the Scenix Ethernet Demo Board.
;
; The server will respond to HTTP requests and serves up the specified web
; resource stored on an external serial EEPROM. The IP address of the webserver
; is http://10.1.1.20
;
; The email client is capable of sending simple emails to a predefined email
; address. In the demo, an email is sent to "joe@demo.sx" whenever button SW2
; is pressed.
;
; There is a separate demo (requires the SX52 microcontroller to be re-
; programmed, using a third-party programming tool) that can be enabled to
; demonstrate support for DHCP. For this demo, the webserver (HTTP) will be
; disabled and only the email client will remain operational.
;
; The SX can also be controlled by the user, through a standard web broswer
; form.
;
; Finally, the SX can be pinged ("ping 10.1.1.20", or "ping <IP_address>"
; supplied by the DHCP server) using the standard PC/Unix ping utiility.
; ********************
; *** DEMO DEFINES ***
; ********************
SXKEY = 1 ; set to "1" if, and only if, using Parallax SX-Key tool
; ... otherwise set to "0"
SASM = 0 ; set to "1" if, and only if, using Advance Transdata or Nohau tool
; ... otherwise set to "0"
; choose Demo1 or Demo2 by setting DEMO to "1" or "2" in the section below:
; Demo1 - "Webserver and email client" (kit default when shipped)
; Demo2 - "Email client configured by DHCP (no webserver)"
DEMO = 1 ; uncomment for: Webserver and Email client
;DEMO = 2 ; uncoment for: Email client configured by DHCP (no webserver)
; fill in your SMTP server's IP address here (default 10.1.1.1) (both demos):
SMTP_SERVER_IP3 = 10
SMTP_SERVER_IP2 = 1
SMTP_SERVER_IP1 = 1
SMTP_SERVER_IP0 = 10
; locate the following functions in the code and customize them if you wish:
; _senderDomainName (default "sx")
; _mailFrom (default "sx")
; _mailTo (default "joe@demo.sx")
; _mailData (default "Button SW2 pressed")
; if you need to change the demo board's (webserver) IP address (default 10.1.1.20):
SX_IP_ADDR3 = 192 ; SX's static IP address (if DHCP Disabled, i.e. Demo1)
SX_IP_ADDR2 = 168 ; "
SX_IP_ADDR1 = 1 ; "
SX_IP_ADDR0 = 105 ; "
; if you need to change the demo board's MAC address (default 00-00-00-00-00-01):
SX_ETH_ADDR0 = 0 ; SX's Ethernet Phy MAC Address
SX_ETH_ADDR1 = 3 ; "
SX_ETH_ADDR2 = 0 ; "
SX_ETH_ADDR3 = 0 ; "
SX_ETH_ADDR4 = 0 ; "
SX_ETH_ADDR5 = 2 ; "
; INCLUDE "SX52.inc"
; SX52.inc
;*********************************************************************************
; SX48BD/52BD Mode addresses
; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
; exception of CMP and WKPND which do an exchange with W.
;*********************************************************************************
; Timer (read) addresses
TCPL_R = $00 ; Read Timer Capture register low byte
TCPH_R = $01 ; Read Timer Capture register high byte
TR2CML_R = $02 ; Read Timer R2 low byte
TR2CMH_R = $03 ; Read Timer R2 high byte
TR1CML_R = $04 ; Read Timer R1 low byte
TR1CMH_R = $05 ; Read Timer R1 high byte
TCNTB_R = $06 ; Read Timer control register B
TCNTA_R = $07 ; Read Timer control register A
; Exchange addresses
CMP = $08 ; Exchange Comparator enable/status register with W
WKPND = $09 ; Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_R = $0A ; Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R = $0B ; Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R = $0C ; Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R = $0D ; Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R = $0E ; Read Port Pull-up setup, 0 = enabled, 1 = disabled
DIR_R = $0F ; Read Port Direction
; Timer (write) addresses
TR2CML_W = $12 ; Write Timer R2 low byte
TR2CMH_W = $13 ; Write Timer R2 high byte
TR1CML_W = $14 ; Write Timer R1 low byte
TR1CMH_W = $15 ; Write Timer R1 high byte
TCNTB_W = $16 ; Write Timer control register B
TCNTA_W = $17 ; Write Timer control register A
; Port setup (write) addresses
WKED_W = $1A ; Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W = $1B ; Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W = $1C ; Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W = $1D ; Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W = $1E ; Write Port Pull-up setup, 0 = enabled, 1 = disabled
DIR_W = $1F ; Write Port Direction
;*********************************************************************************
; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
;*********************************************************************************
RTCC_ON = %10000000 ; Enables RTCC at address $01 (RTW hi)
; WREG at address $01 (RTW lo) by default
RTCC_ID = %01000000 ; Disables RTCC edge interrupt (RTE_IE hi)
; RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT = %00100000 ; Sets RTCC increment on RTCC pin transition (RTS hi)
; RTCC increment on internal instruction (RTS lo) is defalut
RTCC_FE = %00010000 ; Sets RTCC to increment on falling edge (RTE_ES hi)
; RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_OFF = %00001000 ; Assigns prescaler to Watchdog (PSA hi)
PS_000 = %00000000 ; RTCC = 1:2, WDT = 1:1
PS_001 = %00000001 ; RTCC = 1:4, WDT = 1:2
PS_010 = %00000010 ; RTCC = 1:8, WDT = 1:4
PS_011 = %00000011 ; RTCC = 1:16, WDT = 1:8
PS_100 = %00000100 ; RTCC = 1:32, WDT = 1:16
PS_101 = %00000101 ; RTCC = 1:64, WDT = 1:32
PS_110 = %00000110 ; RTCC = 1:128, WDT = 1:64
PS_111 = %00000111 ; RTCC = 1:256, WDT = 1:128
; ***************************
; *** CONDITIONAL DEFINES ***
; ***************************
IF DEMO = 1 ; webserver and email client IP address will be 10.1.1.20
DHCP = 0
HTTP = 1
SMTP = 1
ENDIF
IF DEMO = 2 ; email client IP address will be as assigned by remote DHCP server
DHCP = 1
HTTP = 0
SMTP = 1
ENDIF
; **************
; *** DEVICE ***
; **************
IF SXKEY
; Parallax -- if, and only if, using Parallax's SX-Key
DEVICE OSCHS2, DRT60MS
FREQ 48000000 ; have to Debug at freq != resonant freq
ENDIF
IF SASM
; SASM -- if, and only if, using Advance Transdata's SX-ISD
DEVICE SX52BD, OSCHS2, WDRT60
ENDIF
RESET Main
ID 'isx_235'
; *****************
; *** VARIABLES ***
; *****************
; *** Global ***
GLOBAL_ORG = $0A
flags EQU GLOBAL_ORG+0 ; various flags used by TCP/IP stack
flags2 EQU GLOBAL_ORG+1 ; various flags used by TCP/IP stack
flags3 EQU GLOBAL_ORG+2 ; various flags used by TCP/IP stack
globTemp1 EQU GLOBAL_ORG+3 ; not preserved across any function
globTemp2 EQU GLOBAL_ORG+4 ; not preserved across anyfunction
globTemp3 EQU GLOBAL_ORG+5 ; preserved across some functions
; *** Bank 0 ***
; (Don't use this bank - Difficulties with addressing Data)
ORG $00
; *** Bank 1 ***
ORG $10
NIC_BANK = $
nicIOAddr DS 1 ; points to currently addressed register on NIC
nicNextPktPtr DS 1 ; points to next packet in NIC's rx queue
nicCurrPktPtr DS 1 ; points to current packet in NIC's rx queue
nicRemoteEth0 DS 1 ; ethernet addr used for outgoing packet, overwritten by incoming packet
nicRemoteEth1 DS 1 ; "
nicRemoteEth2 DS 1 ; "
nicRemoteEth3 DS 1 ; "
nicRemoteEth4 DS 1 ; "
nicRemoteEth5 DS 1 ; "
nicCopySrcMSB DS 1 ; used by NICBufferCopy()
nicCopySrcLSB DS 1 ; "
nicCopyDestMSB DS 1 ; "
nicCopyDestLSB DS 1 ; "
nicCopyLenMSB DS 1 ; "
nicCopyLenLSB DS 1 ; "
nicCopyTemp DS 1 ; "
; *** Bank 2 ***
ORG $20
IP_BANK = $ ; make sure IP_BANK[7] = NIC_BANK[7]
remoteIP3 DS 1 ; IP addr used for outgoing packet, overwritten by incoming packet
remoteIP2 DS 1 ; "
remoteIP1 DS 1 ; "
remoteIP0 DS 1 ; "
myIP3 DS 1 ; filter value for incoming IP packets, also used in outgoing packet
myIP2 DS 1 ; "
myIP1 DS 1 ; "
myIP0 DS 1 ; "
ipCheckSumMSB DS 1 ; IP <header_checksum>
ipCheckSumLSB DS 1 ; "
ipLengthMSB DS 1 ; IP <length>
ipLengthLSB DS 1 ; "
ipProtocol DS 1 ; IP <protocol>
ipIdentMSB DS 1 ; IP <identifier>, incremented each outgoing packet
ipIdentLSB DS 1 ; "
counter1 DS 1 ; general purpose counter variable
; *** Bank 3 ***
ORG $30
TCB1_BANK = $ ; make sure TCB1_BANK[7] = NIC_BANK[7]
; TCB1 is bound to tcp connection1 indicated by flags2.TCP_SOCK cleared
; The ordering of these variables is significant. It is the same as the TCP
; header. This simpifies the send-packet code
tcb1LocalPortMSB DS 1 ; source port - tcp conn1
tcb1LocalPortLSB DS 1 ; "
tcb1RemotePortMSB DS 1 ; destination port - tcp conn1
tcb1RemotePortLSB DS 1 ; "
tcb1SndUna4 DS 1 ; SND.UNA: oldest unacknowledged byte - tcp conn1
tcb1SndUna3 DS 1 ; "
tcb1SndUna2 DS 1 ; "
tcb1SndUna1 DS 1 ; "
tcb1RcvNxt4 DS 1 ; RCV.NXT: next byte to receive - tcp conn1
tcb1RcvNxt3 DS 1 ; "
tcb1RcvNxt2 DS 1 ; "
tcb1RcvNxt1 DS 1 ; "
tcb1Offset DS 1 ; length of the TCP options
tcb1Flags DS 1 ; flags field
tcb1SendWinMSB DS 1 ; send window
tcb1SendWinLSB DS 1 ; "
TCB1_END = $
; *** Bank 4 ***
ORG $40
TCB2_BANK = $ ; make sure TCB2_BANK[7] = NIC_BANK[7]
; TCB2 is bound to tcp connection2 indicated by flags2.TCP_SOCK set
; The ordering of these variables is significant. It is the same as the TCP
; header. This simpifies the send-packet code
tcb2LocalPortMSB DS 1 ; source port - tcp conn2
tcb2LocalPortLSB DS 1 ; "
tcb2RemotePortMSB DS 1 ; destination port - tcp conn2
tcb2RemotePortLSB DS 1 ; "
tcb2SndUna4 DS 1 ; SND.UNA: oldest unacknowledged byte - tcp conn2
tcb2SndUna3 DS 1 ; "
tcb2SndUna2 DS 1 ; "
tcb2SndUna1 DS 1 ; "
tcb2RcvNxt4 DS 1 ; RCV.NXT: next byte to receive - tcp conn2
tcb2RcvNxt3 DS 1 ; "
tcb2RcvNxt2 DS 1 ; "
tcb2RcvNxt1 DS 1 ; "
tcb2Offset DS 1 ; length of the TCP options
tcb2Flags DS 1 ; flags field
tcb2SendWinMSB DS 1 ; send window
tcb2SendWinLSB DS 1 ; "
TCB2_END = $
; *** Bank 5 ***
ORG $50
TCP_BANK = $ ; make sure TCP_BANK[7] = NIC_BANK[7]
tcp1State DS 1 ; tcp connection1 state-machine state
tcp2State DS 1 ; tcp connection2 state-machine state
tcpTmpSeq4 DS 1 ; TMP.SEQ. 1=LSB, 4=MSB
tcpTmpSeq3 DS 1 ; temporary information from the received packet
tcpTmpSeq2 DS 1
tcpTmpSeq1 DS 1
tcp1UnAckMSB DS 1 ; number of unacknowledged bytes for tcp connection1
tcp1UnAckLSB DS 1 ; "
tcp2UnAckMSB DS 1 ; number of unacknowledged bytes for tcp connection2
tcp2UnAckLSB DS 1 ; "
tcpRxFlags DS 1 ; copy of the received flags field
tcpCheckSumMSB DS 1
tcpCheckSumLSB DS 1
tcpLengthMSB DS 1
tcpLengthLSB DS 1
tcpTmpMSB = tcpTmpSeq4
tcpTmpLSB = tcpTmpSeq3
tcpAppTxBytesMSB = tcp1UnAckMSB ; number of bytes app wants to transmit
tcpAppTxBytesLSB = tcp1UnAckLSB ; "
tcpAppTxBytesMSB = tcp2UnAckMSB ; number of bytes app wants to transmit
tcpAppTxBytesLSB = tcp2UnAckLSB ; "
tcpAppRxBytesMSB = tcpLengthMSB ; number of bytes app will be receiving
tcpAppRxBytesLSB = tcpLengthLSB ; "
; *** Bank 6 ***
ORG $60
ARP_BANK = $ ; make sure ARP_BANK[7] = NIC_BANK[7]
host1IP3 DS 1 ; remote host1 IP address
host1IP2 DS 1 ; "
host1IP1 DS 1 ; "
host1IP0 DS 1 ; "
host1Eth0 DS 1 ; remote host1 Ethernet address
host1Eth1 DS 1 ; "
host1Eth2 DS 1 ; "
host1Eth3 DS 1 ; "
host1Eth4 DS 1 ; "
host1Eth5 DS 1 ; "
stPktTxBufStart DS 1 ; start address of stalled packet in NIC tx buffer
TCPPORT_BANK = $
; Incoming tcp packet ports
tcpRemotePortMSB DS 1
tcpRemotePortLSB DS 1
tcpLocalPortMSB DS 1
tcpLocalPortLSB DS 1
; *** Bank 7 ***
ORG $70
TIMER_BANK = $ ; make sure TIMER_BANK[7] = NIC_BANK[7]
baseTimer DS 1 ; lowest/common cog in timer chain
arpTimerMSB DS 1 ; ARP-timer count
arpTimerLSB DS 1 ; "
tcp1TimerMSB DS 1 ; tcp1 re-transmission timer count
tcp1TimerLSB DS 1 ; "
tcp2TimerMSB DS 1 ; tcp2 re-transmission timer count
tcp2TimerLSB DS 1 ; "
conn1TimerMSB DS 1 ; tcp1 timeout timer count
conn1TimerLSB DS 1 ; "
conn2TimerMSB DS 1 ; tcp2 timeout timer count
conn2TimerLSB DS 1 ; "
IF HTTP
HTTP_BANK = $ ; make sure HTTP_BANK[7] = NIC_BANK[7]
httpParseState DS 1 ; state of the HTTP header parser
httpURIHash DS 1 ; hash of the current URI
httpParseState2 DS 1 ; state of the HTTP message parser
ENDIF
IF SMTP
SMTP_BANK = $
smtpState DS 1 ; state of the SMTP state machine
smtpStrPointer DS 1 ; SMTP string pointer
ENDIF
; *** Bank 8 ***
ORG $80
MISC_BANK = $
; Watch out for this when moving Databanks! This is the only place where more
; than 1 byte is reserved for Data and it's not so obvious!
bcd3 DS 3 ; buffer for binary-to-ascii conversion
pageCount DS 1 ; num times resource.htm page has been accessed
ADC_BANK = $ ; must be same bank as bcd3
adc DS 1 ; averaged ADC value
adcAcc DS 1
adcCount DS 1
adcMSB DS 1 ; for averaging 256 samples
adcLSB DS 1 ; "
adcSampleCount DS 1 ; count number of averaged samples
EEPROM_BANK = $ ; make sure EEPROM_BANK[7] = NIC_BANK[7]
e2AddrMSB DS 1 ; address in EEPROM to start reading from
e2AddrLSB DS 1 ; "
e2FileLenMSB DS 1 ; length of the file being read
e2FileLenLSB DS 1 ; "
; *** Bank 9 ***
ORG $90
IF DHCP
DHCP_BANK = $
dhcpServerId3 DS 1 ; DHCP <server_identifier> = IP addr of DHCP server
dhcpServerId2 DS 1 ; "
dhcpServerId1 DS 1 ; "
dhcpServerId0 DS 1 ; "
dhcpIPLeaseTm3 DS 1 ; IP lease time offered by the DHCP server
dhcpIPLeaseTm2 DS 1 ; "
dhcpIPLeaseTm1 DS 1 ; "
dhcpIPLeaseTm0 DS 1 ; "
dhcpBaseTimer1 DS 1 ; DHCP base timer for renewals
dhcpBaseTimer0 DS 1 ; "
dhcpTimer3 DS 1 ; DHCP timer for renewals
dhcpTimer2 DS 1 ; "
dhcpTimer1 DS 1 ; "
dhcpTimer0 DS 1 ; "
dhcpFlags DS 1 ; flags
; dhcpFlags
DHCP_CONFIG = 0
ENDIF
; *** Bank A ***
ORG $A0
UDP_BANK = $
udpRxSrcPortMSB DS 1
udpRxSrcPortLSB DS 1
udpRxDestPortMSB DS 1 ; filter value for incoming UDP packets
udpRxDestPortLSB DS 1 ; "
udpRxDataLenMSB DS 1 ; length of <data> field of incoming UDP packet
udpRxDataLenLSB DS 1 ; "
udpTxSrcPortMSB DS 1
udpTxSrcPortLSB DS 1
udpTxDestPortMSB DS 1
udpTxDestPortLSB DS 1
udpTxDataLenMSB DS 1 ; length of <data> field of outgoing UDP packet
udpTxDataLenLSB DS 1 ; "
TCPTMP_BANK = $ ; stores temporary tcp info
tcpTmpAck4 DS 1 ; TMP.ACK
tcpTmpAck3 DS 1 ; temporary information from the received packet
tcpTmpAck2 DS 1
tcpTmpAck1 DS 1
; *** Bank B ***
ORG $B0
TCPSOCKET_BANK = $ ; contains the 2 TCP sockets
; socket1 - will always be bound to TCB1_BANK
sock1RemoteIP3 DS 1
sock1RemoteIP2 DS 1
sock1RemoteIP1 DS 1
sock1RemoteIP0 DS 1
sock1RemotePortMSB DS 1
sock1RemotePortLSB DS 1
; socket2 - will always be bound to TCB2_BANK
sock2RemoteIP3 DS 1
sock2RemoteIP2 DS 1
sock2RemoteIP1 DS 1
sock2RemoteIP0 DS 1
sock2RemotePortMSB DS 1
sock2RemotePortLSB DS 1
IF SMTP
SMTPREPLY_BANK = $
smtpReplyCode3 DS 1 ; SMTP reply code received from remote SMTP
smtpReplyCode2 DS 1 ; "
smtpReplyCode1 DS 1 ; "
ENDIF
; *** Bank C ***
ORG $C0
; *** Bank D ***
ORG $D0
; *** Bank E ***
ORG $E0
; *** Bank F ***
ORG $F0
; ***************
; *** EQUATES ***
; ***************
INT_PERIOD = 145 ; RTCC interrupt periodicity (345kHz)
; change this if you're not clocking the SX at 50MHz
; *** Pin Definitions ***
RA_DIR = %10101010
RA_OUT = %01110101
RA_LVL = %11111111
RA_PLP = %01111111
RB_DIR = %10000000
RB_OUT = %01100000
RB_LVL = %11111111
RB_PLP = %01111111
RC_DIR = %11111111
RC_OUT = %00000000
RC_LVL = %11111111
RC_PLP = %11111111
RD_DIR = %11111111
RD_OUT = %00000000
RD_LVL = %11111111
RD_PLP = %00000000
RE_DIR = %01111111
RE_OUT = %00000000
RE_LVL = %00111111
RE_PLP = %11000000
NIC_DATA_PORT = rc
NIC_CTRL_PORT = rb
IOWB_PIN = NIC_CTRL_PORT.5
IORB_PIN = NIC_CTRL_PORT.6
IOCH_PIN = NIC_CTRL_PORT.7
E2_PORT = ra
E2SCL = 4
E2SDA = 5
E2SCL_PIN = E2_PORT.E2SCL
E2SDA_PIN = E2_PORT.E2SDA
LED_PORT = ra
LED = 6
LED_PIN = LED_PORT.LED
SW_PORT = ra
SW = 7
SW_PIN = SW_PORT.SW
ADC_PORT = re
ADC_OUT = 7
ADC_IN = 6
ADC_OUT_PIN = ADC_PORT.ADC_OUT
ADC_IN_PIN = ADC_PORT.ADC_IN
; *** flags ***
RX_IS_ARP = 0 ; incoming packet is an ARP packet
RX_IS_ICMP = 1 ; incoming packet is an ICMP packet
RX_IS_UDP = 2 ; incoming packet is a UDP packet
RX_IS_TCP = 3 ; incoming packet is a TCP packet
RX_IS_IP_BCST = 4 ; incoming packet is an IP Broadcast packet
GOT_DHCP_OFFER = 5 ; received DHCP IP address offer
GOT_IP_ADDR = 6 ; received an IP address assignment (recv'ed DHCP ACK)
IP_CHKSUM_LSB = 7 ; next byte to accumulate IP checksum is LSB
TCP_CHKSUM_LSB = 5 ; next byte to accumulate TCP checksum is LSB
; *** flags2 ***
GOT_RX_FRAME = 0 ; used by NICWaitRxFrame to indicate we got one.
GOT_IP_LEASE = 1 ; indicates that an IP lease was granted by a DHCP server
RENEW_IP_LEASE = 2 ; indicates an IP lease renewal is in progress
TCP_SOCK = 3 ; tcp connection/socket indicator. 0=tcp1, 1=tcp2
TCP_TXSEMA = 4 ; tcp transmit semaphore.
SMTP_TXEN = 5 ; SMTP transmit enable
SW_PRESSED = 6 ; indicates the switch SW2 is held in (pressed) when 1
BROWSER_TYPE = 7 ; 1=netscape type, 0=IE type
; *** flags3 ***
ARP_REQ_SENT = 0 ; indicates that an ARP request has been sent
ARP_RSP_RCVD = 1 ; indicates that an ARP response has been received
ARP_STL_TX = 2 ; indicates that the stalled packet is to be transmitted
SMTP_OK = 3 ; indicates that SMTP Delivered mail successfully
HTTP_METHOD = 4 ; 0=get, 1=post/put (requested method by remote browser)
GOT_HTTP_METHOD = 5 ; indicates the HTTP_METHOD bit is valid
GOT_URI = 6 ; indicates the file URI is complete
LED_LOCK = 7 ; locks access to the LED when set to 1.
; *** NIC Constants ***
RXBUF_START = $40 ; 3328 byte receive buffer (2 max-size packets)
RXBUF_END = $4D ; "
TXBUF1_START = $4D ; 1536 byte transmit buffer for ICMP/UDP
TXBUF2_START = $53 ; 1536 byte transmit buffer for tcp connection1
TXBUF3_START = $59 ; 1536 byte transmit buffer for tcp connection2
TXBUF4_START = $5F ; 256 byte transmit buffer for ARP
; *** Ethernet Constants ***
; also see in "DEMO DEFINES" section
; *** ARP Constants ***
ARP_TIMEOUT = 5 ; waiting for an ARP response timeout period
; *** IP Constants ***
; also see in "DEMO DEFINES" section
IP_TTL = 32
; *** DHCP Constants ***
IF DHCP
DHCP_DISC_EXP = 3 ; timeout in seconds waiting for an OFFER
DHCP_REQ_EXP = 3 ; timeout in seconds waiting for an ACK after REQUEST
DHCP_REQ_TRIES = 3 ; no of retries in txing a DHCPREQUEST
ENDIF
; *** UDP Constants ***
UDP_RX_DEST_MSB = $04 ; user UDP RX Port: 1025
UDP_RX_DEST_LSB = $01 ; "
; *** TCP Constants ***
; TCP state-machine states (numbering order is signifcant)
TCP_ST_CLOSED = 0
TCP_ST_LISTEN = 1
TCP_ST_SYNSENT = 2
TCP_ST_SYNRCVED = 3
TCP_ST_ESTABED = 4
TCP_ST_FINWAIT1 = 5
TCP_ST_FINWAIT2 = 6
TCP_ST_CLOSEWAIT= 7
TCP_ST_CLOSING = 8
TCP_ST_LASTACK = 9
TCP_ST_TIMEWAIT = 10
; Bit positions in the TCP <flag> byte.
TCP_FLAG_ACK = 4
TCP_FLAG_PSH = 3
TCP_FLAG_RST = 2
TCP_FLAG_SYN = 1
TCP_FLAG_FIN = 0
; TCP Options
TCP_OPTION_END = 0
TCP_OPTION_NOP = 1
TCP_OPTION_MSS = 2 ; max segment size
TCP_HDR_LENGTH = 5 ; normal TCP header length.
TCP_OFFSET_MASK = $F0
TCP_WINDOW_SIZE = 1400 ; max # of Data bytes we will accept
TCP_SEG_SIZE = 1400 ; max # of Data bytes TCP will transmit per segment
TCP_RESTART_EXP = 8 ; TCP re-transmission timeout period (0.19s per tick)
TCP_CONN_EXP = 160 ; TCP connection timeout period (0.19s per tick)
; *** HTTP Constants ***
; States for parsing HTTP headers
HTTP_PORT_MSB = 0 ; local tcp port number for HTTP server.
HTTP_PORT_LSB = 80 ; "
HTTP_SEG_SIZE = 1400
URI1 = $81 ; hash of "resource.htm"
URI2 = $54 ; hash of "temperature.htm"
URI3 = $21 ; hash of "postctrl.htm"
; *** EEPROM Constants ***
E2_CMD_RD = $A1 ; most-significant 7-bits is the I2C slave addr
E2_CMD_WR = $A0 ; most-significant 7-bits is the I2C slave addr
E2_SDA_MASK = (1 << E2SDA) ; a '1' in the SDA bit, '0' everywhere else
E2_DDR_SDA_IN = (RA_DIR | E2_SDA_MASK) ; direction of SDA port when SDA is input
E2_DDR_SDA_OUT = (RA_DIR & (~E2_SDA_MASK)) ; direction of SDA port when SDA is output
; *** SMTP Constants ***
SMTP_PORT_MSB = 0 ; remote tcp port number for SMTP server.
SMTP_PORT_LSB = 25 ; "
; SMTP sendmail state-machine states
SMTP_CLOSE = 0 ; smtp closing or closed
SMTP_CONNECT = 1 ; state machine in connection establishment
SMTP_HELO = 2 ; HELO command sent, waiting for 250 reply
SMTP_MAIL = 3 ; MAIL FROM command sent, waiting for 250 reply
SMTP_RCPT = 4 ; RECIPIENT TO command sent, waiting for 250 reply
SMTP_DATA = 5 ; DATA command sent, waiting for 354 reply
SMTP_TEXT = 6 ; sent email text message, waiting for ack
SMTP_QUIT = 7 ; sent QUIT command, waiting for 221 reply
; email constants
CR = $0D ; Control
LF = $0A ; Line Feed
; **************
; *** MACROS ***
; **************
_bank MACRO 1 ; sets FSR[7:4]
bank \1
IF \1 & %10000000 ; SX48BD and SX52BD (production release)
; BANK instruction
setb fsr.7 ; modifies FSR bits 4,5 and 6. FSR.7 needs
; to be set by sw.
ELSE
clrb fsr.7
ENDIF
ENDM
_banky MACRO 1 ; set FSR[7] ~only~
IF \1 & %10000000 ; SX48BD and SX52BD (production release)
; bank instruction
setb fsr.7 ; modifies FSR bits 4,5 and 6. FSR.7 needs to
; be set by sw.
ELSE
clrb fsr.7
ENDIF
ENDM
_mode MACRO 1
mov w, #\1 ; loads the M register correctly for the SX48BD
; and SX52BD
mov m, w
ENDM
_pc_check MACRO 0
IF ($ & $100)
ERROR 'ERROR!! ADD PC,W instruction at invalid addr'
ENDIF
ENDM
; ***********
; *** ISR ***
; ***********
ORG 0 ; Page0
ISR
Timer ; implement various SW timers
; lowest-common-denominator timer
_bank TIMER_BANK
incsz baseTimer ; inc at 2.9us per tick with 50MHz clk
jmp :timerEnd
:tcp1Timer ; TCP1-timer (used for TCP1 re-transmission timeouts)
incsz tcp1TimerLSB ; inc at 742.4us per tick with 50MHz clk
jmp :tcp2Timer
inc tcp1TimerMSB ; inc at 190.0544ms per tick with 50MHz clk
:tcp2Timer ; TCP2-timer (used for TCP2 re-transmission timeouts)
incsz tcp2TimerLSB ; inc at 742.4us per tick with 50MHz clk
jmp :conn1Timer
inc tcp2TimerMSB ; inc at 190.0544ms per tick with 50MHz clk
:conn1Timer ; Connection-timer (used for TCP1 connection timeouts)
incsz conn1TimerLSB
jmp :conn2Timer
inc conn1TimerMSB
:conn2Timer ; Connection-timer (used for TCP2 connection timeouts)
incsz conn2TimerLSB
jmp :arpTimer
inc conn2TimerMSB
:arpTimer ; ARP-timer (used for ARP response timeouts)
incsz arpTimerLSB
jmp :dhcpTimer
inc arpTimerMSB
:dhcpTimer
IF DHCP
_bank DHCP_BANK
; DHCP-timer (used for IP lease renewals)
inc dhcpBaseTimer0 ; inc at 742.4us per tick
; w 50MHz clk
snz
inc dhcpBaseTimer1 ; inc at 190.0544ms per tick
; w 50MHz clk
mov w, dhcpBaseTimer1 ; check if 1s has passed
xor w, #5 ; 5 ticks = 0.950272s
jnz :timerEnd
mov w, dhcpBaseTimer0
xor w, #63 ; 63 ticks = 0.0467712s
jnz :timerEnd
clr dhcpBaseTimer0 ; reset 1s counters
clr dhcpBaseTimer1
incsz dhcpTimer0 ; inc at 1s per tick
jmp :timerEnd
incsz dhcpTimer1 ; inc at 256s per tick
jmp :timerEnd
incsz dhcpTimer2 ; inc at 65536s per tick
jmp :timerEnd
inc dhcpTimer3 ; inc at 16777216s per tick
ENDIF
:timerEnd
ADCTempSensor ; SW A-to-D for measuring current board temperature to be then
; displayed on a Dynamic web page
; ADC(out) = !ADC(in) (balancing the yin and the yang)
mov w, ADC_PORT
sb wreg.ADC_IN
setb ADC_OUT_PIN
snb wreg.ADC_IN
clrb ADC_OUT_PIN
; decision time
_bank ADC_BANK
sb wreg.ADC_OUT
incsz adcAcc
inc adcAcc
dec adcAcc
inc adcCount
jnz :adcTempSensorEnd
; accumulate for averaging (256 samples)
mov w, adcAcc
clr adcAcc
add adcLSB, w
snc
inc adcMSB
; check if averaging is done
incsz adcSampleCount
jmp :adcTempSensorEnd
; averaging Done -- save results
mov adc, adcMSB ; divide by 256 (clever huh?)
clr adcMSB
clr adcLSB
:adcTempSensorEnd
LedBlinker ; blinks auxilliary LED, but Don't interfere if E2 access is
; in progress because LED and E2 may share same port
; do not blink if LED locked
jb flags3.LED_LOCK, :ledBlinkerEnd
IF HTTP
_bank HTTP_BANK
test httpParseState ; do not blink led if E2 access going
jnz :ledBlinkerEnd
ENDIF
_bank TIMER_BANK
mov w, tcp2TimerMSB ; use the tcp2 timer
; for led flash rate
and w, #%00001111
jnz :ledHere
_bank MISC_BANK
clrb LED_PORT.LED ; switch led on
jmp :ledBlinkerEnd
:ledHere mov w, tcp2TimerMSB
and w, #%00001111
xor w, #1
jnz :ledBlinkerEnd
_bank MISC_BANK
setb LED_PORT.LED ; switch led off
:ledBlinkerEnd
ISRExit mov w, #-INT_PERIOD
retiw
; ********************
; *** MAIN PROGRAM ***
; ********************
Init jmp _Init
ARPInit jmp _ARPInit
TCPIPInit jmp _TCPIPInit
StartupDelay jmp _StartupDelay
CopyRemoteIPSocket1 jmp _CopyRemoteIPSocket1
CopyRemoteIPSocket2 jmp _CopyRemoteIPSocket2
CheckRemotePortTCB1 jmp _CheckRemotePortTCB1
CheckRemotePortTCB2 jmp _CheckRemotePortTCB2
IF HTTP
E2Init jmp _E2Init
ENDIF
Main
call @Init
IF DHCP
call @DHCPConfig ; configure iSX via DHCP server
ENDIF
call @UDPAppInit ; initialise UDP application
_bank MISC_BANK
clr pageCount ; clear page counter (dynamic Data)
; main program loop
:mainLoop
IF DHCP
call @CheckIPLeaseExpire ; check validity of IP lease
ENDIF
call @NICCheckRxFrame ; check if we received a frame
jz :noRxFrame ; no
call @NICWaitRxFrame ; no waiting cus we've already
; checked
call @ARPCheckIfIs ; check and process if ARP
jb flags.RX_IS_ARP, :mainLoop
call @CheckIPDatagram ; not ARP, check if IP
jb flags.RX_IS_ICMP, :icmp
jb flags.RX_IS_UDP, :udp
jb flags.RX_IS_TCP, :tcp
jmp :mainLoop
:icmp call @ICMPProcPktIn ; process incoming ICMP packet
jmp :mainLoop
:udp call @UDPProcPktIn ; process incoming UDP packet
jmp :mainLoop
:tcp call @TCPProcPktIn ; process incoming TCP packet
jmp :mainLoop
:noRxFrame call @ARPSendStPacket ; send ARP stalled packets if any
bank TCP_BANK
cje tcp2State, #TCP_ST_CLOSED, :tcp2Closed ; check if tcp2
; in listen
cje tcp2State, #TCP_ST_LISTEN, :tcp2Listen ; check if tcp2
; closed
; tcp2 is in another state, check if hanging connection
bank TIMER_BANK
cjae conn2TimerMSB, #TCP_CONN_EXP, :resetTCP2
jmp :tcpTx ; connection timer not timed out
:tcp2Closed call @TCPApp2Init ; initialise tcp2 application
_bank TIMER_BANK
clr conn2TimerMSB ; clear the connection timer
jmp :tcp1Check
:tcp2Listen bank TIMER_BANK
clr conn2TimerMSB ; tcp2 in listen, clr conn timer
jmp :tcp1Check
:tcp1Check _bank TCP_BANK
cje tcp1State, #TCP_ST_CLOSED, :tcp1Closed ; check if tcp1
; in listen
cje tcp1State, #TCP_ST_LISTEN, :tcp1Listen ; check if tcp1
; closed
; tcp1 is in another state, check if hanging connection
bank TIMER_BANK
cjae conn1TimerMSB, #TCP_CONN_EXP, :resetTCP1
jmp :tcpTx ; connection timer not timed out
:tcp1Closed call @TCPApp1Init ; initialise tcp1 application
_bank TIMER_BANK
clr conn1TimerMSB ; clear the connection timer
jmp :mainLoop
:tcp1Listen bank TIMER_BANK
clr conn1TimerMSB ; tcp2 in listen, clr conn timer
jmp :mainLoop
:resetTCP2 ; reset hung TCP2 connection
bank TCP_BANK
clr tcp2UnAckMSB ; clear bytes to still send on tcp2
clr tcp2UnAckLSB
mov tcp2State, #TCP_ST_CLOSED
IF HTTP
bank HTTP_BANK ; reset HTTP webserver state
clr httpParseState
ENDIF
jmp :mainLoop
:resetTCP1 ; reset hung TCP1 connection
bank TCP_BANK
clr tcp1UnAckMSB ; clear bytes to still send on tcp1
clr tcp1UnAckLSB
mov tcp1State, #TCP_ST_CLOSED
IF SMTP
bank SMTP_BANK ; reset SMTP client state
clr smtpState
ENDIF
jmp :mainLoop ; repeat cycle by returning to entry
; do not allow new tx if waiting for ARP response
:tcpTx jb flags3.ARP_REQ_SENT, :mainLoop
mov w, #(1<<TCP_TXSEMA)
xor flags2, w ; toggle TCP_TXSEMA to give other tcp
; connection a time-slice to transmit
call @TCPTransmit ; check if app has anything to transmit
jmp :mainLoop ; repeat cycle by returning to entry
; *******************
; *** SUBROUTINES ***
; *******************
ORG $D0 ; Page0
; ******************************************************************************
_Init
; Main program initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_mode LVL_W
mov !ra, #RA_LVL
mov !rb, #RB_LVL
mov !rc, #RC_LVL
mov !rd, #RD_LVL
mov !re, #RE_LVL
_mode PLP_W
mov !ra, #RA_PLP
mov !rb, #RB_PLP
mov !rc, #RC_PLP
mov !rd, #RD_PLP
mov !re, #RE_PLP
_mode DIR_W
mov !ra, #RA_DIR
mov !rb, #RB_DIR
mov !rc, #RC_DIR
mov !rd, #RD_DIR
mov !re, #RE_DIR
mov ra, #RA_OUT
mov rb, #RB_OUT
mov rc, #RC_OUT
mov rd, #RD_OUT
mov re, #RE_OUT
clr flags
clr flags2
clr flags3
call @NICInit
call @ARPInit
call @TCPIPInit
IF HTTP
call @E2Init
ENDIF
mov w, #(RTCC_PS_OFF) ; setup option register
mov !option, w
retp
; ******************************************************************************
_ARPInit
; ARP initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank ARP_BANK
clr host1IP0 ; clear the cache
clr host1IP1 ; "
clr host1IP2 ; "
clr host1IP3 ; "
retp
; ******************************************************************************
_TCPIPInit
; TCP/IP stack initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank IP_BANK
IF DHCP
clr myIP3
clr myIP2
clr myIP1
clr myIP0
ELSE
; initialize SX's IP addr
setb flags.GOT_IP_ADDR
mov myIP3, #SX_IP_ADDR3
mov myIP2, #SX_IP_ADDR2
mov myIP1, #SX_IP_ADDR1
mov myIP0, #SX_IP_ADDR0
ENDIF
; initialize IP Identifier sequence number
clr ipIdentMSB
clr ipIdentLSB
; initialise the TCP variables
bank TCP_BANK
clr tcp1UnAckMSB
clr tcp1UnAckLSB
clr tcp2UnAckMSB
clr tcp2UnAckLSB
mov tcp1State, #TCP_ST_CLOSED
mov tcp2State, #TCP_ST_CLOSED
; clear the tcp socket local ports (used for listening on).
bank TCB1_BANK
clr tcb1LocalPortLSB
clr tcb1LocalPortMSB
bank TCB2_BANK
clr tcb2LocalPortLSB
clr tcb2LocalPortMSB
; clear the tcp sockets remote IP addresses
_bank TCPSOCKET_BANK
clr sock1RemoteIP3
clr sock2RemoteIP3
retp
IF HTTP
; ******************************************************************************
_E2Init
; EEPROM initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; get the I2C Device into a known state
call @E2SDAInput
:e2InitLoop clrb E2SCL_PIN
call @E2Delay1300ns
setb E2SCL_PIN
call @E2Delay900ns
jnb E2SDA_PIN, :e2InitLoop
retp
ENDIF
; ******************************************************************************
_StartupDelay
; Delay for ?ms @ 50MHz
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov globTemp2, #10
:loop3 clr globTemp1
:loop2 clr w
:loop1 decsz wreg
jmp :loop1
decsz globTemp1
jmp :loop2
decsz globTemp2
jmp :loop3
retp
; ******************************************************************************
_CopyRemoteIPSocket1
; Copies remoteIP3-0 into sock1RemoteIP3
; INPUT: remoteIP3-0
; OUTPUT: sock1RemoteIP3
; ******************************************************************************
mov globTemp1, #sock1RemoteIP3
mov globTemp3, #remoteIP3
call @Copy4Inc
retp
; ******************************************************************************
_CopyRemoteIPSocket2
; Copies remoteIP3-0 into sock2RemoteIP3
; INPUT: remoteIP3-0
; OUTPUT: sock2RemoteIP3
; ******************************************************************************
mov globTemp1, #sock2RemoteIP3
mov globTemp3, #remoteIP3
call @Copy4Inc
retp
; ******************************************************************************
_CheckRemotePortTCB1
; Checks if the remote port in the TCP packet is in TCB1_BANK
; INPUT: tcpRemotePortLSB, tcpRemotePortMSB, tcb1RemotePortLSB,
; tcb1RemotePortMSB
; OUTPUT: Z set if in there
; ******************************************************************************
_bank TCPPORT_BANK
mov w, tcpRemotePortMSB
bank TCB1_BANK
xor w, tcb1RemotePortMSB
sz
retp
mov w, tcb1RemotePortLSB
bank TCPPORT_BANK
xor w, tcpRemotePortLSB
retp
; ******************************************************************************
_CheckRemotePortTCB2
; Checks if the remote port in the TCP packet is in TCB2_BANK
; INPUT: tcpRemotePortLSB, tcpRemotePortMSB, tcb2RemotePortLSB,
; tcb2RemotePortMSB
; OUTPUT: Z set if in there
; ******************************************************************************
_bank TCPPORT_BANK
mov w, tcpRemotePortMSB
bank TCB2_BANK
xor w, tcb2RemotePortMSB
sz
retp
mov w, tcb2RemotePortLSB
bank TCPPORT_BANK
xor w, tcpRemotePortLSB
retp
IF SMTP
; ******************************************************************************
_senderDomainName
; Contains the sender's domain name.
; [SMTP API function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
SMTPTEXT_HELO = $
dw 'HELO '
; insert the Domain name here
dw 'sx'
dw CR,LF
SMTPTEXT_HELO_END = $
; ******************************************************************************
_mailFrom
; Contains the sender's address.
; [SMTP API function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
SMTPTEXT_MAIL = $
dw 'MAIL FROM: '
; insert the sender's address here
dw '<sx>'
dw CR,LF
SMTPTEXT_MAIL_END = $
; ******************************************************************************
_mailTo
; Contains the recipient's address.
; [SMTP API function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
SMTPTEXT_RCPT = $
dw 'RCPT TO: '
; insert the recipient's address here
dw '<joe@demo.sx>'
dw CR,LF
SMTPTEXT_RCPT_END = $
ENDIF
IF SMTP
; ******************************************************************************
_mailData
; Contains the mail message Data.
; [SMTP API function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
SMTPTEXT_TEXT = $
dw 'From: SX'
dw CR,LF
; insert To: field here
dw 'To: Joe'
dw CR,LF
; insert Subject: field here
dw 'Subject: Button Pressed!'
dw CR,LF,CR,LF
; insert body of email message here
dw 'Button SW2 pressed'
dw CR,LF,'.',CR,LF
SMTPTEXT_TEXT_END = $
ENDIF
IF SMTP
SMTPTEXT_QUIT = $
_SMTPTEXT_QUIT dw 'QUIT',$0D,$0A
ENDIF
ORG $200 ; Page1
NICBufCopy jmp _NICBufCopy
NICBufWrite jmp _NICBufWrite
NICBufRead jmp _NICBufRead
NICBufIPAddrWr jmp _NICBufIPAddrWr
NICWriteSrcIP jmp _NICWriteSrcIP
NICWriteDestIP jmp _NICWriteDestIP
NICWriteSrcEth jmp _NICWriteSrcEth
NICWriteDestEth jmp _NICWriteDestEth
NICDMAInit jmp _NICDMAInit
; ******************************************************************************
NICInit
; Initializes and configures Realtek RTL8019AS NIC
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank NIC_BANK
mov nicIOAddr, #$1F ; write to reset port
mov w, #0
call NICWrite
call @StartupDelay ; give it a little time to reset
; --- Page3 Registers ---
clr nicIOAddr ; CR
mov w, #%11000001 ; Page3, Stop
call NICWrite
mov nicIOAddr, #$01 ; 9346CR
mov w, #%11000000 ; config register write enable
call NICWrite
mov nicIOAddr, #$05 ; CONFIG2
mov w, #%00000000 ; link test enable
call NICWrite
; --- Page1 Registers ---
clr nicIOAddr ; CR
mov w, #%01000001 ; Page1, Stop
call NICWrite
inc nicIOAddr ; ($01) PAR0
mov w, #SX_ETH_ADDR0
call NICWrite
inc nicIOAddr ; ($02) PAR1
mov w, #SX_ETH_ADDR1
call NICWrite
inc nicIOAddr ; ($03) PAR2
mov w, #SX_ETH_ADDR2
call NICWrite
inc nicIOAddr ; ($04) PAR3
mov w, #SX_ETH_ADDR3
call NICWrite
inc nicIOAddr ; ($05) PAR4
mov w, #SX_ETH_ADDR4
call NICWrite
inc nicIOAddr ; ($06) PAR5
mov w, #SX_ETH_ADDR5
call NICWrite
inc nicIOAddr ; ($07) CURR
mov w, #RXBUF_START
call NICWrite
; --- Page0 Registers ---
clr nicIOAddr ; CR
mov w, #%00000001 ; Page0, Stop
call NICWrite
inc nicIOAddr ; ($01) PSTART
mov w, #RXBUF_START
call NICWrite
inc nicIOAddr ; ($02) PSTOP
mov w, #RXBUF_END
call NICWrite
inc nicIOAddr ; ($03) BNRY
mov w, #RXBUF_START
call NICWrite
mov nicIOAddr, #$07 ; ISR
mov w, #$FF
call NICWrite
mov nicIOAddr, #$0C ; RCR
mov w, #%11000110
call NICWrite
inc nicIOAddr ; ($0D) TCR
mov w, #%11100000
call NICWrite
inc nicIOAddr ; ($0E) DCR
mov w, #%10111000
call NICWrite
clr nicIOAddr ; CR
mov w, #%00000010 ; Page0, Start
call NICWrite
retp
; ******************************************************************************
NICWrite
; Does an I/O Write of a byte on the ISA host bus to the NIC
; INPUT: w = byte to be written
; nicIOAddr = I/O address (most-significant 3 bits must be zero)
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
; put Data out on Data bus
mov NIC_DATA_PORT, w
_mode DIR_W
mov w, #0 ; output
mov !NIC_DATA_PORT, w
; put addr out on addr bus
mov w, NIC_CTRL_PORT
and w, #%11100000
or w, nicIOAddr
mov NIC_CTRL_PORT, w
; strobe IOWB pin
jmp $+1
clrb IOWB_PIN
jmp $+1
jnb IOCH_PIN, $
setb IOWB_PIN
retp
; ******************************************************************************
NICWriteAgain
; Write to the same nicIOAddr as the previous call to NICWrite()
; INPUT: w = byte to be written
; OUTPUT: none
; ******************************************************************************
; put Data out on Data bus
mov NIC_DATA_PORT, w
; strobe IOWB pin
jmp $+1
clrb IOWB_PIN
jmp $+1
jnb IOCH_PIN, $
setb IOWB_PIN
retp
; ******************************************************************************
NICRead
; Does an I/O Read of a byte on the ISA host bus from the NIC
; INPUT: nicIOAddr = I/O address (most-significant 3 bits must be zero)
; OUTPUT: w = byte read
; ******************************************************************************
bank NIC_BANK
; configure Data bus for input
_mode DIR_W
mov w, #$FF ; input
mov !NIC_DATA_PORT, w
; put addr out on addr bus
mov w, NIC_CTRL_PORT
and w, #%11100000
or w, nicIOAddr
mov NIC_CTRL_PORT, w
; strobe IORB pin and latch Data
jmp $+1
clrb IORB_PIN
jmp $+1
jnb IOCH_PIN, $
mov w, NIC_DATA_PORT
setb IORB_PIN
retp
; ******************************************************************************
NICReadAgain
; Read the NIC using the same nicIOAddr as the previous call to NICRead()
; INPUT: none
; OUTPUT: w = byte read
; ******************************************************************************
; strobe IORB pin and latch Data
jmp $+1
clrb IORB_PIN
jmp $+1
jnb IOCH_PIN, $
mov w, NIC_DATA_PORT
setb IORB_PIN
retp
; ******************************************************************************
NICPseudoRead
; 'Read' the NIC, but ignore Data. Must have called NICRead() or NICReadAgain()
; priorly
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; strobe IORB pin
jmp $+1
clrb IORB_PIN
jmp $+1
jnb IOCH_PIN, $
setb IORB_PIN
retp
; ******************************************************************************
NICPseudoRead6
; 'Read' the NIC (6) times, but ignore Data. Must have called NICRead() or
; NICReadAgain() priorly
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #6
:loop call NICPseudoRead
decsz wreg
jmp :loop
retp
; ******************************************************************************
NICCheckRxFrame
; Checks to see if an ethernet frame has been received
; INPUT: none
; OUTPUT: z is cleared if there's a frame waiting, otherwise z is set
; ******************************************************************************
_bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%01100010 ; Page1, abort DMA
call NICWrite
mov nicIOAddr, #$07 ; CURR
call NICRead
mov globTemp1, w
clr nicIOAddr ; CR
mov w, #%00000010 ; Page0
call NICWrite
mov nicIOAddr, #$03 ; BNRY
call NICRead
xor w, globTemp1 ; CURR = BNRY => no packets
retp
; ******************************************************************************
NICWaitRxFrame
; Wait for an ethernet frame to be received.
; INPUT: none
; OUTPUT: nicCurrPktPtr = points to beginning of packet just received
; nicNextPktPtr = points to beginnig of next packet
; nicRemoteEth0-5 = source (remote) ethernet address
; ******************************************************************************
clrb flags2.GOT_RX_FRAME ; clear indication of prev.
; rcvd frame
_bank NIC_BANK
:loop clr nicIOAddr ; CR
mov w, #%01100010 ; Page1, abort DMA
call NICWrite
mov nicIOAddr, #$07 ; CURR
call NICRead
mov globTemp1, w
clr nicIOAddr ; CR
mov w, #%00000010 ; Page0
call NICWrite
mov nicIOAddr, #$03 ; BNRY
call NICRead
xor w, globTemp1
snz ; CURR = BNRY => no packets
retp ; Return immediately
setb flags2.GOT_RX_FRAME ; indicate we got something
clr nicIOAddr ; CR
mov w, #%00011010 ; Page0, packet send
call NICWrite
; store current-packet pointer
mov nicIOAddr, #$03 ; BNRY
call NICRead
mov nicCurrPktPtr, w
mov nicIOAddr, #$10 ; RDMA
; ignore receive status
call NICRead
; store next-packet pointer
call NICReadAgain
mov nicNextPktPtr, w
; ignore ethernet frame size
call NICPseudoRead
call NICPseudoRead
; ignore the <destination> ethernet addr
call NICPseudoRead6
; record the sender's <source> ethernet addr
call NICReadAgain
mov nicRemoteEth0, w
call NICReadAgain
mov nicRemoteEth1, w
call NICReadAgain
mov nicRemoteEth2, w
call NICReadAgain
mov nicRemoteEth3, w
call NICReadAgain
mov nicRemoteEth4, w
call NICReadAgain
mov nicRemoteEth5, w
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
retp
; ******************************************************************************
NICDumpRxFrame
; Discard the current received frame by advancing the receive circular buffer
; tail pointer
; INPUT: nicNextPktPtr = next packet page
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$03 ; BNRY
mov w, nicNextPktPtr ; advance tail pointer
call NICWrite
retp
; ******************************************************************************
NICInitTxFrame
; i. initialize NIC for remote-DMA writes
; ii. fills in ethernet <destination> and <source>
; INPUT: w = starting page number of tx buffer
; nicRemoteEth0-5 = Destination ethernet address
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w
bank NIC_BANK
; wait for prior transmission to complete
clr nicIOAddr ; CR
:wait call NICRead
jb wreg.2, :wait
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$04 ; TPSR
mov w, globTemp1
call NICWrite
mov nicIOAddr, #$08 ; RSAR0
mov w, #$00
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, globTemp1
call NICWrite
mov nicIOAddr, #$0A ; RBCR0
mov w, #$EA ; max ethernet packet size
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #$05
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
; <destination>
call NICWriteDestEth
; <source>
call NICWriteSrcEth
retp
; ******************************************************************************
NICSendTxFrame
; Once the transmit buffer on the NIC is filled, call this to tell the NIC to
; start sending
; INPUT: {ipLengthMSB,ipLengthLSB} = length of IP frame to be transmitted
; OUTPUT: none
; ******************************************************************************
call @ARPCheckCache ; start ARP routine
snb flags3.ARP_REQ_SENT ; Continue if an ARP request
; was not sent
retp ; exit, ARP request was sent
; or we are still waiting
; for a response
; an entry point into the function to skip the ARP routines
; no code space left in this bank to elegantly check a bit
NICSendTxFrameNow
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
bank IP_BANK
mov w, #(64-6-6-2)
mov w, ipLengthLSB-w
jc :notRunt
mov w, #1
mov w, ipLengthMSB-w
jc :notRunt
bank NIC_BANK
mov nicIOAddr, #$05 ; TBCR0
mov w, #64 ; min ethernet frame size
call NICWrite
inc nicIOAddr ; ($06) TBCR1
mov w, #0
call NICWrite
jmp :transmit
:notRunt bank NIC_BANK
mov nicIOAddr, #$05 ; TBCR0
bank IP_BANK
mov w, #(6+6+2)
add w, ipLengthLSB
call NICWrite ; should not affect carry flag
inc nicIOAddr ; ($06) TBCR1
bank IP_BANK
mov w, ipLengthMSB
snc
inc wreg
call NICWrite
:transmit clr nicIOAddr ; CR
mov w, #%00000110 ; Page0, transmit
call NICWrite
retp
; ******************************************************************************
_NICBufCopy
; Copy one part of the NIC's SRAM buffer to another part
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = source address in NIC's SRAM
; {nicCopyDestMSB,nicCopyDestLSB} = Destination address in NIC's SRAM
; {nicCopyLenMSB,nicCopyLenLSB} = length of buffer to copy
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$0B ; RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
; initialize RDMA to get source byte
:loop clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
mov nicIOAddr, #$0A ; RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite
mov nicIOAddr, #$10 ; RDMA
call NICRead
mov nicCopyTemp, w ; store source byte temporarily
; initialize RDMA to write byte to Destination
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopyDestLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopyDestMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
mov w, nicCopyTemp
call NICWrite
; increment source and Destination pointers
inc nicCopySrcLSB
snz
inc nicCopySrcMSB
inc nicCopyDestLSB
snz
inc nicCopyDestMSB
; check if source page pointer hit receive buffer ceiling
mov w, nicCopySrcMSB
xor w, #RXBUF_END
jnz :here
mov nicCopySrcMSB, #RXBUF_START
; loop as many times as there are bytes to copy
:here decsz nicCopyLenLSB
jmp :loop
test nicCopyLenMSB
snz
retp
dec nicCopyLenMSB
jmp :loop
; ******************************************************************************
_NICBufWrite
; Writes a byte to the SRAM buffer in the NIC
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = address in buffer memory to write to
; w = byte to write
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
mov nicCopyTemp, w
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
mov w, nicCopyTemp
call NICWrite
retp
; ******************************************************************************
_NICBufRead
; Reads a byte from the SRAM buffer in the NIC
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = address in buffer memory to read from
; OUTPUT: w = byte read
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite
mov nicIOAddr, #$10 ; RDMA
call NICRead
retp
; ******************************************************************************
_NICBufIPAddrWr
; Writes the source and destination IP addresses to the SRAM buffer in the NIC
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = address in buffer memory to write to
; myIP3-0 = <source_IP>
; remoteIP3-0 = <destination_IP>
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #(4+4) ; 8-byte DMA
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
; <source_IP>
call NICWriteSrcIP
; <destination_IP>
call NICWriteDestIP
retp
; ******************************************************************************
_NICWriteSrcIP
; Write Source IP address to NIC's buffer using remote DMA. NIC must be pre-
; initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
jnb flags.GOT_IP_ADDR, :noIP ; use IP address at noIP if
bank IP_BANK ; we got none from DHCP
mov w, myIP3
call NICWrite
bank IP_BANK
mov w, myIP2
call NICWriteAgain
mov w, myIP1
call NICWriteAgain
mov w, myIP0
call NICWriteAgain
retp
:noIP mov w, #0 ; 0.0.0.0
call NICWrite
mov w, #0
call NICWriteAgain
call NICWriteAgain
call NICWriteAgain
retp
; ******************************************************************************
_NICWriteDestIP
; Write Destination IP address to NIC's buffer using remote DMA. NIC must be
; pre-initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
mov w, remoteIP3
call NICWrite
bank IP_BANK
mov w, remoteIP2
call NICWriteAgain
bank IP_BANK
mov w, remoteIP1
call NICWriteAgain
bank IP_BANK
mov w, remoteIP0
call NICWriteAgain
retp
; ******************************************************************************
_NICWriteSrcEth
; Write Source Ethernet address to NIC's buffer using remote DMA. NIC must be
; pre-initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #SX_ETH_ADDR0
call NICWrite
mov w, #SX_ETH_ADDR1
call NICWriteAgain
mov w, #SX_ETH_ADDR2
call NICWriteAgain
mov w, #SX_ETH_ADDR3
call NICWriteAgain
mov w, #SX_ETH_ADDR4
call NICWriteAgain
mov w, #SX_ETH_ADDR5
call NICWriteAgain
retp
; ******************************************************************************
_NICWriteDestEth
; Write Destination Ethernet address to NIC's buffer using remote DMA. NIC must
; be pre-initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, nicRemoteEth0
call NICWrite
mov w, nicRemoteEth1
call NICWriteAgain
mov w, nicRemoteEth2
call NICWriteAgain
mov w, nicRemoteEth3
call NICWriteAgain
mov w, nicRemoteEth4
call NICWriteAgain
mov w, nicRemoteEth5
call NICWriteAgain
retp
; ******************************************************************************
_NICDMAInit
; Setup the NIC's RSAR and RBCR register in preparation for remote DMA.
; INPUT: nicCurrPktPtr = beginning of packet
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
; initialize DMA to <type> field in ethernet frame
mov nicIOAddr, #$08 ; RSAR0
mov w, #(4+6+6) ; point to <type> field
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCurrPktPtr
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #$FF
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #$0F
call NICWrite
retp
ORG $400 ; Page2
ARPSendResponse jmp _ARPSendResponse
ARPSendStPacket jmp _ARPSendStPacket
CheckIPDatagram jmp _CheckIPDatagram
CheckIPDestAddr jmp _CheckIPDestAddr
ICMPProcPktIn jmp _ICMPProcPktIn
Copy4Inc jmp _Copy4Inc
; ******************************************************************************
NICRead_2
; Shortform for calling NICRead(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICRead
; ******************************************************************************
NICReadAgain_2
; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICReadAgain
; ******************************************************************************
NICPseudoRead_2
; Shortform for calling NICPseudoRead(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICPseudoRead
; ******************************************************************************
NICPseudoRead6_2
; Shortform for calling NICPseudoRead(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICPseudoRead6
; ******************************************************************************
NICWrite_2
; Shortform for calling NICWrite(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICWrite
; ******************************************************************************
NICWriteAgain_2
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
NICDumpRxFrame_2
; Shortform for calling NICDumpRxFrame(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICDumpRxFrame
; ******************************************************************************
ARPCheckCache
; Checks if remote(destination) host IP address is in cache. If so, update
; packet Ethernet address in NIC with cache entry, else send ARP request.
; INPUT: remoteIP0-3, host1IP0-3
; OUTPUT: none
; ******************************************************************************
jnb flags3.ARP_STL_TX, :cacheGo ; do not check cache
; if stalled packet
; to be txed
call ARPUpdateEthAddr ; update stalled packet's ethernet address
mov w, #%11111000
and flags3, w ; reset ARP
retp
:cacheGo ; check if remote host IP is in cache
bank IP_BANK
mov w, remoteIP0
bank ARP_BANK
xor w, host1IP0
jnz :cacheNoMatch ; no match
mov w, host1IP1
bank IP_BANK
xor w, remoteIP1
jnz :cacheNoMatch ; no match
mov w, remoteIP2
bank ARP_BANK
xor w, host1IP2
jnz :cacheNoMatch ; no match
mov w, host1IP3
bank IP_BANK
xor w, remoteIP3
jz :cacheMatch ; match! remoteIP0-3 = host1IP0-3
:cacheNoMatch setb flags3.ARP_REQ_SENT ; indicate an ARP request is sent
jmp ARPSendRequest ; do an ARP request to get the
; remote Eth address
:cacheMatch mov w, #%11111000
and flags3, w ; reset ARP
jmp ARPUpdateEthAddr ; update remote Eth address of
; pending packet
; ******************************************************************************
ARPUpdateEthAddr
; Updates Eth Address of pending packet to be txed in NIC's buffer with that in
; ARP cache
; INPUT: host1Eth0-5, stPktTxBufStart
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_2
mov nicIOAddr, #$08 ; RSAR0
mov w, #0
call NICWrite_2
inc nicIOAddr ; ($09) RSAR1
bank ARP_BANK
mov w, stPktTxBufStart ; store page no of stalled pkt
call NICWrite_2
bank NIC_BANK
inc nicIOAddr ; ($0A) RBCR0
mov w, #(6+6) ; 12-byte DMA
call NICWrite_2
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite_2
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite_2
mov nicIOAddr, #$10 ; RDMA
; <destination_Eth>
bank ARP_BANK
mov w, host1Eth0
call NICWrite_2
bank ARP_BANK
mov w, host1Eth1
call NICWriteAgain_2
mov w, host1Eth2
call NICWriteAgain_2
mov w, host1Eth3
call NICWriteAgain_2
mov w, host1Eth4
call NICWriteAgain_2
mov w, host1Eth5
jmp NICWriteAgain_2
; ******************************************************************************
ARPCheckIfIs
; Checks received packet to see if it is ARP.
; Sends an ARP response if its a request.
; Updates cache if it's an ARP response.
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: remoteIP0-3 (:rcvdARPRequest), host1Eth0-5 (:rcvdARPResponse)
; ******************************************************************************
clrb flags.RX_IS_ARP
call @NICDMAInit
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite_2
mov nicIOAddr, #$10 ; RDMA
; check if ethernet <type> field
; contains ARP identifier (0x0806)
call NICRead_2
xor w, #$08
jnz :outtaHere
call NICReadAgain_2
xor w, #$06
jnz :outtaHere
setb flags.RX_IS_ARP ; yes, it's ARP, indicate that for
; main loop
; ignore <hardware_type>,<protocol_type>,<HLEN>,<PLEN>
call NICPseudoRead6_2
; checks if the ARP packet received is a request or a response
call NICReadAgain_2
xor w, #$00
jnz :outtaHere ; not ARP at all
call NICReadAgain_2
mov globTemp1, w ; store ARP opcode
xor w, #$01 ; check if ARP Request (0x0001)
jz :rcvdARPRequest ; yes, process ARP request
mov w, globTemp1
xor w, #$02 ; check if ARP Response (0x0002)
jz :rcvdARPResponse ; yes, process ARP response
jmp :outtaHere ; no, not ARP at all
; ignore <sender_HA>
:rcvdARPRequest call NICPseudoRead6_2
; record the sender's IP addr (<sender_IP>)
; will be used to reply to sender
:senderIP bank IP_BANK
call NICReadAgain_2
mov remoteIP3, w
call NICReadAgain_2
mov remoteIP2, w
call NICReadAgain_2
mov remoteIP1, w
call NICReadAgain_2
mov remoteIP0, w
; ignore <target_HA>
:targetHA call NICPseudoRead6_2
; check if <target_IP> is me
mov fsr, #myIP3
call ARPCompare4
jnz :outtaHere
; so it's for me!
call ARPSendResponse
jmp NICDumpRxFrame_2 ; we're done with this packet, dump it
:rcvdARPResponse
; <sender_HA>
; record sender's Eth address in ARP cache
mov fsr, #host1Eth0
mov globTemp1, #6
:ethLoop call NICReadAgain_2
mov indf, w
inc fsr
decsz globTemp1
jmp :ethLoop
; <sender_IP>
; check if sender's IP corrresponds to IP address in ARP cache
mov fsr, #host1IP3
call ARPCompare4
jz :ipAddrResolved
; whoops, sender doesn't correspond to who we're trying to resolve!
; if we're waiting for an ARP response (no problem here)
jb flags3.ARP_REQ_SENT, :outtaHere
; otherwise, we need to invalidate the now corrupted ARP cache
clr host1IP3
clr host1IP1
clr host1IP2
clr host1IP0
jmp :outtaHere
:ipAddrResolved setb flags3.ARP_RSP_RCVD ; indicate we got a successfull ARP response
setb flags3.ARP_STL_TX ; transmit the stalled packet now
:outtaHere snb flags.RX_IS_ARP ; check if packet was ARP
call NICDumpRxFrame_2 ; if it was ARP, we dump it; otherwise, don't touch it
retp
; ******************************************************************************
ARPCompare4
; Compares Data from NICReadAgain() against a 4-byte word store consequtively
; in the SX's Data memory
; INPUT: fsr = points to beginning of 4-byte word to compare against
; OUTPUT: z: 1 = match, 0 = not match
; ******************************************************************************
mov globTemp1, #4
:loop call NICReadAgain_2
xor w, indf
sz
retp ; mis-match
inc fsr
decsz globTemp1
jmp :loop
stz
retp
; ******************************************************************************
ARPSendCommon1
; Helper function for ARPSendRequest and ARPSendResponse
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; <type> = 0x0806
mov w, #$08
call NICWriteAgain_2
mov w, #$06
call NICWriteAgain_2
; <hardware_type> = 0x0001
mov w, #$00
call NICWriteAgain_2
mov w, #$01
call NICWriteAgain_2
; <protocol_type> = 0x0800
mov w, #$08
call NICWriteAgain_2
mov w, #$00
call NICWriteAgain_2
; <HLEN> = 0x06
mov w, #$06
call NICWriteAgain_2
; <PLEN> = 0x04
mov w, #$04
jmp NICWriteAgain_2
; ******************************************************************************
ARPSendCommon2
; Helper function for ARPSendRequest and ARPSendResponse
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; <sender_HA>
call @NICWriteSrcEth
; <sender_IP>
call @NICWriteSrcIP
; <target_HA>
call @NICWriteDestEth
; <target_IP>
call @NICWriteDestIP
; whew! the ethernet frame is now complete, get ready to send ..
bank NIC_BANK ; NICWriteDestIP changes bank
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_2
mov nicIOAddr, #$05 ; TBCR0
mov w, #$40 ; min ethernet packet size
call NICWrite_2
inc nicIOAddr ; ($06) TBCR1
mov w, #$00
call NICWrite_2
clr nicIOAddr ; CR
mov w, #%00000110 ; transmit
jmp NICWrite_2
; ******************************************************************************
ARPSendRequest
; Stores remote IP address for which pending packet is intended in ARP cache and
; sends an ARP Request
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
mov w, remoteIP3 ; store IP address of target in ARP cache
bank ARP_BANK
mov host1IP3, w
bank IP_BANK
mov w, remoteIP2
bank ARP_BANK
mov host1IP2, w
bank IP_BANK
mov w, remoteIP1
bank ARP_BANK
mov host1IP1, w
bank IP_BANK
mov w, remoteIP0
bank ARP_BANK
mov host1IP0, w
bank NIC_BANK
mov w, #$FF
mov nicRemoteEth0, w ; setup broadcast Ethernet address
mov nicRemoteEth1, w
mov nicRemoteEth2, w
mov nicRemoteEth3, w
mov nicRemoteEth4, w
mov nicRemoteEth5, w
mov w, #TXBUF4_START ; point to ARP transmit buffer
call @NICInitTxFrame
call ARPSendCommon1
; <operation> = 0x0001 , ARP request opcode
mov w, #$00
call NICWriteAgain_2
mov w, #$01
call NICWriteAgain_2
call ARPSendCommon2
bank TIMER_BANK ; initialise the APR timeout timer
clr arpTimerMSB
clr arpTimerLSB
retp
; ******************************************************************************
_ARPSendResponse
; Send an ARP Response in reply to a ARP request.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #TXBUF4_START ; point to ARP transmit buffer
call @NICInitTxFrame
call ARPSendCommon1
; <operation> = 0x0002
mov w, #$00
call NICWriteAgain_2
mov w, #$02
call NICWriteAgain_2
jmp ARPSendCommon2
; ******************************************************************************
_ARPSendStPacket
; Sends the ARP stalled packet if any and also check if a timeout on waiting for
; an ARP response occured. If timeout, clear ARP cache
; INPUT: none
; OUTPUT: none
; ******************************************************************************
sb flags3.ARP_REQ_SENT ; check if we are waiting for
; an ARP response
retp ; no, nothing to Do here
; if no ARP response rcvd, check if timed out on waiting for it
jnb flags3.ARP_STL_TX, :checkARPTimeout
; yes we have rcvd a response we've been waiting for
; now we can send the stalled packet
mov w, #%11111000
and flags3, w ; reset ARP
; re-initialize the NIC's TPSR
bank NIC_BANK
clr nicIOAddr ; CR
:wait call NICRead_2
jb wreg.2, :wait ; wait for prior transmission
; to complete
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_2
mov nicIOAddr, #$04 ; TPSR
bank ARP_BANK
mov w, stPktTxBufStart ; load stalled packet's
; address pointer
call NICWrite_2
; read the NIC's transmit buffer to find out the IP <length>
; so that we can re-initialize the NIC's TBCR
bank ARP_BANK
mov w, stPktTxBufStart
bank NIC_BANK
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 ; transmit the stalled ethernet frame
:checkARPTimeout
bank TIMER_BANK
csae arpTimerMSB, #ARP_TIMEOUT ; has the timer expired?
retp ; no, just return
mov w, #%11111000
and flags3, w ; yes, reset ARP flags
; Clear the ARP cache, since it acted as a temporary storage
; of the requested IP address. If we Do not clear the cache now,
; the next re-transmit routine will find a false match in the
; ARP cache.
bank ARP_BANK
clr host1IP3
clr host1IP2
clr host1IP1
clr host1IP0
retp
; ******************************************************************************
_CheckIPDatagram
; Checks to see if received ethernet frame contains an IP Datagram. If not, the
; frame will be discarded since this stack Doesn't Deal with any other kinds of
; Data-link layer protocol.
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: none
; ******************************************************************************
clrb flags.RX_IS_IP_BCST ; clear broadcast IP indication
CHKIP_MASK = ~((1<<RX_IS_ICMP)|(1<<RX_IS_UDP)|(1<<RX_IS_TCP)|(1<<RX_IS_IP_BCST))
mov w, #CHKIP_MASK
and flags, w
call @NICDMAInit
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite_2
mov nicIOAddr, #$10 ; RDMA
; check if ethernet <type> field contains IP identifier (0x0800)
call NICRead_2
xor w, #$08
jnz :outtaHere
call NICReadAgain_2
xor w, #$00
jnz :outtaHere
; check <version> and <HLEN>
call NICReadAgain_2
xor w, #$45 ; version = 4, header length = 5
jnz :outtaHere
; ignore <service_type>
call NICPseudoRead_2
; record <total_length>
call NICReadAgain_2
bank IP_BANK
mov ipLengthMSB, w
call NICReadAgain_2
mov ipLengthLSB, w
; adjust {ipLengthMSB,ipLengthLSB} to reflect number of
; Data bytes
sub ipLengthLSB, #20
sc
dec ipLengthMSB
; ignore <identification>
REPT 2
call NICPseudoRead_2
ENDR
; check against IP packet fragmentation
call NICReadAgain_2
jb wreg.5, :outtaHere ; <flags>
call NICReadAgain_2
xor w, #0 ; <fragment_offset>
jnz :outtaHere
; ignore <time_to_live>
call NICPseudoRead_2
; record <protocol>
call NICReadAgain_2
mov ipProtocol, w
; ignore <header_checksum>
REPT 2
call NICPseudoRead_2
ENDR
; record <source_IP>
:srcIP bank IP_BANK
call NICReadAgain_2
mov remoteIP3, w
call NICReadAgain_2
mov remoteIP2, w
call NICReadAgain_2
mov remoteIP1, w
call NICReadAgain_2
mov remoteIP0, w
; check <destination_IP>
:destIP clr globTemp2
mov w, myIP3
call CheckIPDestAddr
jnz :outtaHere
mov w, myIP2
call CheckIPDestAddr
jnz :outtaHere
mov w, myIP1
call CheckIPDestAddr
jnz :outtaHere
mov w, myIP0
call CheckIPDestAddr
jnz :outtaHere
xor globTemp2, #4
snz
setb flags.RX_IS_IP_BCST ; IP broadcast addr Detected
; ok! Determine which higher-level protocol
mov w, #1 ; ICMP
xor w, ipProtocol
snz
setb flags.RX_IS_ICMP
mov w, #17 ; UDP
xor w, ipProtocol
snz
setb flags.RX_IS_UDP
mov w, #6 ; TCP
xor w, ipProtocol
snz
setb flags.RX_IS_TCP
retp
:outtaHere jmp NICDumpRxFrame_2 ; if it ain't IP, forget it!
; ******************************************************************************
_CheckIPDestAddr
; Helper function for CheckIPDatagram
; Check for a match of the IP <destination_IP> field
; INPUT: w = byte of IP to check against
; OUTPUT: z = set if match
; globTemp2 = incremented if matched against 0xFF
; ******************************************************************************
mov globTemp1, w
call NICReadAgain_2
xor globTemp1, w
snz
retp
xor w, #$FF ; IP broadcast addr
sz
retp
inc globTemp2
stz
retp
; ******************************************************************************
_ICMPProcPktIn
; Process ICMP message. Only Echo Request ICMP messages are handled. All others
; are ignored. If Echo Request message is Detected, this will generate the echo
; reply.
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: none
; ******************************************************************************
; check <type>
call NICReadAgain_2
xor w, #$08 ; echo-request
jnz :outtaHere
bank IP_BANK
add ipLengthLSB, #(2+20)
snc
inc ipLengthMSB
bank NIC_BANK
mov nicCopySrcMSB, nicCurrPktPtr ; point to receive
; buffer
mov nicCopySrcLSB, #(4+6+6) ; don't copy NIC header
; , eth src & Destn
mov nicCopyDestMSB, #TXBUF1_START ; point to transmit
; buffer
mov nicCopyDestLSB, #(6+6)
bank IP_BANK
mov w, ipLengthMSB
bank NIC_BANK
mov nicCopyLenMSB, w
bank IP_BANK
mov w, ipLengthLSB
bank NIC_BANK
mov nicCopyLenLSB, w
mov w, #TXBUF1_START ; point to transmit buffer
bank ARP_BANK
mov stPktTxBufStart, w ; Store ICMP packet start address
bank NIC_BANK
call @NICInitTxFrame
call @NICBufCopy
; change ICMP <type> to echo reply (0)
mov nicCopySrcMSB, #TXBUF1_START ; point to transmit
; buffer
mov nicCopySrcLSB, #(6+6+2+20) ; point to ICMP <type>
mov w, #$00
call @NICBufWrite
; generate ICMP <checksum>
mov nicCopySrcMSB, #TXBUF1_START ; point to transmit
; buffer
bank IP_BANK
mov w, ipLengthMSB
bank NIC_BANK
mov nicCopyLenMSB, w
bank IP_BANK
mov w, ipLengthLSB
bank NIC_BANK
mov nicCopyLenLSB, w
sub nicCopyLenLSB, #(2+20+4)
sc
dec nicCopyLenMSB
call @ICMPGenCheckSum
; adjust IP <source_IP> and <destination_IP>
mov nicCopySrcMSB, #TXBUF1_START ; point to transmit
; buffer
mov nicCopySrcLSB, #(6+6+2+12) ; point to IP <source_IP>
call @NICBufIPAddrWr
bank IP_BANK
sub ipLengthLSB, #2
sc
dec ipLengthMSB
call @NICSendTxFrame
:outtaHere jmp NICDumpRxFrame_2
; ******************************************************************************
_Copy4Inc
; Copies 4 variables incrementally by copying from var pointed to by globTemp3
; to variable pointed to by globTemp1
; INPUT: globTemp1,3 points to variables
; OUTPUT: none
; ******************************************************************************
_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 glob
mov fsr, globTemp1 ; move tcb pointer to fsr
mov indf, globTemp2 ; overwrite tcb var with tcp var
inc globTemp1 ; increment pointer
inc globTemp3 ; increment pointer
_bank IP_BANK ; check loop counter until finished
dec counter1
sz
jmp :loop
retp
IF SMTP
SMTPTEXT_DATA = $
_SMTPTEXT_DATA dw 'DATA',$0D,$0A
ENDIF
ORG $600 ; Page3
; ******************************************************************************
NICWrite_3
; Shortform for calling NICWrite(), which is in Page1 (This is in Page3)
; ******************************************************************************
jmp @NICWrite
; ******************************************************************************
NICWriteAgain_3
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page3)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
ICMPGenCheckSum
; Goes through the ICMP message stored in the NIC's SRAM buffer and computes
; the ICMP message checksum.
; INPUT: nicCopySrcMSB = transmit buffer page
; {nicCopyLenMSB,nicCopyLenLSB} = (length of ICMP message - 4)
; OUTPUT: {ipCheckSumMSB,ipCheckSumLSB} = new checksum value
; ******************************************************************************
call IPCheckSumInit
bank NIC_BANK
mov nicCopyTemp, nicCopySrcMSB
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, #(6+6+2+20+4) ; point to ICMP <identifier>
call NICWrite_3
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite_3
inc nicIOAddr ; ($0A) RBCR0
mov w, nicCopyLenLSB
call NICWrite_3
inc nicIOAddr ; ($0B) RBCR1
mov w, nicCopyLenMSB
call NICWrite_3
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite_3
mov nicIOAddr, #$10 ; RDMA
; configure Data bus for input
_mode DIR_W
mov w, #$FF ; input
mov !NIC_DATA_PORT, w
; put addr out on addr bus
mov w, NIC_CTRL_PORT
and w, #%11100000
or w, nicIOAddr
mov NIC_CTRL_PORT, w
inc nicCopyLenMSB ; in order to loop easier later
:loop call @NICReadAgain
call IPCheckSumAcc
bank NIC_BANK
decsz nicCopyLenLSB
jmp :loop
decsz nicCopyLenMSB
jmp :loop
mov nicCopySrcMSB, nicCopyTemp
mov nicCopySrcLSB, #(6+6+2+20+2)
bank IP_BANK
mov w, /ipCheckSumMSB
call @NICBufWrite
inc nicCopySrcLSB
bank IP_BANK
mov w, /ipCheckSumLSB
call @NICBufWrite
retp
; ******************************************************************************
IPCheckSumInit
; Initializes the IP checksum routine.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
clr ipCheckSumMSB
clr ipCheckSumLSB
clrb flags.IP_CHKSUM_LSB ; next byte is MSB
retp
; ******************************************************************************
IPCheckSumAcc
; Accumulate the IP checksum value by adding the next 16-bit number
; IP header checksum (also used to compute ICMP checksum) is computed by Doing
; the one's complement of the one's complement sum of the 16-bit numbers in the
; header.
; INPUT: w = byte to accumulate
; flags.IP_CHKSUM_LSB = set if processing LSB, clear if processing MSB
; OUTPUT: {ipCheckSumMSB,ipCheckSumLSB} = new checksum value
; ******************************************************************************
bank IP_BANK
jnb flags.IP_CHKSUM_LSB, :msb ; are we processing an MSB?
:lsb add ipCheckSumLSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc ipCheckSumMSB ; yes
snz
inc ipCheckSumLSB
jmp :done
:msb add ipCheckSumMSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc ipCheckSumLSB ; yes, this time it is added
; to the LSB
snz
inc ipCheckSumMSB
:done xor flags, #(1<<IP_CHKSUM_LSB)
retp
; ******************************************************************************
IPGenCheckSum
; Generate the IP header checksum
; INPUT: {ipLengthMSB,ipLengthLSB} = IP <total_length>
; {ipIdentMSB,ipIdentLSB} = IP <identification>
; ipProtocol = 1(ICMP)/ 6(TCP)/ 17(UDP)
; OUTPUT: {ipCheckSumMSB,ipCheckSumLSB} = new checksum value
; ******************************************************************************
bank IP_BANK
call IPCheckSumInit
mov w, #$45 ; Version 4, 5x 32 bit words in IP header
call IPCheckSumAcc
mov w, #$00
call IPCheckSumAcc
mov w, ipLengthMSB
call IPCheckSumAcc
mov w, ipLengthLSB
call IPCheckSumAcc
mov w, ipIdentMSB
call IPCheckSumAcc
mov w, ipIdentLSB
call IPCheckSumAcc
mov w, #%01000000 ; don't fragment
call IPCheckSumAcc
mov w, #0
call IPCheckSumAcc
mov w, #IP_TTL
call IPCheckSumAcc
mov w, ipProtocol
call IPCheckSumAcc
jnb flags.GOT_IP_ADDR, :remoteIP ; IP = 0.0.0.0 if no IP
mov w, myIP3
call IPCheckSumAcc
mov w, myIP2
call IPCheckSumAcc
mov w, myIP1
call IPCheckSumAcc
mov w, myIP0
call IPCheckSumAcc
; check if this is a tcp packet being constructed and use
; the remoteIP address of the correct socket.
; we can only respond to hosts Defined in the tcp sockets
; foreign incoming packets are not responded to by sending
; a reset since this will need another TCB, they are flat-out
; ignored. The foreign remote host will eventually give up .
:remoteIP mov w, ipProtocol
xor w, #6 ; is it tcp?
jnz :skipSocketCpy ; no, Don't use remoteIP in tcp sockets
; check which is the current tcp connection
mov globTemp1, #remoteIP3 ; set source pointer
sb flags2.TCP_SOCK
mov globTemp3, #sock1RemoteIP3 ; destination pointer
snb flags2.TCP_SOCK
mov globTemp3, #sock2RemoteIP3 ; destination pointer
call @Copy4Inc ; copy sockRemoteIP into remoteIP
:skipSocketCpy _bank IP_BANK ; accumulate checksum with remoteIP
mov w, remoteIP3
call IPCheckSumAcc
mov w, remoteIP2
call IPCheckSumAcc
mov w, remoteIP1
call IPCheckSumAcc
mov w, remoteIP0
call IPCheckSumAcc
retp
; ******************************************************************************
IPStartPktOut
; Starts an outgoing IP packet by constructing the IP packet header
; INPUT: {ipLengthMSB,ipLengthLSB} = IP <total_length>
; {ipIdentMSB,ipIdentLSB} = IP <identification>
; ipProtocol = 1(ICMP)/ 6(TCP)/ 17(UDP)
; {ipCheckSumMSB,ipCheckSumLSB} = IP <header_checksum>
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
mov w, #6 ; is it tcp?
xor w, ipProtocol
jz :useTcpBuf ; yes
mov w, #TXBUF1_START ; no, Default tx buffer is TXBUF1
jmp :contIpStartP
:useTcpBuf sb flags2.TCP_SOCK ; point to correct tcp conn.
; tx buffer
mov w, #TXBUF2_START ; tcp connection1 tx buffer
snb flags2.TCP_SOCK
mov w, #TXBUF3_START ; tcp connection2 tx buffer
:contIpStartP bank ARP_BANK
sb flags3.ARP_REQ_SENT ; check if a prev packet
; is stalled
mov stPktTxBufStart, w ; no, store new address pointer
; to new packet
call @NICInitTxFrame
; <type> = 0x0800
mov w, #$08
call NICWrite_3
mov w, #$00
call NICWriteAgain_3
; <version> = 4, <HLEN> = 5
mov w, #$45
call NICWriteAgain_3
; <service_type>
mov w, #$00 ; normal precedence, D=T=R=0
call NICWriteAgain_3
; <total_length>
bank IP_BANK
mov w, ipLengthMSB
call NICWriteAgain_3
mov w, ipLengthLSB
call NICWriteAgain_3
; <identification>
mov w, ipIdentMSB
call NICWriteAgain_3
mov w, ipIdentLSB
call NICWriteAgain_3
; <flags>, <fragment_offset>
mov w, #%01000000 ; do not fragment
call NICWriteAgain_3
mov w, #0 ; offset = 0
call NICWriteAgain_3
; <time_to_live>
mov w, #IP_TTL
call NICWriteAgain_3
; <protocol>
mov w, ipProtocol
call NICWriteAgain_3
; <header_checksum>
mov w, /ipCheckSumMSB
call NICWriteAgain_3
mov w, /ipCheckSumLSB
call NICWriteAgain_3
; <source_IP>
call @NICWriteSrcIP
; <destination_IP>
call @NICWriteDestIP
retp
; ******************************************************************************
TCPAppPassiveOpen
; Do a passive open. i.e. listen for connections on a given port.
; [TCP API Function]
; INPUT: {tcb1LocalPortMSB,tcb1LocalPortLSB} = TCP port to listen on
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK
mov tcp2State, #TCP_ST_LISTEN
clr tcp2UnAckMSB
clr tcp2UnAckLSB
retp
; ******************************************************************************
TCPAppActiveOpen
; Do a active open. i.e. initiate a connect to a remote TCP.
; [TCP API Function]
; INPUT: {remoteIP0-3} = Destination IP addr
; {tcbLocalPortMSB,tcbLocalPortLSB} = local TCP port
; {tcbRemotePortMSB,tcbRemotePortLSB} = remote TCP port
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK
clr tcp1UnAckMSB
clr tcp1UnAckLSB
mov tcp1State, #TCP_ST_SYNSENT
bank TCB1_BANK
mov tcb1Flags, #(1<<TCP_FLAG_SYN)
jmp @TCPSendSyn
; ******************************************************************************
TCPAppClose
; Force the current connection to close
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; check which tcp connection to close
_bank TCP_BANK
jb flags2.TCP_SOCK, :finwaitSock2
; close tcp connection1
mov tcp1State, #TCP_ST_FINWAIT1
clr tcp1UnAckMSB
clr tcp1UnAckLSB
jmp :contAppClose
; close tcp connection2
:finwaitSock2 mov tcp2State, #TCP_ST_FINWAIT1
clr tcp2UnAckMSB
clr tcp2UnAckLSB
; common tcp close code
:contAppClose mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer
mov indf, #((1<<TCP_FLAG_FIN)|(1<<TCP_FLAG_ACK))
jmp @TCPSendEmptyPkt
; ******************************************************************************
TCPProcPktIn
; Process a received TCP packet. This function implements the TCP state machine
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call @TCPRxHeader ; receive the header
snz ; is the packet OK?
retp ; no, return
; check if incoming packet is for tcp conn. 1 or 2
bank TCP_BANK
jb flags2.TCP_SOCK, :isTcp2
; check the special states for tcp connection1
cje tcp1State, #TCP_ST_CLOSED, :CLOSED
cje tcp1State, #TCP_ST_LISTEN, :LISTEN
cje tcp1State, #TCP_ST_SYNSENT, :SYNSENT
cje tcp1State, #TCP_ST_FINWAIT1, :FINWAIT1
cje tcp1State, #TCP_ST_FINWAIT2, :FINWAIT2
cje tcp1State, #TCP_ST_LASTACK, :LASTACK
jmp :contProc
; check the special states for tcp connection2
:isTcp2 cje tcp2State, #TCP_ST_CLOSED, :CLOSED
cje tcp2State, #TCP_ST_LISTEN, :LISTEN
cje tcp2State, #TCP_ST_SYNSENT, :SYNSENT
cje tcp2State, #TCP_ST_FINWAIT1, :FINWAIT1
cje tcp2State, #TCP_ST_FINWAIT2, :FINWAIT2
cje tcp2State, #TCP_ST_LASTACK, :LASTACK
:contProc call @TCPCmpNxtSeq ; check if RCV.NXT == SEG.SEQ
sz ; are they equal?
jmp @TCPSendAck ; no, send an ACK and Drop packet
; check the flags
mov globTemp1, #(tcb1Flags-TCB1_BANK)
call @SetTCBPointer ; point to tcp conn's TCB
snb indf.TCP_FLAG_RST ; is the reset flag set?
jmp :gotoClosed ; yes, Drop packet and
; close the connection
snb indf.TCP_FLAG_SYN ; is the SYN bit set?
jmp @TCPSendReset ; yes, Drop the packet and
; send a reset
sb indf.TCP_FLAG_ACK ; is the ACK bit set?
jmp @NICDumpRxFrame ; no, Drop the packet
; we only accept ACKs of complete packets.
; Assume the ACK is for our last packet
mov w, #TCP_ST_ESTABED ; indicate tcp conn established
bank TCP_BANK
sb flags2.TCP_SOCK ; indicate tcp state to correct
mov tcp1State, w ; tcp conn. state variable
snb flags2.TCP_SOCK
mov tcp2State, w
; if we were in Syn-received then need to send an ACK
; check if for tcp1 or 2
jb flags2.TCP_SOCK, :tcp2AckCheck
;tcp1
test tcp1UnAckLSB
sz
jmp :outstanding
test tcp1UnAckMSB
snz
jmp :noOutstanding
jmp :outstanding
; tcp2
:tcp2AckCheck test tcp2UnAckLSB
sz
jmp :outstanding
test tcp2UnAckMSB
snz
jmp :noOutstanding
jmp :outstanding
:outstanding call @TCPAppTxDone ; tell the application it was ACKed
_bank TCP_BANK ; _bank cus we called TCPAppTxDone priorly
; check if for tcp1 or 2
jb flags2.TCP_SOCK, :clrTcpUnack2
clr tcp1UnAckLSB ; there should be no Data outstanding now
clr tcp1UnAckMSB
jmp :noOutstanding
:clrTcpUnack2 clr tcp2UnAckLSB ; there should be no Data outstanding now
clr tcp2UnAckMSB
:noOutstanding ; Does the packet contain Data? (Determine this from the length)
test tcpLengthMSB
jnz :packetHasData ; MSB is not zero
test tcpLengthLSB ; MSB is zero
jz :noData ; MSB = LSB = 0
:packetHasData call @TCPAppRxBytes ; inform app how many bytes available
_bank TCP_BANK
inc tcpLengthMSB
:processData call @NICReadAgain ; receive a byte
call @TCPAppRxData ; pass the byte to the application
_bank TCP_BANK ; _bank cus we called TCPAppRxData
; priorly
decsz tcpLengthLSB
jmp :processData
decsz tcpLengthMSB
jmp :processData
inc tcpLengthLSB ; indicate for later there was Data
; received
:noData call @TCPAckUpdate
; send an ACK packet
bank TCP_BANK
test tcpLengthLSB ; was Data received?
jz :checkFIN ; no, it was an ACK packet. Just return
call @TCPAppRxDone ; indicate the packet was OK to the app
_bank TCP_BANK ; _bank cus we called TCPAppRxDone
; priorly
jb tcpRxFlags.TCP_FLAG_FIN, :doClose ; if FIN bit set,
; close the conn.
call @NICDumpRxFrame
jmp @TCPSendAck
:checkFIN bank TCP_BANK
jb tcpRxFlags.TCP_FLAG_FIN, :doClose ; is the FIN bit set?
jmp @NICDumpRxFrame
:doClose call @NICDumpRxFrame
call @TCPIncRcvNxt ; ACK the FIN
mov w, #TCP_ST_LASTACK ; change state
bank TCP_BANK
sb flags2.TCP_SOCK
mov tcp1State, w ; indicate tcp conn state to tcp conn
snb flags2.TCP_SOCK ; state variable
mov tcp2State, w
jmp @TCPSendFin
:gotoClosed mov w, #TCP_ST_CLOSED ; go to the closed state
bank TCP_BANK ; indicate tcp closed conn state to
sb flags2.TCP_SOCK ; correct tcp conn state variable
mov tcp1State, w
snb flags2.TCP_SOCK
mov tcp2State, w
jmp @NICDumpRxFrame ; discard received packet
:FINWAIT1 call @NICDumpRxFrame
bank TCP_BANK
sb tcpRxFlags.TCP_FLAG_ACK ; check for ACK of FIN
retp
mov w, #TCP_ST_FINWAIT2 ; rcved ACK of FIN
sb flags2.TCP_SOCK ; indicate tcp finwait2 conn state to
mov tcp1State, w ; correct tcp conn state variable
snb flags2.TCP_SOCK
mov tcp2State, w
retp
:FINWAIT2 call @NICDumpRxFrame
bank TCP_BANK
sb tcpRxFlags.TCP_FLAG_FIN ; check for FIN
retp
mov w, #TCP_ST_CLOSED ; rcved FIN
sb flags2.TCP_SOCK
mov tcp1State, w
snb flags2.TCP_SOCK
mov tcp2State, w
call @TCPIncRcvNxt ; ACK the FIN
jmp @TCPSendAck
:LASTACK ; ignore the packet
; should check the packet is actually an ACK
mov w, #TCP_ST_CLOSED ; go to the closed state
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 SMTP
mov globTemp2, #3 ; set rcv byte counter
mov globTemp3, #smtpReplyCode3 ; 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 SMTP
sb flags2.SMTP_TXEN ; check if we may tx
retp ; no
clrb flags2.SMTP_TXEN ; yes, clear for next tx
_bank SMTP_BANK
cje smtpState, #SMTP_HELO, :smtpSndHelo ; chk if have to
; tx HELO
cje smtpState, #SMTP_MAIL, :smtpSndMail ; chk if have to
; tx MAIL FROM
cje smtpState, #SMTP_RCPT, :smtpSndRcpt ; chk if have to
; tx RCPT TO
cje smtpState, #SMTP_DATA, :smtpSndData ; chk if have to
; tx DATA
cje smtpState, #SMTP_TEXT, :smtpSndText ; chk if have to
; tx msg text
cje smtpState, #SMTP_QUIT, :smtpSndQuit ; chk if have to
; tx QUIT
cje smtpState, #SMTP_CLOSE, :smtpClose ; chk if have to
; close
;retp ; nothing?
:smtpSndHelo mov smtpStrPointer, #(SMTPTEXT_HELO) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(SMTPTEXT_HELO_END-SMTPTEXT_HELO)
retp
:smtpSndMail mov smtpStrPointer, #(SMTPTEXT_MAIL) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(SMTPTEXT_MAIL_END-SMTPTEXT_MAIL)
retp
:smtpSndRcpt mov smtpStrPointer, #(SMTPTEXT_RCPT) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(SMTPTEXT_RCPT_END-SMTPTEXT_RCPT)
retp
:smtpSndData mov smtpStrPointer, #(SMTPTEXT_DATA) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(4+2) ; DATA plus <CRLF>
retp
:smtpSndText mov smtpStrPointer, #(SMTPTEXT_TEXT) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(SMTPTEXT_TEXT_END-SMTPTEXT_TEXT)
retp
:smtpSndQuit mov smtpStrPointer, #(SMTPTEXT_QUIT) ; make a pointer to the
; char string.
bank TCP_BANK
mov tcp1UnAckLSB, #(4+2) ; QUIT plus <CRLF>
retp
:smtpClose ; 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 SMTP
_bank SMTP_BANK
cje smtpState, #SMTP_HELO, :smtpTxHelo ; chk if we have
; to tx HELO
cje smtpState, #SMTP_MAIL, :smtpTxMail ; chk if we have
; to tx MAIL FROM
cje smtpState, #SMTP_RCPT, :smtpTxRcpt ; chk if we have
; to tx RCPT TO
cje smtpState, #SMTP_DATA, :smtpTxData ; chk if we have
; to tx DATA
cje smtpState, #SMTP_TEXT, :smtpTxText ; chk if we have
; to tx msg text
cje smtpState, #SMTP_QUIT, :smtpTxQuit ; chk if we have
; to tx QUIT
retp ; nothing?
; we have to send a HELO<CRLF>
:smtpTxHelo mov w, #(SMTPTEXT_HELO >> 8) ; upper nibble
jmp smtpRdPrgMemData
; we have to send a MAIL FROM: <sender>
:smtpTxMail mov w, #(SMTPTEXT_MAIL >> 8) ; upper nibble
jmp smtpRdPrgMemData
; we have to send a RCPT TO: <recipient>
:smtpTxRcpt mov w, #(SMTPTEXT_RCPT >> 8) ; upper nibble
jmp smtpRdPrgMemData
; we have to send a DATA
:smtpTxData mov w, #(SMTPTEXT_DATA >> 8) ; upper nibble
jmp smtpRdPrgMemData
; we have to send the email text
:smtpTxText mov w, #(SMTPTEXT_TEXT >> 8) ; upper nibble
jmp smtpRdPrgMemData
; we have to send a QUIT
:smtpTxQuit mov w, #(SMTPTEXT_QUIT >> 8) ; upper nibble
jmp smtpRdPrgMemData
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 SMTP
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 SMTP
; ******************************************************************************
smtpRdPrgMemData ; not relocatable. Must be in same page as caller
; Reads SMTP Data stored in program memory.
; INPUT: w contains upper nibble of prog memory location.
; smtpPointer contains lower byte of prog mem location.
; OUTPUT: Data in w register
; ******************************************************************************
mov m, w ; into MODE register
mov w, smtpStrPointer ; lower byte (2 nibbles)
inc smtpStrPointer ; 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 SMTP
smtpCompare220 jmp _smtpCompare220
smtpCompare221 jmp _smtpCompare221
smtpCompare250 jmp _smtpCompare250
smtpCompare354 jmp _smtpCompare354
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 SMTP
; ******************************************************************************
_smtpCompare220
; Compares the SMTP reply code in memory against 220
; INPUT: smtpReplyCode
; OUTPUT: Z = set if reply code is 220
; ******************************************************************************
_bank SMTPREPLY_BANK
mov w, #'2'
xor w, smtpReplyCode3
sz
retp
mov w, #'2'
xor w, smtpReplyCode2
sz
retp
mov w, #'0'
xor w, smtpReplyCode1
retp
; ******************************************************************************
_smtpCompare221
; Compares the SMTP reply code in memory against 221
; INPUT: smtpReplyCode
; OUTPUT: Z = set if reply code is 220
; ******************************************************************************
_bank SMTPREPLY_BANK
mov w, #'2'
xor w, smtpReplyCode3
sz
retp
mov w, #'2'
xor w, smtpReplyCode2
sz
retp
mov w, #'1'
xor w, smtpReplyCode1
retp
; ******************************************************************************
_smtpCompare250
; Compares the SMTP reply code in memory against 250
; INPUT: smtpReplyCode
; OUTPUT: Z = set if reply code is 220
; ******************************************************************************
_bank SMTPREPLY_BANK
mov w, #'2'
xor w, smtpReplyCode3
sz
retp
mov w, #'5'
xor w, smtpReplyCode2
sz
retp
mov w, #'0'
xor w, smtpReplyCode1
retp
; ******************************************************************************
_smtpCompare354
; Compares the SMTP reply code in memory against 354
; INPUT: smtpReplyCode
; OUTPUT: Z = set if reply code is 220
; ******************************************************************************
_bank SMTPREPLY_BANK
mov w, #'3'
xor w, smtpReplyCode3
sz
retp
mov w, #'5'
xor w, smtpReplyCode2
sz
retp
mov w, #'4'
xor w, smtpReplyCode1
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 SMTP
_bank SMTP_BANK
cje smtpState, #SMTP_CONNECT, :smtpCon ; check if we
; connected
cje smtpState, #SMTP_HELO, :smtpHelo ; check if we
; sent a HELO
cje smtpState, #SMTP_MAIL, :smtpMail ; check if we
; sent a MAIL FROM
cje smtpState, #SMTP_RCPT, :smtpRcpt ; check if we
; sent a RCPT TO
cje smtpState, #SMTP_DATA, :smtpData ; check if we
; sent a DATA
cje smtpState, #SMTP_TEXT, :smtpText ; check if we
; sent the msg text
cje smtpState, #SMTP_QUIT, :smtpQuit ; check if we
; sent a QUIT
retp ; nothing?
; we have established a tcp conn with a remote smtp
:smtpCon call @smtpCompare220 ; check if we got 220
sz
jmp :smtpWeQuit ; no, we got something else
; we got a 220 service ready reply
jmp :smtpNxtState
; we have received a response to our HELO sent.
; We expect it to be 250
:smtpHelo call @smtpCompare250 ; check if we got 250
sz
jmp :smtpWeQuit ; no, we got something else
; we got a 250 requested mail action ok, completed reply
jmp :smtpNxtState
; we have received a response to our MAIL sent.
; We expect it to be 250
:smtpMail call @smtpCompare250 ; check if we got 250
sz
jmp :smtpWeQuit ; no, we got something else
; we got a 250 requested mail action ok, completed reply
jmp :smtpNxtState
; we have received a response to our RCPT sent.
; We expect it to be 250
:smtpRcpt call @smtpCompare250 ; check if we got 250
sz
jmp :smtpWeQuit ; no, we got something else
; we got a 250 requested mail action ok, completed reply
jmp :smtpNxtState
; we have received a response to our DATA sent.
; We expect it to be 354
:smtpData call @smtpCompare354 ; check if we got 354
sz
jmp :smtpWeQuit ; no, we got something else
; we got a 354 start mail input, end with <CRLF>.<CRLF> reply
jmp :smtpNxtState
; we have received a response to the text we sent.
; We expect it to be 250
:smtpText call @smtpCompare250 ; check if we got 250
sz
jmp :smtpWeQuit ; no, we got something else
; we got a 250 message accepted reply
setb flags3.SMTP_OK ; indicate mail went through
jmp :smtpNxtState
; we have received a response to the QUIT we sent.
; We expect it to be 221
:smtpQuit call @smtpCompare221 ; check if we got 221
jz :smtpQuitGood
_bank SMTP_BANK
clr smtpState
call @TCPAppClose ; no, we have to close now or
; will be here forever
retp
; we got a 221 service closing transmission channel reply
:smtpQuitGood _bank SMTP_BANK
clr smtpState ; change state to CLOSE, remote
; SMTP will close tcp
retp
; we got a response we can't handle.
; We will send a QUIT to let the remote
; SMTP Do a tcp close
:smtpWeQuit _bank SMTP_BANK
mov w, #SMTP_QUIT ; change state to
; QUIT must be sent
mov smtpState, w
setb flags2.SMTP_TXEN
retp
; go to next SMTP state
:smtpNxtState _bank SMTP_BANK
inc smtpState
setb flags2.SMTP_TXEN ; enable smtp transmit so that
; a new command
; or text can be sent
retp
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 SMTP
; 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 SMTP, so we
; can try again.
jnb flags3.SMTP_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.SMTP_OK ; clear the 'mail went through' indicator
; before we send it
_bank SMTP_BANK
mov w, #SMTP_CONNECT ; start SMTP state machine to connect
mov smtpState, w
; set up local & remote ports for tcp1 connection
_bank TCB1_BANK
mov tcb1LocalPortLSB, #100
mov tcb1LocalPortMSB, #100
mov tcb1RemotePortLSB, #SMTP_PORT_LSB
mov tcb1RemotePortMSB, #SMTP_PORT_MSB
; fill in remote IP to connect with in tcp socket
_bank TCPSOCKET_BANK
mov sock1RemoteIP3, #SMTP_SERVER_IP3
mov sock1RemoteIP2, #SMTP_SERVER_IP2
mov sock1RemoteIP1, #SMTP_SERVER_IP1
mov sock1RemoteIP0, #SMTP_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
file: /Techref/scenix/lib/io/osi3/tcpip/isx_2_3_5.src, 177KB, , updated: 2002/2/21 11:32, local time: 2024/11/19 10:38,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/Techref/scenix/lib/io/osi3/tcpip/isx_2_3_5.src"> scenix lib io osi3 tcpip isx_2_3_5</A> |
Did you find what you needed?
|