; 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