; isx_1_6_8.src ; Wing Poon, Chris Waters, Deon Roelofse . V1.6.8 . 1/9/01 . (C) Scenix, Inc. ; SX Ethernet TCP/IP Stack. Protocols implemented: ARP, DHCP, IP/ICMP, UDP, TCP, ; HTTP. ; ****************************************************************************** ; 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 ; line 150 for important information! ; 3. If using SX-ISD, you need SASM.EXE V1.45.5 and SXIDE.EXE V1.07.03 or ; greater. Pls go to line 150 for important information! ; 4. The schematics for the board that runs this code is available, pls ; contact sales@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 iSX, pls ; contact sales@scenix.com . You'll need the Microsoft Java Virtual ; Machine Build 3240, or greater (installed by default with IE5). ; ****************************************************************************** ; Program Memory Usage Summary (/w DHCP): ; Page | Usage ; ----------------------------- ; 0: 000-0AF, 190-1F1 (53%) ; 1: 200-3FB (100%) ; 2: 400-5FD (100%) ; 3: 600-7FD (100%) ; 4: 800-96D (71%) ; 5: A00-B5B (68%) ; 6: C00-D38 (61%) ; 7: E00-E25 (7%) ; Data Memory Usage Summary (/w DHCP): ; Bank | Usage ; ----------------------------- ; 0: - (0%) ; 1: 0-F (100%) ; 2: 0-E (94%) ; 3: 0-F (100%) ; 4: 0-F (100%) ; 5: 0-F (100%) ; 6: 0-A (73%) ; 7: 0-C (81%) ; 8: 0-A (73%) ; 9: - (0%) ; A: - (0%) ; B: - (0%) ; C: - (0%) ; D: - (0%) ; E: - (0%) ; F: - (0%) ; This program implements an embedded web-server. The server will respond to ; HTTP requests and surfs up the specified web resource stored on an external ; serial EEPROM. The IP address of the webserver is http://10.1.1.20, if ; the DHCP option is disabled (default). In addition, the iSX can be pinged ; ("ping 10.1.1.20") using the standard PC/Unix ping utility. Finally, the iSX ; can also be controlled, using UDP, by the user, using a Java program, ; udpsx.class, available from Scenix. ; How to Setup your PC to talk with the iSX Board ; ----------------------------------------------- ; 1. Open a DOS Command Prompt Window ; 2. Type "route print" ; 3. If you see a line like the one below, skip step [4] and [5] ; Network Address Netmask Gateway Address Interface Metric ; 10.1.1.0 255.255.255.0 w.x.y.z w.x.y.z 1 ; (in the above, "w.x.y.z" is any non-zero IP address) ; 4. Find out the IP address of the PC's Ethernet Interface. Type the following ; command: "ipconfig" ; Look for the line "IP Address. . .", given under a section called "Ethernet ; adapter :". This is the Ethernet IP address of the PC. ; The IP address must not be 0.0.0.0 ; If you don't find a non-zero Ethernet IP address, follow the instructions ; Given in the section "How to give your PC a Static IP Address" and restart ; your computer. ; 5. Update the PC's routing table: Issue the following command: ; "route add 10.1.1.0 mask 255.255.255.0 <PC_ETHERNET_IP_ADDR>" ; Be sure to substitute the IP address found in step [4] for the field ; <PC_ETHERNET_IP_ADDR> ; 6. Power-up the iSX board and connect it to the PC using the supplied cross- ; over cable. ; 7. Ping the iSX to see if it the connection is alive: "ping 10.1.1.20" ; If you get a "Request timed out" message, the connection was unsucessful; ; try typing "arp -s 10.1.1.20 00-00-00-00-00-01 <PC_ETHERNET_IP_ADDR>" at the ; DOS prompt and try pinging the iSX again. ; If you get a "Reply from 10.1.1.20: ..." message, the connection is alive. ; 8. Start your web-browser (IE5 works best). Type in the following ; http://10.1.1.20 ; 9. If you successfully pinged the iSX board in step [7] but failed to load the ; web page in step [8], do the following: ; In IE, go to Tools->Internet Options. Click on the "Use Blank" button, then ; hit "OK". Exit and restart IE. ; In Netscape, go to Edit->Preferences. Click on the "Blank Page" radio button, ; then hit "OK". Exit and restart Netscape. ; Repeat step [8]. ; ; How to give your PC a Static IP Address (optional) ; -------------------------------------------------- ; 1. Right-click on the "Network Neighborhood" icon on the Windows Desktop. ; Select Properties. ; 2. Select the "TCP/IP -> <YOUR_ETHERNET_ADAPTER>" entry from the scroll-box. ; Click the Properties button. ; 3. Click on the "IP Address" tab. Select "Specify an IP Address". Enter ; "10.1.1.1" for the "IP Address" (This will be the Ethernet IP address of ; the PC). Enter "255.255.255.0" for the "Subnet Mask". Click on OK and ; restart the computer. ; 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 ; ************** ; *** DEVICE *** ; ************** ; Parallax -- uncomment the following 2 lines if, and only if, using Parallax's SX-Key DEVICE OSCHS2, DRT60MS FREQ 48_000_000 ; have to debug at freq != resonant freq ; SASM -- uncomment the following line if, and only if, using Advance Transdata's SX-ISD ; DEVICE SX52BD, OSCHS2, WDRT60 RESET Main ID 'iSX_167' ; *************************** ; *** CONDITIONAL DEFINES *** ; *************************** DHCP = 0 ; 1 = DHCP enabled, 0 = DHCP disabled CREDENCE = 0 ; 1 = Credence board, 0 = Scenix board ; ***************** ; *** VARIABLES *** ; ***************** ; *** Global *** GLOBAL_ORG = $0A flags EQU GLOBAL_ORG+0 ; various flags used by TCP/IP stack arpFlags EQU GLOBAL_ORG+1 ; ARP status flags globTemp1 EQU GLOBAL_ORG+2 ; not preserved across any function globTemp2 EQU GLOBAL_ORG+3 ; not preserved across any function globTemp3 EQU GLOBAL_ORG+4 ; preserved across some functions ; *** Bank 0 *** ; (Don't use this bank -- it's bad for your mental health) 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 ; " ; *** Bank 3 *** ORG $30 UDP_BANK = $ ; make sure UDP_BANK[7] = NIC_BANK[7] 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 ; " DHCP_BANK = $ dhcpServerId3 DS 1 ; DHCP <server_identifier> = IP addr of DHCP server dhcpServerId2 DS 1 ; " dhcpServerId1 DS 1 ; " dhcpServerId0 DS 1 ; " ; *** Bank 4 *** ORG $40 TCP_BANK = $ ; make sure TCP_BANK[7] = NIC_BANK[7] tcpState DS 1 ; 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 tcpTmpAck4 DS 1 ; TMP.ACK tcpTmpAck3 DS 1 ; temporary information from the received packet tcpTmpAck2 DS 1 tcpTmpAck1 DS 1 tcpUnAckMSB DS 1 ; number of unacknowledged bytes tcpUnAckLSB 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 = tcpUnAckMSB ; number of bytes app wants to transmit tcpAppTxBytesLSB = tcpUnAckLSB ; " tcpAppRxBytesMSB = tcpLengthMSB ; number of bytes app will be receiving tcpAppRxBytesLSB = tcpLengthLSB ; " ; *** Bank 5 *** ORG $50 TCB_BANK = $ ; make sure TCB_BANK[7] = NIC_BANK[7] ; The ordering of these variables is significant. It is the same as the TCP ; header. This simpifies the send-packet code tcbLocalPortMSB DS 1 ; source port tcbLocalPortLSB DS 1 ; " tcbRemotePortMSB DS 1 ; destination port tcbRemotePortLSB DS 1 ; " tcbSndUna4 DS 1 ; SND.UNA: oldest unacknowledged byte tcbSndUna3 DS 1 ; " tcbSndUna2 DS 1 ; " tcbSndUna1 DS 1 ; " tcbRcvNxt4 DS 1 ; RCV.NXT: next byte to receive tcbRcvNxt3 DS 1 ; " tcbRcvNxt2 DS 1 ; " tcbRcvNxt1 DS 1 ; " tcbOffset DS 1 ; length of the TCP options tcbFlags DS 1 ; flags field tcbSendWinMSB DS 1 ; send window tcbSendWinLSB DS 1 ; " TCB_END = $ ; *** 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 ; *** 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 ; " tcpTimerMSB DS 1 ; TCP-timer count tcpTimerLSB DS 1 ; " connTimerMSB DS 1 ; Connection-timer count connTimerLSB DS 1 ; " 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 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 8 *** ORG $80 MISC_BANK = $ ledPort DS 1 ; records the state of the led port 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 ; *** Bank 9 *** ORG $90 ; *** Bank A *** ORG $A0 ; *** Bank B *** ORG $B0 ; *** 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 *** IF CREDENCE RA_DIR = %11110011 RA_OUT = %00000000 RA_LVL = %11111111 RA_PLP = %11111111 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 = %00000010 RD_OUT = %00000011 RD_LVL = %11111111 RD_PLP = %11111100 RE_DIR = %00000000 RE_OUT = %00000000 RE_LVL = %11111111 RE_PLP = %11111111 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 = rd E2SCL = 0 E2SDA = 1 E2SCL_PIN = E2_PORT.E2SCL E2SDA_PIN = E2_PORT.E2SDA LED_PORT = re LED = 0 LED_PIN = LED_PORT.LED ELSE 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 ENDIF ; *** 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 ; *** arpFlags *** 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 ARP_BYPASS = 3 ; indicates that the outgoing packet should not be checked ; *** NIC Constants *** RXBUF_START = $40 ; 4608 byte receive buffer (3 max-size packets) RXBUF_END = $53 ; " TXBUF1_START = $53 ; 1536 byte transmit buffer for ICMP/UDP TXBUF2_START = $59 ; 1536 byte transmit buffer for TCP TXBUF3_START = $5F ; 256 byte transmit buffer for ARP ; *** Ethernet Constants *** SX_ETH_ADDR0 = 0 ; SX's Ethernet Phy MAC Address SX_ETH_ADDR1 = 0 ; " SX_ETH_ADDR2 = 0 ; " SX_ETH_ADDR3 = 0 ; " SX_ETH_ADDR4 = 0 ; " SX_ETH_ADDR5 = 1 ; " ; *** ARP Constants *** ARP_TIMEOUT = 5 ; ARP response timeout period. Must be smaller than other timeouts! ; *** IP Constants *** SX_IP_ADDR3 = 10 ; SX's static IP address (if DHCP disabled) SX_IP_ADDR2 = 1 ; " SX_IP_ADDR1 = 1 ; " SX_IP_ADDR0 = 20 ; " IP_TTL = 32 ; *** 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 TCP_CONN_EXP = 80 ; TCP connection timeout period ; *** HTTP Constants *** ; States for parsing HTTP headers HTTP_PORT_MSB = 0 ; 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" ; *** 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 IF CREDENCE E2_DDR_SDA_IN = (RD_DIR | E2_SDA_MASK) ; direction of SDA port when SDA is input E2_DDR_SDA_OUT = (RD_DIR & (~E2_SDA_MASK)) ; direction of SDA port when SDA is output 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 ENDIF ; *** ADC Constants *** ADC_PORT = re ADC_OUT = 7 ADC_IN = 6 ADC_OUT_PIN = ADC_PORT.ADC_OUT ADC_IN_PIN = ADC_PORT.ADC_IN ; ************** ; *** 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 software. 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 software. 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 jmp :timerEnd :tcpTimer ; TCP-timer (used for TCP re-transmission timeouts) incsz tcpTimerLSB jmp :connTimer inc tcpTimerMSB :connTimer ; Connection-timer (used for TCP connection timeouts) incsz connTimerLSB jmp :arpTimer inc connTimerMSB :arpTimer ; ARP-timer (used for ARP response timeouts) incsz arpTimerLSB jmp :timerEnd inc arpTimerMSB :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 intefere if E2 access in ; in progress because LED and E2 may share same port _bank HTTP_BANK test httpParseState jnz :ledBlinkerEnd bank TIMER_BANK mov w, tcpTimerMSB and w, #%00001111 jnz :ledHere _bank MISC_BANK movb LED_PORT.LED, /ledPort.LED jmp :ledBlinkerEnd :ledHere mov w, tcpTimerMSB and w, #%00001111 xor w, #1 jnz :ledBlinkerEnd _bank MISC_BANK movb LED_PORT.LED, ledPort.LED :ledBlinkerEnd ISRExit mov w, #-INT_PERIOD retiw ; ******************** ; *** MAIN PROGRAM *** ; ******************** Init jmp _Init ARPInit jmp _ARPInit TCPIPInit jmp _TCPIPInit E2Init jmp _E2Init StartupDelay jmp _StartupDelay Main call @Init ; DHCP Dynamic IP Address solicitation IF DHCP ; initialize UDP receive port for DHCP _bank UDP_BANK mov udpRxDestPortMSB, #0 ; DHCP(BOOTP) Client mov udpRxDestPortLSB, #68 ; ; send DHCPDISCOVER message to find DHCP server(s) call @DHCPDISCOVERSend ; wait for DHCPOFFER :dhcpWaitOffer call @NICWaitRxFrame call @CheckIPDatagram jnb flags.RX_IS_UDP, :dhcpWaitOffer call @UDPProcPktIn sb flags.GOT_DHCP_OFFER jmp :dhcpWaitOffer ; send DHCPREQUEST message call @DHCPREQUESTSend ; wait for DHCPACK :dhcpGotOffer call @NICWaitRxFrame call @CheckIPDatagram jnb flags.RX_IS_UDP, :dhcpGotOffer call @UDPProcPktIn sb flags.GOT_IP_ADDR jmp :dhcpGotOffer :dhcpGotAck ; hallelujah! ENDIF call @UDPAppInit _bank MISC_BANK clr pageCount ; clear page counter (dynamic data) mov ledPort, LED_PORT ; main program loop :mainLoop call @NICCheckRxFrame jz :noRxFrame 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 call @NICDumpRxFrame ; not something we recognize, so dump it 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 TCPState, #TCP_ST_CLOSED, :tcpClosed cje TCPState, #TCP_ST_LISTEN, :tcpListen bank TIMER_BANK cjae connTimerMSB, #TCP_CONN_EXP, :resetTCP sb arpFlags.ARP_REQ_SENT ; do not allow new tx if waiting for ARP response call @TCPTransmit ; check if app has anything to transmit jmp :mainLoop :tcpClosed call @TCPAppInit jmp :mainLoop :tcpListen bank TIMER_BANK clr connTimerMSB jmp :mainLoop :resetTCP ; reset hung TCP connection bank TCP_BANK clr tcpUnAckMSB clr tcpUnAckLSB mov tcpState, #TCP_ST_CLOSED bank HTTP_BANK clr httpParseState jmp :mainLoop ; ******************* ; *** SUBROUTINES *** ; ******************* ORG $190 ; 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 call @NICInit call @ARPInit call @TCPIPInit call @E2Init mov w, #(RTCC_PS_OFF) ; setup option register mov !option, w retp ; ****************************************************************************** _ARPInit ; ARP initialization code ; INPUT: none ; OUTPUT: none ; ****************************************************************************** clr arpFlags _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 tcpUnAckMSB clr tcpUnAckLSB mov tcpState, #TCP_ST_CLOSED retp ; ****************************************************************************** _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 ; ****************************************************************************** _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 ORG $200 ; Page1 NICSendTxFrame jmp _NICSendTxFrame 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 call @StartupDelay ; give it a little time to come out of POR mov nicIOAddr, #$1F ; write to reset port 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, #%11000100 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 jmp NICWrite ; ****************************************************************************** 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, #%00100010 ; Page0, abort DMA call NICWrite mov nicIOAddr, #$07 ; ISR call NICRead ;jb wreg.4, :overflowed ; OVW (bit set when rx buffer overflowed) DEBUG jb wreg.4, @Main ; OVW (bit set when rx buffer overflowed)-> reset clr nicIOAddr ; CR mov w, #%01000010 ; Page1 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 ;:overflowed mov w, #(RTCC_ID) ; DEBUG ; mov !option, w ; DEBUG ; clrb LED_PIN ; DEBUG ; jmp $ ; DEBUG ; ****************************************************************************** NICWaitRxFrame ; Wait for an ethernet frame to be received. ; INPUT: none ; OUTPUT: nicCurrPktPtr = points to beginning of packet just received ; nicNextPktPtr = points to beginning of next packet ; nicRemoteEth0-5 = source (remote) ethernet address ; ****************************************************************************** _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 jz :loop ; CURR = BNRY => no packets 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 jmp NICWrite ; ****************************************************************************** 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 jmp NICWrite ; ****************************************************************************** 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> jmp NICWriteSrcEth ; ****************************************************************************** _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 ; ****************************************************************************** ; sometimes we need to bypass ARP (e.g. broadcast IP addr) jb arpFlags.ARP_BYPASS, :here ; otherwise we need to check if we know the MAC mapping for the IP addr call @ARPCheckCache ; Start ARP snb arpFlags.ARP_REQ_SENT ; Continue if an ARP request was not sent ; or if a stalled packet is to be sent retp ; exit, ARP request was sent. packet to be stalled :here 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 jmp NICWrite ; ****************************************************************************** _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 jmp NICWrite ; ****************************************************************************** _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 jmp NICRead ; ****************************************************************************** _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> jmp NICWriteDestIP ; ****************************************************************************** _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 bank IP_BANK mov w, myIP3 call NICWrite bank IP_BANK mov w, myIP2 call NICWriteAgain mov w, myIP1 call NICWriteAgain mov w, myIP0 jmp NICWriteAgain :noIP mov w, #0 ; 0.0.0.0 call NICWrite mov w, #0 call NICWriteAgain call NICWriteAgain jmp NICWriteAgain ; ****************************************************************************** _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 jmp NICWriteAgain ; ****************************************************************************** _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 jmp NICWriteAgain ; ****************************************************************************** _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 jmp NICWriteAgain ; ****************************************************************************** _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 jmp NICWrite ORG $400 ; Page2 ARPSendResponse jmp _ARPSendResponse ARPSendStPacket jmp _ARPSendStPacket CheckIPDatagram jmp _CheckIPDatagram CheckIPDestAddr jmp _CheckIPDestAddr ICMPProcPktIn jmp _ICMPProcPktIn ; ****************************************************************************** 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 arpFlags.ARP_STL_TX, :cacheGo ; do not check cache if stalled packet to be txed call ARPUpdateEthAddr ; now update stalled packet's ethernet address clr arpFlags ; 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 arpFlags.ARP_REQ_SENT ; indicate an ARP request is sent jmp ARPSendRequest ; do an ARP request now to get the remote Eth address :cacheMatch clr arpFlags ; reset ARP jmp ARPUpdateEthAddr ; check, update remote Eth address of pending packet in NIC ; ****************************************************************************** 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 ; stPktTxBufStart contains page number of stalled pkt call NICWrite_2 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 :rcvdARPRequest ; ignore <sender_HA> call NICPseudoRead6_2 ; if a TCP connection has been established, check to see if remote ; IP is the one we've established connection with, otherwise, make ; sure {remoteIP3-0} isn't clobbered bank TCP_BANK cjne tcpState, #TCP_ST_ESTABED, :senderIP ; check <source_IP> mov fsr, #remoteIP3 call ARPCompare4 jnz :outtaHere jmp :targetHA ; record the sender's IP addr (<sender_IP>) :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 arpFlags.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 arpFlags.ARP_RSP_RCVD ; indicate we got a successfull ARP response :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, #TXBUF3_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, #TXBUF3_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 arpFlags.ARP_REQ_SENT ; check if we are waiting for an ARP response retp ; no, nothing to do here jnb arpFlags.ARP_RSP_RCVD, :checkARPTimeout ; if no ARP response rcvd, check if timeout ; yes we have rcvd a response so now we can send the stalled packet clr arpFlags ; reset ARP setb arpFlags.ARP_STL_TX ; indicate that stalled packet is to be transmitted ; 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 clr arpFlags ; yes, reset ARP flags ; Very important now! 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 match in the ARP cache since the original IP is still there! 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 ; ****************************************************************************** mov w, #~((1<<RX_IS_ICMP)|(1<<RX_IS_UDP)|(1<<RX_IS_TCP)|(1<<RX_IS_IP_BCST)) 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 ; if a TCP connection has been established, check to see if remote ; IP is the one we've established connection with, otherwise, make ; sure {remoteIP3-0} isn't clobbered bank TCP_BANK cjne tcpState, #TCP_ST_ESTABED, :srcIP ; check <source_IP> bank IP_BANK call NICReadAgain_2 xor w, remoteIP3 jnz :outtaHere call NICReadAgain_2 xor w, remoteIP2 jnz :outtaHere call NICReadAgain_2 xor w, remoteIP1 jnz :outtaHere call NICReadAgain_2 xor w, remoteIP0 jnz :outtaHere jmp :destIP ; 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 ORG $600 ; Page3 TCPTransmit jmp _TCPTransmit TCPReTransmit jmp _TCPReTransmit TCPAppPassiveOpen jmp _TCPAppPassiveOpen TCPAppActiveOpen jmp _TCPAppActiveOpen TCPAppClose jmp _TCPAppClose ; ****************************************************************************** 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 ; ****************************************************************************** NICDumpRxFrame_3 ; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page3) ; ****************************************************************************** jmp @NICDumpRxFrame ; ****************************************************************************** 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 :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 ; TCP protocol xor w, ipProtocol sz mov w, #TXBUF1_START ; default tx buffer is TXBUF1 snz mov w, #TXBUF2_START ; unless it's TCP, then we use TXBUF2 bank ARP_BANK sb arpFlags.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 ; ****************************************************************************** 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 the special states bank TCP_BANK cje tcpState, #TCP_ST_CLOSED, :CLOSED cje tcpState, #TCP_ST_LISTEN, :LISTEN cje tcpState, #TCP_ST_SYNSENT, :SYNSENT cje tcpState, #TCP_ST_FINWAIT1, :FINWAIT1 cje tcpState, #TCP_ST_FINWAIT2, :FINWAIT2 cje tcpState, #TCP_ST_LASTACK, :LASTACK ; check the flags bank TCB_BANK snb tcbFlags.TCP_FLAG_RST ; is the reset flag set? jmp :gotoClosed ; yes, drop packet and close the connection snb tcbFlags.TCP_FLAG_SYN ; is the SYN bit set? jmp @TCPSendReset ; yes, drop the packet and send a reset sb tcbFlags.TCP_FLAG_ACK ; is the ACK bit set? jmp NICDumpRxFrame_3 ; no, drop the packet call @TCPChkSeq ; check if received packet is expected ; we only accept ACKs of complete packets. Assume the ACK is for our last packet bank TCP_BANK mov tcpState, #TCP_ST_ESTABED ; switch to the established state :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 application _bank TCP_BANK ; _bank cus we called TCPAppRxDone priorly jb tcpRxFlags.TCP_FLAG_FIN, :doClose ; if FIN bit set, close the connection jmp @TCPSendAck :checkFIN bank TCP_BANK jb tcpRxFlags.TCP_FLAG_FIN, :doClose; is the FIN bit set? jmp NICDumpRxFrame_3 :doClose call @TCPIncRcvNxt ; ACK the FIN bank TCP_BANK mov tcpState, #TCP_ST_CLOSEWAIT ; change state jmp @TCPSendAck :gotoClosed bank TCP_BANK mov tcpState, #TCP_ST_CLOSED ; go to the closed state jmp NICDumpRxFrame_3 ; discard received packet :FINWAIT1 call NICDumpRxFrame_3 bank TCP_BANK sb tcpRxFlags.TCP_FLAG_ACK ; check for ACK of FIN retp mov tcpState, #TCP_ST_FINWAIT2 ; rcved ACK of FIN retp :FINWAIT2 call NICDumpRxFrame_3 bank TCP_BANK sb tcpRxFlags.TCP_FLAG_FIN ; check for FIN retp mov tcpState, #TCP_ST_CLOSED ; rcved FIN call @TCPIncRcvNxt ; ACK the FIN jmp @TCPSendAck :LASTACK ; ignore the packet ; should check the packet is actually an ACK mov tcpState, #TCP_ST_CLOSED ; go to the closed state jmp NICDumpRxFrame_3 :CLOSED jmp @TCPSendReset ; we shouldn't receive packets while closed :LISTEN call NICDumpRxFrame_3 ; discard received packet bank TCB_BANK snb tcbFlags.TCP_FLAG_RST ; check for an RST retp ; ignore a packet with RST snb tcbFlags.TCP_FLAG_ACK ; check for an ACK jmp @TCPSendReset ; bad ACK, send a RST call @TCPCopySeqToNxt call @TCPSendSynAck bank TCP_BANK mov tcpState, #TCP_ST_SYNRCVED ; change state retp :SYNSENT call NICDumpRxFrame_3 bank TCB_BANK jb tcbFlags.TCP_FLAG_RST, :rst ; is the reset bit set? sb tcbFlags.TCP_FLAG_SYN ; if SYN bit not set, retp ; ignore packet sb tcbFlags.TCP_FLAG_ACK ; if ACK bit not set, retp ; ignore packet ; check that SND.UNA <= SEG.ACK <= SND.NXT bank TCP_BANK mov tcpState, #TCP_ST_ESTABED ; the connection is now estabished bank IP_BANK clr ipLengthMSB ; set the received data length to one mov ipLengthLSB, #1 ; " call @TCPAckUpdate jmp @TCPSendAck :rst bank TCP_BANK mov tcpState, #TCP_ST_CLOSED ; close the TCP 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 cje tcpState, #TCP_ST_SYNSENT, :timeout ; check for SYN timeout cje tcpState, #TCP_ST_CLOSEWAIT, :closeWait cjae tcpState, #TCP_ST_ESTABED, :ok retp :ok test tcpUnAckMSB ; are there any bytes unacknowledged? jnz :timeout test tcpUnAckLSB jnz :timeout cje tcpState, #TCP_ST_FINWAIT1, :finTimeout ; check for FIN timeout :askAppTxData cse tcpState, #TCP_ST_ESTABED retp ; only ask if connection is established call @TCPAppTxBytes ; no, ask the application if it wants to transmit _bank TCP_BANK ; _bank cus we called TCPAppTxBytes priorly mov w, tcpUnAckMSB or w, tcpUnAckLSB jnz :appHasTxData retp ; nope, it doesn't; so we're done here then :appHasTxData ; start a TCP packet mov tcpLengthLSB, tcpUnAckLSB ; tcpLength = # bytes to transmit mov tcpLengthMSB, tcpUnAckMSB bank TCB_BANK mov tcbFlags, #((1<<TCP_FLAG_PSH)|(1<<TCP_FLAG_ACK)) call @TCPCheckSumInit call @TCPStartPktOut ; send the header call @TCPUpdateSeq ; Update the outgoing sequence no ; insert the packet data while computing the checksum over the data bank TCP_BANK mov tcpTmpLSB, tcpUnAckLSB mov tcpTmpMSB, tcpUnAckMSB inc tcpTmpMSB ; so that we can loop easier later :dataLoop call @TCPAppTxData _banky TCP_BANK ; cus we called TCPAppTxData priorly call NICWriteAgain_3 ; 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 mov nicCopySrcMSB, #TXBUF2_START 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 clr tcpTimerMSB clr tcpTimerLSB retp :finTimeout bank TIMER_BANK csae tcpTimerMSB, #TCP_RESTART_EXP ; has the restart timer expired? retp ; no clr tcpTimerMSB ; yes, initialise the restart timer clr tcpTimerLSB jmp @TCPSendFin :timeout bank TIMER_BANK csae tcpTimerMSB, #TCP_RESTART_EXP ; has the restart timer expired? retp ; no clr tcpTimerMSB ; yes, initialise the restart timer clr tcpTimerLSB jmp @TCPReTransmit ; transmit the packet again :closeWait mov tcpState, #TCP_ST_LASTACK jmp @TCPSendFin ; send FIN, ACK ; ****************************************************************************** _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_3 mov nicIOAddr, #$04 ; TPSR mov w, #TXBUF2_START call NICWrite_3 ; read the NIC's TCP transmit buffer to find out the IP <length> ; so that we can re-initialize the NIC's TBCR mov nicCopySrcMSB, #TXBUF2_START 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 ; ****************************************************************************** _TCPAppPassiveOpen ; Do a passive open. i.e. listen for connections on a given port. ; [TCP API Function] ; INPUT: {tcbLocalPortMSB,tcbLocalPortLSB} = TCP port to listen on ; OUTPUT: none ; ****************************************************************************** _bank TCP_BANK mov tcpState, #TCP_ST_LISTEN clr tcpUnAckMSB clr tcpUnAckLSB 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 ; {tcpRemotePortMSB,tcbRemotePortLSB} = remote TCP port ; OUTPUT: none ; ****************************************************************************** _bank TCP_BANK clr tcpUnAckMSB clr tcpUnAckLSB mov tcpState, #TCP_ST_SYNSENT bank TCB_BANK mov tcbFlags, #(1<<TCP_FLAG_SYN) jmp @TCPSendSyn ; ****************************************************************************** _TCPAppClose ; Force the current connection to close ; [TCP API Function] ; INPUT: none ; OUTPUT: none ; ****************************************************************************** _bank TCP_BANK mov tcpState, #TCP_ST_FINWAIT1 clr tcpUnAckMSB clr tcpUnAckLSB bank TCB_BANK mov tcbFlags, #((1<<TCP_FLAG_FIN)|(1<<TCP_FLAG_ACK)) jmp @TCPSendEmptyPkt ORG $800 ; Page4 TCPRxHeader jmp _TCPRxHeader TCPChkSeq jmp _TCPChkSeq TCPRestorePrev jmp _TCPRestorePrev TCPUpdateSeq jmp _TCPUpdateSeq ; ****************************************************************************** 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: {tcbRcvNxt1-4} ; ****************************************************************************** bank IP_BANK mov w, ipLengthLSB bank TCB_BANK add tcbRcvNxt1, w bank IP_BANK mov w, ipLengthMSB snc mov w, ++ipLengthMSB bank TCB_BANK add tcbRcvNxt2, w sc retp incsz tcbRcvNxt3 retp inc tcbRcvNxt4 retp ; ****************************************************************************** TCPIncRcvNxt ; Increment RCV.NXT by one ; INPUT: none ; OUTPUT: {tcbRcvNxt1-4} ; ****************************************************************************** bank TCB_BANK inc tcbRcvNxt1 sz retp incsz tcbRcvNxt2 retp incsz tcbRcvNxt3 retp inc tcbRcvNxt4 retp ; ****************************************************************************** TCPIncSndUna ; Increment SND.UNA by one ; INPUT: none ; OUTPUT: {tcbSndUna1-4} ; ****************************************************************************** bank TCB_BANK inc tcbSndUna1 sz retp incsz tcbSndUna2 retp incsz tcbSndUna3 retp inc tcbSndUna4 retp ; ****************************************************************************** TCPCopySeqToNxt ; Copy {tcpTmpSeq4-1} -> {tcbRcvNxt4-1} ; INPUT: {tcpTmpSeq4-1} ; OUTPUT: {tcbRcvNxt4-1} ; ****************************************************************************** bank TCP_BANK mov w, tcpTmpSeq4 bank TCB_BANK mov tcbRcvNxt4, w bank TCP_BANK mov w, tcpTmpSeq3 bank TCB_BANK mov tcbRcvNxt3, w bank TCP_BANK mov w, tcpTmpSeq2 bank TCB_BANK mov tcbRcvNxt2, w bank TCP_BANK mov w, tcpTmpSeq1 bank TCB_BANK mov tcbRcvNxt1, w 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 @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 ; {tcbRcvNxt4-1} = RCV.NXT ; OUTPUT: z is set if RCV.NXT == SEG.SEQ ; ****************************************************************************** bank TCB_BANK mov w, tcbRcvNxt1 bank TCP_BANK xor w, tcpTmpSeq1 sz retp bank TCB_BANK mov w, tcbRcvNxt2 bank TCP_BANK xor w, tcpTmpSeq2 sz retp bank TCB_BANK mov w, tcbRcvNxt3 bank TCP_BANK xor w, tcpTmpSeq3 sz retp bank TCB_BANK mov w, tcbRcvNxt4 bank TCP_BANK xor w, tcpTmpSeq4 retp ; ****************************************************************************** TCPSendEmptyPkt ; Constructs and sends a TCP packet containing no data ; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt ; {tcbSndUna4-1} = sequence number ; {tcbRcvNxt4-1} = acknowledgement number ; tcbFlags = code flags ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port ; OUTPUT: ; ****************************************************************************** call @TCPCheckSumInit bank TCP_BANK clr tcpLengthMSB clr tcpLengthLSB call @TCPStartPktOut call @NICSendTxFrame bank TIMER_BANK clr tcpTimerMSB clr tcpTimerLSB 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 ; {tcbRcvNxt4-1} = acknowledgement number ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port ; OUTPUT: ; ****************************************************************************** bank TCB_BANK mov tcbFlags, #(1<<TCP_FLAG_RST) call @TCPSendEmptyPkt jmp NICDumpRxFrame_4 ; discard the received pkt ; ****************************************************************************** TCPSendSyn ; Send a SYN packet ; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt ; {tcbRcvNxt4-1} = acknowledgement number ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port ; OUTPUT: {tcpSndUna4-1} = new sequence number ; ****************************************************************************** bank TCB_BANK mov tcbFlags, #(1<<TCP_FLAG_SYN) jmp TCPSendISN ; ****************************************************************************** TCPSendISN ; Send the TCP initial sequence number ; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt ; {tcbRcvNxt4-1} = acknowledgement number ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = 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 bank TCB_BANK mov tcbSndUna4, w mov tcbSndUna3, w mov tcbSndUna2, w mov tcbSndUna1, w 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 ; {tcbRcvNxt4-1} = acknowledgement number ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port ; OUTPUT: {tcpSndUna4-1} = new sequence number ; ****************************************************************************** bank TCB_BANK mov tcbFlags, #((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 ; {tcbSndUna4-1} = sequence number ; {tcbRcvNxt4-1} = acknowledgement number ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port ; OUTPUT: ; ****************************************************************************** bank TCB_BANK mov tcbFlags, #(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 ; {tcbSndUna4-1} = sequence number ; {tcbRcvNxt4-1} = acknowledgement number ; tcbFlags = code flags ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port ; OUTPUT: ; ****************************************************************************** bank TCB_BANK mov tcbFlags, #(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 ; {tcbSndUna4-1} = sequence number ; {tcbRcvNxt4-1} = acknowledgement number ; tcbFlags = code flags ; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port ; {tcbRemotePortMSB,tcbRemotePortLSB} = 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 ; compute IP <header_checksum> call @IPGenCheckSum ; now we're ready to construct the IP header call @IPStartPktOut ; then construct the TCP header ; TCP <source_port>,<destination_port>,<sequence_number>, ; <acknowledgement_number>,<hlen>,<code>,<window> bank TCB_BANK mov tcbOffset, #(TCP_HDR_LENGTH<<4) mov tcbSendWinMSB, #((TCP_WINDOW_SIZE&$FF00)>>8) mov tcbSendWinLSB, #(TCP_WINDOW_SIZE&$00FF) mov globTemp3, #TCB_BANK ; send the TCB fields :loop mov fsr, globTemp3 mov w, indf ; load the value call @TCPTxByte ; transmit and accumulate header checksum inc globTemp3 cse globTemp3, #TCB_END ; is the loop finished? jmp :loop ; 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 ; {tcbRemotePortMSB,tcbRemotePortLSB} ; {tcbSendWinMSB,tcbSendWinLSB} ; tcbOffset ; tcbFlags ; tcpRxFlags ; {tcpTmpSeq4-1} = <sequence_number> ; {tcpTmpAck4-1} = <acknowledgement_number> ; {tcpLengthMSB,tcpLengthLSB} = length of TCP data ; {ipLengthMSB,ipLengthLSB} = length of TCP data ; ****************************************************************************** ; Check port and refuse packet if not for current connection bank TCP_BANK cjne tcpState, #TCP_ST_LISTEN, :checkSrc ; <source_port> :readSrc ; read the source port bank TCB_BANK call NICReadAgain_4 mov tcbRemotePortMSB, w call NICReadAgain_4 mov tcbRemotePortLSB, w jmp :checkDest :checkSrc ; check the source port is for the current connection bank TCB_BANK call NICReadAgain_4 xor w, tcbRemotePortMSB sz ; is the high byte the same? jmp :ignorePacket ; no, ignore the packet call NICReadAgain_4 ; get the low byte xor w, tcbRemotePortLSB sz ; is the low byte the same? jmp :ignorePacket ; no, ignore the packet ; <destination_port> :checkDest ; check the destination port matches our port call NICReadAgain_4 xor w, tcbLocalPortMSB sz ; is the high byte the same? jmp :ignorePacket ; no, ignore the packet call NICReadAgain_4 ; get the low byte xor w, tcbLocalPortLSB sz ; Is the low byte the same? jmp :ignorePacket ; no, ignore the packet ; <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 ; <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 ; <hlen> call NICReadAgain_4 ; receive the data offset. Used to skip the options and w, #TCP_OFFSET_MASK ; mask out the offset bank TCB_BANK mov tcbOffset, w clc rr tcbOffset ; shift right to get the number of bytes rr tcbOffset ; ipLength = tcpLength = length of TCP data mov w, tcbOffset ; size of TCP header in bytes 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 bank TCB_BANK mov tcbFlags, w ; take a copy of the flags ; <window> call NICReadAgain_4 mov tcbSendWinMSB, w ; receive the window call NICReadAgain_4 mov tcbSendWinLSB, w ; <checksum>,<urgent_ptr>,<options> ; skip over these bank TCB_BANK sub tcbOffset, #((TCP_HDR_LENGTH<<2)-4) :loop call NICReadAgain_4 decsz tcbOffset jmp :loop clz retp :ignorePacket ; ignore the rest of the packet. call NICDumpRxFrame_4 ; ## Need to send a reset in response to this packet. ; ## Unfortunately sending a packet would overwrite the TCB. ; ## Need to create a second TCB? stz retp ; ****************************************************************************** _TCPUpdateSeq ; Add an 16-bit number to outgoing Sequence nr ; INPUT: {tcbSndUna1-4}{tcpUnAckMSB/LSB} = number to add ; OUTPUT: {tcbSndUna1-4} ; ****************************************************************************** bank TCP_BANK mov w, tcpUnAckLSB bank TCB_BANK add tcbSndUna1, w bank TCP_BANK mov w, tcpUnAckMSB snc mov w, ++tcpUnAckMSB bank TCB_BANK add tcbSndUna2, w sc retp incsz tcbSndUna3 retp inc tcbSndUna4 retp ; ****************************************************************************** _TCPChkSeq ; This is called in the TCPProcPktIn to check if the received packet is the one ; expected or if it is a previous unacked packet. ; [TCP API Function] ; INPUT: none ; OUTPUT: z flag is set if the received packet is incorrect ; ****************************************************************************** call TCPCmpNxtSeq ; Check if received is expected jz :equal ; z is set if RCV.NXT == SEG.SEQ _bank TCP_BANK test tcpUnAckLSB sz jmp :outstanding test tcpUnAckMSB snz jmp :noOutstanding :outstanding call TCPRestorePrev ; RCV.NXT = RCV.NXT - tcpUnAck call TCPCmpNxtSeq ; Check if received is ack on previous packet jnz :noOutstanding ; z is set if RCV.NXT == SEG.SEQ setb z ; Return value = "OK" retp :equal _bank TCP_BANK clr tcpUnAckLSB clr tcpUnAckMSB ; Z is set (return value = "OK") retp :noOutstanding clrb LED_PIN ; DEBUG TODO jmp $ ; DEBUG ; ****************************************************************************** _TCPRestorePrev ; Subtract an 16-bit number from RCV.NXT ; INPUT: {tcpUnAckLSB,tcpUnAckMSB} = number to add ; OUTPUT: {tcbRcvNxt1-4} ; ****************************************************************************** bank TCP_BANK mov w, tcpUnAckLSB bank TCB_BANK sub tcbRcvNxt1, w bank TCP_BANK mov w, tcpUnAckMSB jc :dontBorrow mov w, ++tcpUnAckMSB ; if prev.sub was negative test w ; carry on inc? jz :exit ; yes (w = $100) :dontBorrow bank TCB_BANK sub tcbRcvNxt2, w snc ; c=0 => negative result retp :exit bank TCB_BANK test tcbRcvNxt3 snz dec tcbRcvNxt4 dec tcbRcvNxt3 retp ORG $A00 ; Page5 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 ; ****************************************************************************** TCPAppInit ; Called repeatedly as long as TCP connection state is closed ; [TCP API Function] ; INPUT: none ; OUTPUT: none ; ****************************************************************************** bank TCB_BANK mov tcbLocalPortLSB, #HTTP_PORT_LSB mov tcbLocalPortMSB, #HTTP_PORT_MSB bank HTTP_BANK clr httpParseState clr httpURIHash jmp @TCPAppPassiveOpen ; ****************************************************************************** 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: {tcpUnAckMSB,tcpUnAckLSB} = number of bytes to transmit ; ****************************************************************************** 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 tcpUnAckMSB, #(HTTP_SEG_SIZE>>8) mov tcpUnAckLSB, #(HTTP_SEG_SIZE&$00FF) retp :lastSegment ; last segment so send whatever is leftover mov w, e2FileLenMSB bank TCP_BANK mov tcpUnAckMSB, w bank EEPROM_BANK mov w, e2FileLenLSB bank TCP_BANK mov tcpUnAckLSB, w bank HTTP_BANK inc httpParseState ; next-state = 3 retp :state3 ; no more to send so we close call @TCPAppClose retp :state4 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 ; ****************************************************************************** bank HTTP_BANK cje httpURIHash, #URI1, :specialFile ; resource.htm cje httpURIHash, #URI2, :specialFile ; temperature.htm call @E2Read8Ack retp :specialFile call @E2Read8Ack mov globTemp1, w ; save temporarily csb globTemp1, #$F0 jmp :match mov w, globTemp1 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 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 ; ****************************************************************************** retp ; ****************************************************************************** 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 ; ****************************************************************************** retp ; ****************************************************************************** TCPAppRxData ; Called once for each byte received in a packet. ; [TCP API Function] ; INPUT: w = received data byte ; OUTPUT: none ; ****************************************************************************** mov globTemp1, w bank HTTP_BANK test httpParseState jz :state0 cje httpParseState, #1, :state1 cjae httpParseState, #2, :state2 :state0 cse globTemp1, #' ' retp inc httpParseState ; next-state = 1 retp :state1 cje globTemp1, #' ', :gotURI add httpURIHash, globTemp1 retp :gotURI ; obtain pointer to file 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 inc httpParseState ; next-state = 2 retp :state2 retp ; ****************************************************************************** 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 ; ****************************************************************************** retp ; ****************************************************************************** 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 ; ****************************************************************************** call E2SDAOutputLo call E2Delay900ns setb E2SCL_PIN call E2Delay600ns clrb E2SCL_PIN call E2Delay900ns retp ; ****************************************************************************** _E2SendNotAck ; Send ACK bit as not-acknowledge ; INPUT: none ; ****************************************************************************** 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 ORG $C00 ; Page6 IF DHCP DHCPREQUESTSend jmp _DHCPREQUESTSend ENDIF ; ****************************************************************************** NICReadAgain_6 ; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page6) ; ****************************************************************************** jmp @NICReadAgain ; ****************************************************************************** NICWriteAgain_6 ; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page6) ; ****************************************************************************** jmp @NICWriteAgain ; ****************************************************************************** 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_6 mov w, udpTxSrcPortLSB call NICWriteAgain_6 ; UDP <destination_port> mov w, udpTxDestPortMSB call NICWriteAgain_6 mov w, udpTxDestPortLSB call NICWriteAgain_6 ; UDP <length> mov w, #8 add w, udpTxDataLenLSB mov w, udpTxDataLenMSB snc inc wreg call NICWriteAgain_6 mov w, #8 add w, udpTxDataLenLSB call NICWriteAgain_6 ; UDP <checksum> = 0 mov w, #$0 call NICWriteAgain_6 call NICWriteAgain_6 retp ; ****************************************************************************** UDPEndPktOut ; Wraps up and transmits the UDP packet ; [UDP API Function] ; INPUT: none ; OUTPUT: none ; ****************************************************************************** _bank NIC_BANK jmp @NICSendTxFrame ; ****************************************************************************** UDPProcPktIn ; Processes an Incoming UDP packet ; INPUT: nicCurrPktPtr = points to beginning of received packet ; OUTPUT: none ; ****************************************************************************** bank UDP_BANK ; UDP <source_port> call NICReadAgain_6 mov udpRxSrcPortMSB, w call NICReadAgain_6 mov udpRxSrcPortLSB, w ; UDP <destination_port> call NICReadAgain_6 xor w, udpRxDestPortMSB jnz :outtaHere call NICReadAgain_6 xor w, udpRxDestPortLSB jnz :outtaHere ; UDP <message_length> call NICReadAgain_6 mov udpRxDataLenMSB, w call NICReadAgain_6 mov udpRxDataLenLSB, w ; ignore UDP <checksum> REPT 2 call NICReadAgain_6 ENDR ; UDP <data> snb flags.RX_IS_IP_BCST call UDPProcBcstPktIn sb flags.RX_IS_IP_BCST call @UDPAppProcPktIn :outtaHere call @NICDumpRxFrame retp IF DHCP ; ****************************************************************************** UDPProcBcstPktIn ; The only kind of broadcast UDP packets accepted are DHCP messages: DHCPOFFER ; and DHCPACK ; INPUT: none ; OUTPUT: myIP0-3 ; ****************************************************************************** clrb flags.GOT_DHCP_OFFER clrb flags.GOT_IP_ADDR call NICReadAgain_6 xor w, #2 ; check <op> = BOOTP reply jnz :outtaHere call NICReadAgain_6 xor w, #1 ; check <htype> = 1 jnz :outtaHere call NICReadAgain_6 xor w, #6 ; check <hlen> = 6 jnz :outtaHere ; ignore <hops> call NICReadAgain_6 ; check <transaction_id> = 0xABABABAB REPT 4 call NICReadAgain_6 xor w, #$AB jnz :outtaHere ENDR ; ignore <seconds>, <flags>, <client_IP> mov globTemp1, #(2+2+4) :loop1 call NICReadAgain_6 decsz globTemp1 jmp :loop1 ; record <your_IP> bank IP_BANK call NICReadAgain_6 mov myIP3, w call NICReadAgain_6 mov myIP2, w call NICReadAgain_6 mov myIP1, w call NICReadAgain_6 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_6 xor w, #53 ; DHCP Message Type jnz :outtaHere call NICReadAgain_6 xor w, #1 jnz :outtaHere call NICReadAgain_6 xor w, #2 ; DHCPOFFER snz setb flags.GOT_DHCP_OFFER xor w, #2 xor w, #5 ; DHCPACK snz setb flags.GOT_IP_ADDR ; now search for that dang(!) <option-server_id> :loop4 call NICReadAgain_6 xor w, #54 ; Server Identifier jz :foundServId call NICReadAgain_6 ; length mov globTemp1, w :loop3 call @NICPseudoRead decsz globTemp1 jmp :loop3 jmp :loop4 :foundServId call NICReadAgain_6 ; ignore length bank DHCP_BANK call NICReadAgain_6 mov dhcpServerId3, w call NICReadAgain_6 mov dhcpServerId2, w call NICReadAgain_6 mov dhcpServerId1, w call NICReadAgain_6 mov dhcpServerId0, w :outtaHere 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 ; tell ARP not to send out an ARP REQUEST for this pkt setb arpFlags.ARP_BYPASS 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_6 ; <htype> mov w, #1 call NICWriteAgain_6 ; <hlen> mov w, #6 call NICWriteAgain_6 ; <hops> mov w, #0 call NICWriteAgain_6 ; <transaction_id> = 0xABABABAB mov w, #$AB REPT 4 call NICWriteAgain_6 ENDR ; <seconds> = 256 mov w, #1 REPT 2 call NICWriteAgain_6 ENDR ; <flags> mov w, #$80 call NICWriteAgain_6 mov w, #0 call NICWriteAgain_6 retp ; ****************************************************************************** DHCPSendCommon2 ; Helper function for DHCPDISCOVERSend and DHCPREQUESTSend ; INPUT: none ; OUTPUT: none ; ****************************************************************************** ; <client_hw_addr> mov w, #SX_ETH_ADDR0 call NICWriteAgain_6 mov w, #SX_ETH_ADDR1 call NICWriteAgain_6 mov w, #SX_ETH_ADDR2 call NICWriteAgain_6 mov w, #SX_ETH_ADDR3 call NICWriteAgain_6 mov w, #SX_ETH_ADDR4 call NICWriteAgain_6 mov w, #SX_ETH_ADDR5 call NICWriteAgain_6 ; <client_hw_addr>,<server_host_name>,<boot_filename> mov globTemp1, #(10+64+128) mov w, #0 :loop2 call NICWriteAgain_6 decsz globTemp1 jmp :loop2 ; <option_magic_cookie> mov w, #99 call NICWriteAgain_6 mov w, #130 call NICWriteAgain_6 mov w, #83 call NICWriteAgain_6 mov w, #99 call NICWriteAgain_6 retp ; ****************************************************************************** DHCPDISCOVERSend ; Send DHCPDISCOVER message ; INPUT: none ; OUTPUT: none ; ****************************************************************************** bank UDP_BANK clr udpTxDataLenMSB mov udpTxDataLenLSB, #(240+3+0+1) ; without requested-IP option ;mov udpTxDataLenLSB, #(240+3+6+1) ; with requested-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_6 decsz globTemp1 jmp :loop1 call DHCPSendCommon2 ; <option-message_type> mov w, #53 call NICWriteAgain_6 mov w, #1 call NICWriteAgain_6 mov w, #1 ; DHCPDISCOVER call NICWriteAgain_6 ; <option-requested_IP> -- optional ;mov w, #50 ;call NICWriteAgain_6 ;mov w, #4 ;call NICWriteAgain_6 ;mov w, #SX_IP_ADDR3 ;call NICWriteAgain_6 ;mov w, #SX_IP_ADDR2 ;call NICWriteAgain_6 ;mov w, #SX_IP_ADDR1 ;call NICWriteAgain_6 ;mov w, #SX_IP_ADDR0 ;call NICWriteAgain_6 ; <option-end_option> -- not optional mov w, #255 call NICWriteAgain_6 ; and ... that should do it! call @UDPEndPktOut clrb arpFlags.ARP_BYPASS retp ; ****************************************************************************** _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_6 mov w, myIP2 call NICWriteAgain_6 mov w, myIP1 call NICWriteAgain_6 mov w, myIP0 call NICWriteAgain_6 ; <your_IP>,<server_IP>,<router_IP> = 0 mov globTemp1, #(4+4+4) mov w, #0 :loop1 call NICWriteAgain_6 decsz globTemp1 jmp :loop1 call DHCPSendCommon2 ; <option-message_type> mov w, #53 call NICWriteAgain_6 mov w, #1 call NICWriteAgain_6 mov w, #3 ; DHCPREQUEST call NICWriteAgain_6 ; <option-server_id> mov w, #54 ; option server identifier call NICWriteAgain_6 mov w, #4 ; length call NICWriteAgain_6 bank DHCP_BANK mov w, dhcpServerId3 call NICWriteAgain_6 mov w, dhcpServerId2 call NICWriteAgain_6 mov w, dhcpServerId1 call NICWriteAgain_6 mov w, dhcpServerId0 call NICWriteAgain_6 ; <option-requested_IP> -- not optional mov w, #50 call NICWriteAgain_6 mov w, #4 call NICWriteAgain_6 bank IP_BANK mov w, myIP3 call NICWriteAgain_6 mov w, myIP2 call NICWriteAgain_6 mov w, myIP1 call NICWriteAgain_6 mov w, myIP0 call NICWriteAgain_6 ; <option-end_option> -- not optional mov w, #255 call NICWriteAgain_6 ; and ... that should do it! call @UDPEndPktOut clrb arpFlags.ARP_BYPASS retp ELSE ; DHCP ; ****************************************************************************** UDPProcBcstPktIn ; The only kind of broadcast UDP packets accepted are DHCP messages: DHCPOFFER ; and DHCPACK ; INPUT: none ; OUTPUT: none ; ****************************************************************************** retp ; cus DHCP not enabled ENDIF ; DHCP ORG $E00 ; Page7 (Holly Cow! Still not done??) ; ****************************************************************************** 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 IF CREDENCE xor re, w ; toggle I/O pins _bank MISC_BANK mov ledPort, re ELSE and w, #%01000000 xor ra, w ; toggle I/O pins _bank MISC_BANK mov ledPort, ra ENDIF _bank UDP_BANK clr udpTxSrcPortMSB clr udpTxSrcPortLSB mov udpTxDestPortMSB, udpRxSrcPortMSB mov udpTxDestPortLSB, udpRxSrcPortLSB mov udpTxDataLenMSB, #0 mov udpTxDataLenLSB, #2 call @UDPStartPktOut IF CREDENCE mov w, re ; send new port state ELSE mov w, ra ; send new port state ENDIF call @NICWriteAgain mov w, #$00 ; one-byte padding call @NICWriteAgain call @UDPEndPktOut retp ; *********** ; *** END *** ; *********** END
file: /Techref/scenix/lib/io/osi3/tcpip/isx_1_6_8.src, 128KB, , updated: 2002/2/21 11:32, local time: 2024/11/30 03:43,
18.218.71.21: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_1_6_8.src"> scenix lib io osi3 tcpip isx_1_6_8</A> |
Did you find what you needed? |
Welcome to massmind.org! |
Welcome to techref.massmind.org! |
.