; 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/30 03:18,
3.15.211.71:LOG IN
|
©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://techref.massmind.org/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? |
Welcome to massmind.org! |
Welcome to techref.massmind.org! |
.