; isx_1_6_8.src
; Wing Poon, Chris Waters, Deon Roelofse . V1.6.8 . 1/9/01 . (C) Scenix, Inc.
; SX Ethernet TCP/IP Stack. Protocols implemented: ARP, DHCP, IP/ICMP, UDP, TCP,
; HTTP.
; ******************************************************************************
; NOTES: 1. Will work only on SX48/52 Production Release silicon!
; 2. If using SX-Key, you need SXKEY52.EXE V1.19 or greater. Pls go to
; line 150 for important information!
; 3. If using SX-ISD, you need SASM.EXE V1.45.5 and SXIDE.EXE V1.07.03 or
; greater. Pls go to line 150 for important information!
; 4. The schematics for the board that runs this code is available, pls
; contact sales@scenix.com .
; 5. There is a Java application program available to demonstrate the
; use of the UDP protocol to allow the user to control the iSX, pls
; contact sales@scenix.com . You'll need the Microsoft Java Virtual
; Machine Build 3240, or greater (installed by default with IE5).
; ******************************************************************************
; Program Memory Usage Summary (/w DHCP):
; Page | Usage
; -----------------------------
; 0: 000-0AF, 190-1F1 (53%)
; 1: 200-3FB (100%)
; 2: 400-5FD (100%)
; 3: 600-7FD (100%)
; 4: 800-96D (71%)
; 5: A00-B5B (68%)
; 6: C00-D38 (61%)
; 7: E00-E25 (7%)
; Data Memory Usage Summary (/w DHCP):
; Bank | Usage
; -----------------------------
; 0: - (0%)
; 1: 0-F (100%)
; 2: 0-E (94%)
; 3: 0-F (100%)
; 4: 0-F (100%)
; 5: 0-F (100%)
; 6: 0-A (73%)
; 7: 0-C (81%)
; 8: 0-A (73%)
; 9: - (0%)
; A: - (0%)
; B: - (0%)
; C: - (0%)
; D: - (0%)
; E: - (0%)
; F: - (0%)
; This program implements an embedded web-server. The server will respond to
; HTTP requests and surfs up the specified web resource stored on an external
; serial EEPROM. The IP address of the webserver is http://10.1.1.20, if
; the DHCP option is disabled (default). In addition, the iSX can be pinged
; ("ping 10.1.1.20") using the standard PC/Unix ping utility. Finally, the iSX
; can also be controlled, using UDP, by the user, using a Java program,
; udpsx.class, available from Scenix.
; How to Setup your PC to talk with the iSX Board
; -----------------------------------------------
; 1. Open a DOS Command Prompt Window
; 2. Type "route print"
; 3. If you see a line like the one below, skip step [4] and [5]
; Network Address Netmask Gateway Address Interface Metric
; 10.1.1.0 255.255.255.0 w.x.y.z w.x.y.z 1
; (in the above, "w.x.y.z" is any non-zero IP address)
; 4. Find out the IP address of the PC's Ethernet Interface. Type the following
; command: "ipconfig"
; Look for the line "IP Address. . .", given under a section called "Ethernet
; adapter :". This is the Ethernet IP address of the PC.
; The IP address must not be 0.0.0.0
; If you don't find a non-zero Ethernet IP address, follow the instructions
; Given in the section "How to give your PC a Static IP Address" and restart
; your computer.
; 5. Update the PC's routing table: Issue the following command:
; "route add 10.1.1.0 mask 255.255.255.0 <PC_ETHERNET_IP_ADDR>"
; Be sure to substitute the IP address found in step [4] for the field
; <PC_ETHERNET_IP_ADDR>
; 6. Power-up the iSX board and connect it to the PC using the supplied cross-
; over cable.
; 7. Ping the iSX to see if it the connection is alive: "ping 10.1.1.20"
; If you get a "Request timed out" message, the connection was unsucessful;
; try typing "arp -s 10.1.1.20 00-00-00-00-00-01 <PC_ETHERNET_IP_ADDR>" at the
; DOS prompt and try pinging the iSX again.
; If you get a "Reply from 10.1.1.20: ..." message, the connection is alive.
; 8. Start your web-browser (IE5 works best). Type in the following
; http://10.1.1.20
; 9. If you successfully pinged the iSX board in step [7] but failed to load the
; web page in step [8], do the following:
; In IE, go to Tools->Internet Options. Click on the "Use Blank" button, then
; hit "OK". Exit and restart IE.
; In Netscape, go to Edit->Preferences. Click on the "Blank Page" radio button,
; then hit "OK". Exit and restart Netscape.
; Repeat step [8].
;
; How to give your PC a Static IP Address (optional)
; --------------------------------------------------
; 1. Right-click on the "Network Neighborhood" icon on the Windows Desktop.
; Select Properties.
; 2. Select the "TCP/IP -> <YOUR_ETHERNET_ADAPTER>" entry from the scroll-box.
; Click the Properties button.
; 3. Click on the "IP Address" tab. Select "Specify an IP Address". Enter
; "10.1.1.1" for the "IP Address" (This will be the Ethernet IP address of
; the PC). Enter "255.255.255.0" for the "Subnet Mask". Click on OK and
; restart the computer.
; INCLUDE "SX52.inc"
; SX52.inc
;*********************************************************************************
; SX48BD/52BD Mode addresses
; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
; exception of CMP and WKPND which do an exchange with W.
;*********************************************************************************
; Timer (read) addresses
TCPL_R = $00 ; Read Timer Capture register low byte
TCPH_R = $01 ; Read Timer Capture register high byte
TR2CML_R = $02 ; Read Timer R2 low byte
TR2CMH_R = $03 ; Read Timer R2 high byte
TR1CML_R = $04 ; Read Timer R1 low byte
TR1CMH_R = $05 ; Read Timer R1 high byte
TCNTB_R = $06 ; Read Timer control register B
TCNTA_R = $07 ; Read Timer control register A
; Exchange addresses
CMP = $08 ; Exchange Comparator enable/status register with W
WKPND = $09 ; Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_R = $0A ; Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R = $0B ; Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R = $0C ; Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R = $0D ; Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R = $0E ; Read Port Pull-up setup, 0 = enabled, 1 = disabled
DIR_R = $0F ; Read Port Direction
; Timer (write) addresses
TR2CML_W = $12 ; Write Timer R2 low byte
TR2CMH_W = $13 ; Write Timer R2 high byte
TR1CML_W = $14 ; Write Timer R1 low byte
TR1CMH_W = $15 ; Write Timer R1 high byte
TCNTB_W = $16 ; Write Timer control register B
TCNTA_W = $17 ; Write Timer control register A
; Port setup (write) addresses
WKED_W = $1A ; Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W = $1B ; Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W = $1C ; Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W = $1D ; Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W = $1E ; Write Port Pull-up setup, 0 = enabled, 1 = disabled
DIR_W = $1F ; Write Port Direction
;*********************************************************************************
; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
;*********************************************************************************
RTCC_ON = %10000000 ; Enables RTCC at address $01 (RTW hi)
; WREG at address $01 (RTW lo) by default
RTCC_ID = %01000000 ; Disables RTCC edge interrupt (RTE_IE hi)
; RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT = %00100000 ; Sets RTCC increment on RTCC pin transition (RTS hi)
; RTCC increment on internal instruction (RTS lo) is defalut
RTCC_FE = %00010000 ; Sets RTCC to increment on falling edge (RTE_ES hi)
; RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_OFF = %00001000 ; Assigns prescaler to Watchdog (PSA hi)
PS_000 = %00000000 ; RTCC = 1:2, WDT = 1:1
PS_001 = %00000001 ; RTCC = 1:4, WDT = 1:2
PS_010 = %00000010 ; RTCC = 1:8, WDT = 1:4
PS_011 = %00000011 ; RTCC = 1:16, WDT = 1:8
PS_100 = %00000100 ; RTCC = 1:32, WDT = 1:16
PS_101 = %00000101 ; RTCC = 1:64, WDT = 1:32
PS_110 = %00000110 ; RTCC = 1:128, WDT = 1:64
PS_111 = %00000111 ; RTCC = 1:256, WDT = 1:128
; **************
; *** DEVICE ***
; **************
; Parallax -- uncomment the following 2 lines if, and only if, using Parallax's SX-Key
DEVICE OSCHS2, DRT60MS
FREQ 48_000_000 ; have to debug at freq != resonant freq
; SASM -- uncomment the following line if, and only if, using Advance Transdata's SX-ISD
; DEVICE SX52BD, OSCHS2, WDRT60
RESET Main
ID 'iSX_167'
; ***************************
; *** CONDITIONAL DEFINES ***
; ***************************
DHCP = 0 ; 1 = DHCP enabled, 0 = DHCP disabled
CREDENCE = 0 ; 1 = Credence board, 0 = Scenix board
; *****************
; *** VARIABLES ***
; *****************
; *** Global ***
GLOBAL_ORG = $0A
flags EQU GLOBAL_ORG+0 ; various flags used by TCP/IP stack
arpFlags EQU GLOBAL_ORG+1 ; ARP status flags
globTemp1 EQU GLOBAL_ORG+2 ; not preserved across any function
globTemp2 EQU GLOBAL_ORG+3 ; not preserved across any function
globTemp3 EQU GLOBAL_ORG+4 ; preserved across some functions
; *** Bank 0 ***
; (Don't use this bank -- it's bad for your mental health)
ORG $00
; *** Bank 1 ***
ORG $10
NIC_BANK = $
nicIOAddr DS 1 ; points to currently addressed register on NIC
nicNextPktPtr DS 1 ; points to next packet in NIC's rx queue
nicCurrPktPtr DS 1 ; points to current packet in NIC's rx queue
nicRemoteEth0 DS 1 ; ethernet addr used for outgoing packet, overwritten by incoming packet
nicRemoteEth1 DS 1 ; "
nicRemoteEth2 DS 1 ; "
nicRemoteEth3 DS 1 ; "
nicRemoteEth4 DS 1 ; "
nicRemoteEth5 DS 1 ; "
nicCopySrcMSB DS 1 ; used by NICBufferCopy()
nicCopySrcLSB DS 1 ; "
nicCopyDestMSB DS 1 ; "
nicCopyDestLSB DS 1 ; "
nicCopyLenMSB DS 1 ; "
nicCopyLenLSB DS 1 ; "
nicCopyTemp DS 1 ; "
; *** Bank 2 ***
ORG $20
IP_BANK = $ ; make sure IP_BANK[7] = NIC_BANK[7]
remoteIP3 DS 1 ; IP addr used for outgoing packet, overwritten by incoming packet
remoteIP2 DS 1 ; "
remoteIP1 DS 1 ; "
remoteIP0 DS 1 ; "
myIP3 DS 1 ; filter value for incoming IP packets, also used in outgoing packet
myIP2 DS 1 ; "
myIP1 DS 1 ; "
myIP0 DS 1 ; "
ipCheckSumMSB DS 1 ; IP <header_checksum>
ipCheckSumLSB DS 1 ; "
ipLengthMSB DS 1 ; IP <length>
ipLengthLSB DS 1 ; "
ipProtocol DS 1 ; IP <protocol>
ipIdentMSB DS 1 ; IP <identifier>, incremented each outgoing packet
ipIdentLSB DS 1 ; "
; *** Bank 3 ***
ORG $30
UDP_BANK = $ ; make sure UDP_BANK[7] = NIC_BANK[7]
udpRxSrcPortMSB DS 1
udpRxSrcPortLSB DS 1
udpRxDestPortMSB DS 1 ; filter value for incoming UDP packets
udpRxDestPortLSB DS 1 ; "
udpRxDataLenMSB DS 1 ; length of <data> field of incoming UDP packet
udpRxDataLenLSB DS 1 ; "
udpTxSrcPortMSB DS 1
udpTxSrcPortLSB DS 1
udpTxDestPortMSB DS 1
udpTxDestPortLSB DS 1
udpTxDataLenMSB DS 1 ; length of <data> field of outgoing UDP packet
udpTxDataLenLSB DS 1 ; "
DHCP_BANK = $
dhcpServerId3 DS 1 ; DHCP <server_identifier> = IP addr of DHCP server
dhcpServerId2 DS 1 ; "
dhcpServerId1 DS 1 ; "
dhcpServerId0 DS 1 ; "
; *** Bank 4 ***
ORG $40
TCP_BANK = $ ; make sure TCP_BANK[7] = NIC_BANK[7]
tcpState DS 1 ; state-machine state
tcpTmpSeq4 DS 1 ; TMP.SEQ. 1=LSB, 4=MSB
tcpTmpSeq3 DS 1 ; temporary information from the received packet
tcpTmpSeq2 DS 1
tcpTmpSeq1 DS 1
tcpTmpAck4 DS 1 ; TMP.ACK
tcpTmpAck3 DS 1 ; temporary information from the received packet
tcpTmpAck2 DS 1
tcpTmpAck1 DS 1
tcpUnAckMSB DS 1 ; number of unacknowledged bytes
tcpUnAckLSB DS 1 ; "
tcpRxFlags DS 1 ; copy of the received flags field
tcpCheckSumMSB DS 1
tcpCheckSumLSB DS 1
tcpLengthMSB DS 1
tcpLengthLSB DS 1
tcpTmpMSB = tcpTmpSeq4
tcpTmpLSB = tcpTmpSeq3
tcpAppTxBytesMSB = tcpUnAckMSB ; number of bytes app wants to transmit
tcpAppTxBytesLSB = tcpUnAckLSB ; "
tcpAppRxBytesMSB = tcpLengthMSB ; number of bytes app will be receiving
tcpAppRxBytesLSB = tcpLengthLSB ; "
; *** Bank 5 ***
ORG $50
TCB_BANK = $ ; make sure TCB_BANK[7] = NIC_BANK[7]
; The ordering of these variables is significant. It is the same as the TCP
; header. This simpifies the send-packet code
tcbLocalPortMSB DS 1 ; source port
tcbLocalPortLSB DS 1 ; "
tcbRemotePortMSB DS 1 ; destination port
tcbRemotePortLSB DS 1 ; "
tcbSndUna4 DS 1 ; SND.UNA: oldest unacknowledged byte
tcbSndUna3 DS 1 ; "
tcbSndUna2 DS 1 ; "
tcbSndUna1 DS 1 ; "
tcbRcvNxt4 DS 1 ; RCV.NXT: next byte to receive
tcbRcvNxt3 DS 1 ; "
tcbRcvNxt2 DS 1 ; "
tcbRcvNxt1 DS 1 ; "
tcbOffset DS 1 ; length of the TCP options
tcbFlags DS 1 ; flags field
tcbSendWinMSB DS 1 ; send window
tcbSendWinLSB DS 1 ; "
TCB_END = $
; *** Bank 6 ***
ORG $60
ARP_BANK = $ ; make sure ARP_BANK[7] = NIC_BANK[7]
host1IP3 DS 1 ; remote host1 IP address
host1IP2 DS 1 ; "
host1IP1 DS 1 ; "
host1IP0 DS 1 ; "
host1Eth0 DS 1 ; remote host1 Ethernet address
host1Eth1 DS 1 ; "
host1Eth2 DS 1 ; "
host1Eth3 DS 1 ; "
host1Eth4 DS 1 ; "
host1Eth5 DS 1 ; "
stPktTxBufStart DS 1 ; start address of stalled packet in NIC tx buffer
; *** Bank 7 ***
ORG $70
TIMER_BANK = $ ; make sure TIMER_BANK[7] = NIC_BANK[7]
baseTimer DS 1 ; lowest/common cog in timer chain
arpTimerMSB DS 1 ; ARP-timer count
arpTimerLSB DS 1 ; "
tcpTimerMSB DS 1 ; TCP-timer count
tcpTimerLSB DS 1 ; "
connTimerMSB DS 1 ; Connection-timer count
connTimerLSB DS 1 ; "
HTTP_BANK = $ ; make sure HTTP_BANK[7] = NIC_BANK[7]
httpParseState DS 1 ; state of the HTTP header parser
httpURIHash DS 1 ; hash of the current URI
EEPROM_BANK = $ ; make sure EEPROM_BANK[7] = NIC_BANK[7]
e2AddrMSB DS 1 ; address in EEPROM to start reading from
e2AddrLSB DS 1 ; "
e2FileLenMSB DS 1 ; length of the file being read
e2FileLenLSB DS 1 ; "
; *** Bank 8 ***
ORG $80
MISC_BANK = $
ledPort DS 1 ; records the state of the led port
bcd3 DS 3 ; buffer for binary-to-ascii conversion
pageCount DS 1 ; num times resource.htm page has been accessed
ADC_BANK = $ ; must be same bank as bcd3
adc DS 1 ; averaged ADC value
adcAcc DS 1
adcCount DS 1
adcMSB DS 1 ; for averaging 256 samples
adcLSB DS 1 ; "
adcSampleCount DS 1 ; count number of averaged samples
; *** Bank 9 ***
ORG $90
; *** Bank A ***
ORG $A0
; *** Bank B ***
ORG $B0
; *** Bank C ***
ORG $C0
; *** Bank D ***
ORG $D0
; *** Bank E ***
ORG $E0
; *** Bank F ***
ORG $F0
; ***************
; *** EQUATES ***
; ***************
INT_PERIOD = 145 ; RTCC interrupt periodicity (345kHz)
; change this if you're not clocking
; the SX at 50MHz
; *** Pin Definitions ***
IF CREDENCE
RA_DIR = %11110011
RA_OUT = %00000000
RA_LVL = %11111111
RA_PLP = %11111111
RB_DIR = %10000000
RB_OUT = %01100000
RB_LVL = %11111111
RB_PLP = %01111111
RC_DIR = %11111111
RC_OUT = %00000000
RC_LVL = %11111111
RC_PLP = %11111111
RD_DIR = %00000010
RD_OUT = %00000011
RD_LVL = %11111111
RD_PLP = %11111100
RE_DIR = %00000000
RE_OUT = %00000000
RE_LVL = %11111111
RE_PLP = %11111111
NIC_DATA_PORT = rc
NIC_CTRL_PORT = rb
IOWB_PIN = NIC_CTRL_PORT.5
IORB_PIN = NIC_CTRL_PORT.6
IOCH_PIN = NIC_CTRL_PORT.7
E2_PORT = rd
E2SCL = 0
E2SDA = 1
E2SCL_PIN = E2_PORT.E2SCL
E2SDA_PIN = E2_PORT.E2SDA
LED_PORT = re
LED = 0
LED_PIN = LED_PORT.LED
ELSE
RA_DIR = %10101010
RA_OUT = %01110101
RA_LVL = %11111111
RA_PLP = %01111111
RB_DIR = %10000000
RB_OUT = %01100000
RB_LVL = %11111111
RB_PLP = %01111111
RC_DIR = %11111111
RC_OUT = %00000000
RC_LVL = %11111111
RC_PLP = %11111111
RD_DIR = %11111111
RD_OUT = %00000000
RD_LVL = %11111111
RD_PLP = %00000000
RE_DIR = %01111111
RE_OUT = %00000000
RE_LVL = %00111111
RE_PLP = %11000000
NIC_DATA_PORT = rc
NIC_CTRL_PORT = rb
IOWB_PIN = NIC_CTRL_PORT.5
IORB_PIN = NIC_CTRL_PORT.6
IOCH_PIN = NIC_CTRL_PORT.7
E2_PORT = ra
E2SCL = 4
E2SDA = 5
E2SCL_PIN = E2_PORT.E2SCL
E2SDA_PIN = E2_PORT.E2SDA
LED_PORT = ra
LED = 6
LED_PIN = LED_PORT.LED
ENDIF
; *** flags ***
RX_IS_ARP = 0 ; incoming packet is an ARP packet
RX_IS_ICMP = 1 ; incoming packet is an ICMP packet
RX_IS_UDP = 2 ; incoming packet is a UDP packet
RX_IS_TCP = 3 ; incoming packet is a TCP packet
RX_IS_IP_BCST = 4 ; incoming packet is an IP Broadcast packet
GOT_DHCP_OFFER = 5 ; received DHCP IP address offer
GOT_IP_ADDR = 6 ; received an IP address assignment (recv'ed DHCP ACK)
IP_CHKSUM_LSB = 7 ; next byte to accumulate IP checksum is LSB
TCP_CHKSUM_LSB = 5 ; next byte to accumulate TCP checksum is LSB
; *** arpFlags ***
ARP_REQ_SENT = 0 ; indicates that an ARP request has been sent
ARP_RSP_RCVD = 1 ; indicates that an ARP response has been received
ARP_STL_TX = 2 ; indicates that the stalled packet is to be transmitted
ARP_BYPASS = 3 ; indicates that the outgoing packet should not be checked
; *** NIC Constants ***
RXBUF_START = $40 ; 4608 byte receive buffer (3 max-size packets)
RXBUF_END = $53 ; "
TXBUF1_START = $53 ; 1536 byte transmit buffer for ICMP/UDP
TXBUF2_START = $59 ; 1536 byte transmit buffer for TCP
TXBUF3_START = $5F ; 256 byte transmit buffer for ARP
; *** Ethernet Constants ***
SX_ETH_ADDR0 = 0 ; SX's Ethernet Phy MAC Address
SX_ETH_ADDR1 = 0 ; "
SX_ETH_ADDR2 = 0 ; "
SX_ETH_ADDR3 = 0 ; "
SX_ETH_ADDR4 = 0 ; "
SX_ETH_ADDR5 = 1 ; "
; *** ARP Constants ***
ARP_TIMEOUT = 5 ; ARP response timeout period. Must be smaller than other timeouts!
; *** IP Constants ***
SX_IP_ADDR3 = 10 ; SX's static IP address (if DHCP disabled)
SX_IP_ADDR2 = 1 ; "
SX_IP_ADDR1 = 1 ; "
SX_IP_ADDR0 = 20 ; "
IP_TTL = 32
; *** UDP Constants ***
UDP_RX_DEST_MSB = $04 ; user UDP RX Port: 1025
UDP_RX_DEST_LSB = $01 ; "
; *** TCP Constants ***
; TCP state-machine states (numbering order is signifcant)
TCP_ST_CLOSED = 0
TCP_ST_LISTEN = 1
TCP_ST_SYNSENT = 2
TCP_ST_SYNRCVED = 3
TCP_ST_ESTABED = 4
TCP_ST_FINWAIT1 = 5
TCP_ST_FINWAIT2 = 6
TCP_ST_CLOSEWAIT = 7
TCP_ST_CLOSING = 8
TCP_ST_LASTACK = 9
TCP_ST_TIMEWAIT = 10
; Bit positions in the TCP <flag> byte.
TCP_FLAG_ACK = 4
TCP_FLAG_PSH = 3
TCP_FLAG_RST = 2
TCP_FLAG_SYN = 1
TCP_FLAG_FIN = 0
; TCP Options
TCP_OPTION_END = 0
TCP_OPTION_NOP = 1
TCP_OPTION_MSS = 2 ; max segment size
TCP_HDR_LENGTH = 5 ; normal TCP header length.
TCP_OFFSET_MASK = $F0
TCP_WINDOW_SIZE = 1400 ; max # of data bytes we will accept
TCP_SEG_SIZE = 1400 ; max # of data bytes TCP will transmit per segment
TCP_RESTART_EXP = 8 ; TCP re-transmission timeout period
TCP_CONN_EXP = 80 ; TCP connection timeout period
; *** HTTP Constants ***
; States for parsing HTTP headers
HTTP_PORT_MSB = 0 ; port number for HTTP server.
HTTP_PORT_LSB = 80 ; "
HTTP_SEG_SIZE = 1400
URI1 = $81 ; hash of "resource.htm"
URI2 = $54 ; hash of "temperature.htm"
; *** EEPROM Constants ***
E2_CMD_RD = $A1 ; most-significant 7-bits is the I2C slave addr
E2_CMD_WR = $A0 ; most-significant 7-bits is the I2C slave addr
E2_SDA_MASK = (1 << E2SDA) ; a '1' in the SDA bit, '0' everywhere else
IF CREDENCE
E2_DDR_SDA_IN = (RD_DIR | E2_SDA_MASK) ; direction of SDA port when SDA is input
E2_DDR_SDA_OUT = (RD_DIR & (~E2_SDA_MASK)) ; direction of SDA port when SDA is output
ELSE
E2_DDR_SDA_IN = (RA_DIR | E2_SDA_MASK) ; direction of SDA port when SDA is input
E2_DDR_SDA_OUT = (RA_DIR & (~E2_SDA_MASK)) ; direction of SDA port when SDA is output
ENDIF
; *** ADC Constants ***
ADC_PORT = re
ADC_OUT = 7
ADC_IN = 6
ADC_OUT_PIN = ADC_PORT.ADC_OUT
ADC_IN_PIN = ADC_PORT.ADC_IN
; **************
; *** MACROS ***
; **************
_bank MACRO 1 ; sets FSR[7:4]
bank \1
IF \1 & %10000000 ; SX48BD and SX52BD (production release) BANK instruction
setb fsr.7 ; modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
ELSE
clrb fsr.7
ENDIF
ENDM
_banky MACRO 1 ; set FSR[7] ~only~
IF \1 & %10000000 ; SX48BD and SX52BD (production release) bank instruction
setb fsr.7 ; modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
ELSE
clrb fsr.7
ENDIF
ENDM
_mode MACRO 1
mov w, #\1 ; loads the M register correctly for the SX48BD and SX52BD
mov m, w
ENDM
_pc_check MACRO 0
IF ($ & $100)
ERROR 'ERROR!! ADD PC,W instruction at invalid addr'
ENDIF
ENDM
; ***********
; *** ISR ***
; ***********
ORG 0 ; Page0
ISR
Timer ; implement various SW timers
; lowest-common-denominator timer
_bank TIMER_BANK
incsz baseTimer
jmp :timerEnd
:tcpTimer ; TCP-timer (used for TCP re-transmission timeouts)
incsz tcpTimerLSB
jmp :connTimer
inc tcpTimerMSB
:connTimer ; Connection-timer (used for TCP connection timeouts)
incsz connTimerLSB
jmp :arpTimer
inc connTimerMSB
:arpTimer ; ARP-timer (used for ARP response timeouts)
incsz arpTimerLSB
jmp :timerEnd
inc arpTimerMSB
:timerEnd
ADCTempSensor ; SW A-to-D for measuring current board temperature to be then
; displayed on a dynamic web page
; ADC(out) = !ADC(in) (balancing the yin and the yang)
mov w, ADC_PORT
sb wreg.ADC_IN
setb ADC_OUT_PIN
snb wreg.ADC_IN
clrb ADC_OUT_PIN
; decision time
_bank ADC_BANK
sb wreg.ADC_OUT
incsz adcAcc
inc adcAcc
dec adcAcc
inc adcCount
jnz :adcTempSensorEnd
; accumulate for averaging (256 samples)
mov w, adcAcc
clr adcAcc
add adcLSB, w
snc
inc adcMSB
; check if averaging is done
incsz adcSampleCount
jmp :adcTempSensorEnd
; averaging done -- save results
mov adc, adcMSB ; divide by 256 (clever huh?)
clr adcMSB
clr adcLSB
:adcTempSensorEnd
LedBlinker ; blinks auxilliary LED, but don't intefere if E2 access in
; in progress because LED and E2 may share same port
_bank HTTP_BANK
test httpParseState
jnz :ledBlinkerEnd
bank TIMER_BANK
mov w, tcpTimerMSB
and w, #%00001111
jnz :ledHere
_bank MISC_BANK
movb LED_PORT.LED, /ledPort.LED
jmp :ledBlinkerEnd
:ledHere mov w, tcpTimerMSB
and w, #%00001111
xor w, #1
jnz :ledBlinkerEnd
_bank MISC_BANK
movb LED_PORT.LED, ledPort.LED
:ledBlinkerEnd
ISRExit mov w, #-INT_PERIOD
retiw
; ********************
; *** MAIN PROGRAM ***
; ********************
Init jmp _Init
ARPInit jmp _ARPInit
TCPIPInit jmp _TCPIPInit
E2Init jmp _E2Init
StartupDelay jmp _StartupDelay
Main
call @Init
; DHCP Dynamic IP Address solicitation
IF DHCP
; initialize UDP receive port for DHCP
_bank UDP_BANK
mov udpRxDestPortMSB, #0 ; DHCP(BOOTP) Client
mov udpRxDestPortLSB, #68 ;
; send DHCPDISCOVER message to find DHCP server(s)
call @DHCPDISCOVERSend
; wait for DHCPOFFER
:dhcpWaitOffer call @NICWaitRxFrame
call @CheckIPDatagram
jnb flags.RX_IS_UDP, :dhcpWaitOffer
call @UDPProcPktIn
sb flags.GOT_DHCP_OFFER
jmp :dhcpWaitOffer
; send DHCPREQUEST message
call @DHCPREQUESTSend
; wait for DHCPACK
:dhcpGotOffer call @NICWaitRxFrame
call @CheckIPDatagram
jnb flags.RX_IS_UDP, :dhcpGotOffer
call @UDPProcPktIn
sb flags.GOT_IP_ADDR
jmp :dhcpGotOffer
:dhcpGotAck ; hallelujah!
ENDIF
call @UDPAppInit
_bank MISC_BANK
clr pageCount ; clear page counter (dynamic data)
mov ledPort, LED_PORT
; main program loop
:mainLoop call @NICCheckRxFrame
jz :noRxFrame
call @NICWaitRxFrame ; no waiting cus we've already checked
call @ARPCheckIfIs ; check and process if ARP
jb flags.RX_IS_ARP, :mainLoop
call @CheckIPDatagram ; not ARP, check if IP
jb flags.RX_IS_ICMP, :icmp
jb flags.RX_IS_UDP, :udp
jb flags.RX_IS_TCP, :tcp
call @NICDumpRxFrame ; not something we recognize, so dump it
jmp :mainLoop
:icmp call @ICMPProcPktIn ; process incoming ICMP packet
jmp :mainLoop
:udp call @UDPProcPktIn ; process incoming UDP packet
jmp :mainLoop
:tcp call @TCPProcPktIn ; process incoming TCP packet
jmp :mainLoop
:noRxFrame call @ARPSendStPacket ; send ARP stalled packets if any
bank TCP_BANK
cje TCPState, #TCP_ST_CLOSED, :tcpClosed
cje TCPState, #TCP_ST_LISTEN, :tcpListen
bank TIMER_BANK
cjae connTimerMSB, #TCP_CONN_EXP, :resetTCP
sb arpFlags.ARP_REQ_SENT ; do not allow new tx if waiting for ARP response
call @TCPTransmit ; check if app has anything to transmit
jmp :mainLoop
:tcpClosed call @TCPAppInit
jmp :mainLoop
:tcpListen bank TIMER_BANK
clr connTimerMSB
jmp :mainLoop
:resetTCP ; reset hung TCP connection
bank TCP_BANK
clr tcpUnAckMSB
clr tcpUnAckLSB
mov tcpState, #TCP_ST_CLOSED
bank HTTP_BANK
clr httpParseState
jmp :mainLoop
; *******************
; *** SUBROUTINES ***
; *******************
ORG $190 ; Page0
; ******************************************************************************
_Init
; Main program initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_mode LVL_W
mov !ra, #RA_LVL
mov !rb, #RB_LVL
mov !rc, #RC_LVL
mov !rd, #RD_LVL
mov !re, #RE_LVL
_mode PLP_W
mov !ra, #RA_PLP
mov !rb, #RB_PLP
mov !rc, #RC_PLP
mov !rd, #RD_PLP
mov !re, #RE_PLP
_mode DIR_W
mov !ra, #RA_DIR
mov !rb, #RB_DIR
mov !rc, #RC_DIR
mov !rd, #RD_DIR
mov !re, #RE_DIR
mov ra, #RA_OUT
mov rb, #RB_OUT
mov rc, #RC_OUT
mov rd, #RD_OUT
mov re, #RE_OUT
clr flags
call @NICInit
call @ARPInit
call @TCPIPInit
call @E2Init
mov w, #(RTCC_PS_OFF) ; setup option register
mov !option, w
retp
; ******************************************************************************
_ARPInit
; ARP initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
clr arpFlags
_bank ARP_BANK
clr host1IP0 ; clear the cache
clr host1IP1 ; "
clr host1IP2 ; "
clr host1IP3 ; "
retp
; ******************************************************************************
_TCPIPInit
; TCP/IP stack initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank IP_BANK
IF DHCP
clr myIP3
clr myIP2
clr myIP1
clr myIP0
ELSE
; initialize SX's IP addr
setb flags.GOT_IP_ADDR
mov myIP3, #SX_IP_ADDR3
mov myIP2, #SX_IP_ADDR2
mov myIP1, #SX_IP_ADDR1
mov myIP0, #SX_IP_ADDR0
ENDIF
; initialize IP Identifier sequence number
clr ipIdentMSB
clr ipIdentLSB
; initialise the TCP variables
bank TCP_BANK
clr tcpUnAckMSB
clr tcpUnAckLSB
mov tcpState, #TCP_ST_CLOSED
retp
; ******************************************************************************
_E2Init
; EEPROM initialization code
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; get the I2C device into a known state
call @E2SDAInput
:e2InitLoop clrb E2SCL_PIN
call @E2Delay1300ns
setb E2SCL_PIN
call @E2Delay900ns
jnb E2SDA_PIN, :e2InitLoop
retp
; ******************************************************************************
_StartupDelay
; Delay for ?ms @ 50MHz
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov globTemp2, #10
:loop3 clr globTemp1
:loop2 clr w
:loop1 decsz wreg
jmp :loop1
decsz globTemp1
jmp :loop2
decsz globTemp2
jmp :loop3
retp
ORG $200 ; Page1
NICSendTxFrame jmp _NICSendTxFrame
NICBufCopy jmp _NICBufCopy
NICBufWrite jmp _NICBufWrite
NICBufRead jmp _NICBufRead
NICBufIPAddrWr jmp _NICBufIPAddrWr
NICWriteSrcIP jmp _NICWriteSrcIP
NICWriteDestIP jmp _NICWriteDestIP
NICWriteSrcEth jmp _NICWriteSrcEth
NICWriteDestEth jmp _NICWriteDestEth
NICDMAInit jmp _NICDMAInit
; ******************************************************************************
NICInit
; Initializes and configures Realtek RTL8019AS NIC
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank NIC_BANK
call @StartupDelay ; give it a little time to come out of POR
mov nicIOAddr, #$1F ; write to reset port
call NICWrite
call @StartupDelay ; give it a little time to reset
; --- Page3 Registers ---
clr nicIOAddr ; CR
mov w, #%11000001 ; Page3, Stop
call NICWrite
mov nicIOAddr, #$01 ; 9346CR
mov w, #%11000000 ; config register write enable
call NICWrite
mov nicIOAddr, #$05 ; CONFIG2
mov w, #%00000000 ; link test enable
call NICWrite
; --- Page1 Registers ---
clr nicIOAddr ; CR
mov w, #%01000001 ; Page1, Stop
call NICWrite
inc nicIOAddr ; ($01) PAR0
mov w, #SX_ETH_ADDR0
call NICWrite
inc nicIOAddr ; ($02) PAR1
mov w, #SX_ETH_ADDR1
call NICWrite
inc nicIOAddr ; ($03) PAR2
mov w, #SX_ETH_ADDR2
call NICWrite
inc nicIOAddr ; ($04) PAR3
mov w, #SX_ETH_ADDR3
call NICWrite
inc nicIOAddr ; ($05) PAR4
mov w, #SX_ETH_ADDR4
call NICWrite
inc nicIOAddr ; ($06) PAR5
mov w, #SX_ETH_ADDR5
call NICWrite
inc nicIOAddr ; ($07) CURR
mov w, #RXBUF_START
call NICWrite
; --- Page0 Registers ---
clr nicIOAddr ; CR
mov w, #%00000001 ; Page0, Stop
call NICWrite
inc nicIOAddr ; ($01) PSTART
mov w, #RXBUF_START
call NICWrite
inc nicIOAddr ; ($02) PSTOP
mov w, #RXBUF_END
call NICWrite
inc nicIOAddr ; ($03) BNRY
mov w, #RXBUF_START
call NICWrite
mov nicIOAddr, #$07 ; ISR
mov w, #$FF
call NICWrite
mov nicIOAddr, #$0C ; RCR
mov w, #%11000100
call NICWrite
inc nicIOAddr ; ($0D) TCR
mov w, #%11100000
call NICWrite
inc nicIOAddr ; ($0E) DCR
mov w, #%10111000
call NICWrite
clr nicIOAddr ; CR
mov w, #%00000010 ; Page0, Start
jmp NICWrite
; ******************************************************************************
NICWrite
; Does an I/O Write of a byte on the ISA host bus to the NIC
; INPUT: w = byte to be written
; nicIOAddr = I/O address (most-significant 3 bits must be zero)
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
; put data out on data bus
mov NIC_DATA_PORT, w
_mode DIR_W
mov w, #0 ; output
mov !NIC_DATA_PORT, w
; put addr out on addr bus
mov w, NIC_CTRL_PORT
and w, #%11100000
or w, nicIOAddr
mov NIC_CTRL_PORT, w
; strobe IOWB pin
jmp $+1
clrb IOWB_PIN
jmp $+1
jnb IOCH_PIN, $
setb IOWB_PIN
retp
; ******************************************************************************
NICWriteAgain
; Write to the same nicIOAddr as the previous call to NICWrite()
; INPUT: w = byte to be written
; OUTPUT: none
; ******************************************************************************
; put data out on data bus
mov NIC_DATA_PORT, w
; strobe IOWB pin
jmp $+1
clrb IOWB_PIN
jmp $+1
jnb IOCH_PIN, $
setb IOWB_PIN
retp
; ******************************************************************************
NICRead
; Does an I/O Read of a byte on the ISA host bus from the NIC
; INPUT: nicIOAddr = I/O address (most-significant 3 bits must be zero)
; OUTPUT: w = byte read
; ******************************************************************************
bank NIC_BANK
; configure data bus for input
_mode DIR_W
mov w, #$FF ; input
mov !NIC_DATA_PORT, w
; put addr out on addr bus
mov w, NIC_CTRL_PORT
and w, #%11100000
or w, nicIOAddr
mov NIC_CTRL_PORT, w
; strobe IORB pin and latch data
jmp $+1
clrb IORB_PIN
jmp $+1
jnb IOCH_PIN, $
mov w, NIC_DATA_PORT
setb IORB_PIN
retp
; ******************************************************************************
NICReadAgain
; Read the NIC using the same nicIOAddr as the previous call to NICRead()
; INPUT: none
; OUTPUT: w = byte read
; ******************************************************************************
; strobe IORB pin and latch data
jmp $+1
clrb IORB_PIN
jmp $+1
jnb IOCH_PIN, $
mov w, NIC_DATA_PORT
setb IORB_PIN
retp
; ******************************************************************************
NICPseudoRead
; 'Read' the NIC, but ignore data. Must have called NICRead() or NICReadAgain()
; priorly
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; strobe IORB pin
jmp $+1
clrb IORB_PIN
jmp $+1
jnb IOCH_PIN, $
setb IORB_PIN
retp
; ******************************************************************************
NICPseudoRead6
; 'Read' the NIC (6) times, but ignore data. Must have called NICRead() or
; NICReadAgain() priorly
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #6
:loop call NICPseudoRead
decsz wreg
jmp :loop
retp
; ******************************************************************************
NICCheckRxFrame
; Checks to see if an ethernet frame has been received
; INPUT: none
; OUTPUT: z is cleared if there's a frame waiting, otherwise z is set
; ******************************************************************************
_bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$07 ; ISR
call NICRead
;jb wreg.4, :overflowed ; OVW (bit set when rx buffer overflowed) DEBUG
jb wreg.4, @Main ; OVW (bit set when rx buffer overflowed)-> reset
clr nicIOAddr ; CR
mov w, #%01000010 ; Page1
call NICWrite
mov nicIOAddr, #$07 ; CURR
call NICRead
mov globTemp1, w
clr nicIOAddr ; CR
mov w, #%00000010 ; Page0
call NICWrite
mov nicIOAddr, #$03 ; BNRY
call NICRead
xor w, globTemp1 ; CURR = BNRY => no packets
retp
;:overflowed mov w, #(RTCC_ID) ; DEBUG
; mov !option, w ; DEBUG
; clrb LED_PIN ; DEBUG
; jmp $ ; DEBUG
; ******************************************************************************
NICWaitRxFrame
; Wait for an ethernet frame to be received.
; INPUT: none
; OUTPUT: nicCurrPktPtr = points to beginning of packet just received
; nicNextPktPtr = points to beginning of next packet
; nicRemoteEth0-5 = source (remote) ethernet address
; ******************************************************************************
_bank NIC_BANK
:loop clr nicIOAddr ; CR
mov w, #%01100010 ; Page1, abort DMA
call NICWrite
mov nicIOAddr, #$07 ; CURR
call NICRead
mov globTemp1, w
clr nicIOAddr ; CR
mov w, #%00000010 ; Page0
call NICWrite
mov nicIOAddr, #$03 ; BNRY
call NICRead
xor w, globTemp1
jz :loop ; CURR = BNRY => no packets
clr nicIOAddr ; CR
mov w, #%00011010 ; Page0, packet send
call NICWrite
; store current-packet pointer
mov nicIOAddr, #$03 ; BNRY
call NICRead
mov nicCurrPktPtr, w
mov nicIOAddr, #$10 ; RDMA
; ignore receive status
call NICRead
; store next-packet pointer
call NICReadAgain
mov nicNextPktPtr, w
; ignore ethernet frame size
call NICPseudoRead
call NICPseudoRead
; ignore the <destination> ethernet addr
call NICPseudoRead6
; record the sender's <source> ethernet addr
call NICReadAgain
mov nicRemoteEth0, w
call NICReadAgain
mov nicRemoteEth1, w
call NICReadAgain
mov nicRemoteEth2, w
call NICReadAgain
mov nicRemoteEth3, w
call NICReadAgain
mov nicRemoteEth4, w
call NICReadAgain
mov nicRemoteEth5, w
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
jmp NICWrite
; ******************************************************************************
NICDumpRxFrame
; Discard the current received frame by advancing the receive circular buffer
; tail pointer
; INPUT: nicNextPktPtr = next packet page
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$03 ; BNRY
mov w, nicNextPktPtr ; advance tail pointer
jmp NICWrite
; ******************************************************************************
NICInitTxFrame
; i. initialize NIC for remote-DMA writes
; ii. fills in ethernet <destination> and <source>
; INPUT: w = starting page number of tx buffer
; nicRemoteEth0-5 = destination ethernet address
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w
bank NIC_BANK
; wait for prior transmission to complete
clr nicIOAddr ; CR
:wait call NICRead
jb wreg.2, :wait
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$04 ; TPSR
mov w, globTemp1
call NICWrite
mov nicIOAddr, #$08 ; RSAR0
mov w, #$00
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, globTemp1
call NICWrite
mov nicIOAddr, #$0A ; RBCR0
mov w, #$EA ; max ethernet packet size
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #$05
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
; <destination>
call NICWriteDestEth
; <source>
jmp NICWriteSrcEth
; ******************************************************************************
_NICSendTxFrame
; Once the transmit buffer on the NIC is filled, call this to tell the NIC to
; start sending
; INPUT: {ipLengthMSB,ipLengthLSB} = length of IP frame to be transmitted
; OUTPUT: none
; ******************************************************************************
; sometimes we need to bypass ARP (e.g. broadcast IP addr)
jb arpFlags.ARP_BYPASS, :here
; otherwise we need to check if we know the MAC mapping for the IP addr
call @ARPCheckCache ; Start ARP
snb arpFlags.ARP_REQ_SENT ; Continue if an ARP request was not sent
; or if a stalled packet is to be sent
retp ; exit, ARP request was sent. packet to be stalled
:here bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
bank IP_BANK
mov w, #(64-6-6-2)
mov w, ipLengthLSB-w
jc :notRunt
mov w, #1
mov w, ipLengthMSB-w
jc :notRunt
bank NIC_BANK
mov nicIOAddr, #$05 ; TBCR0
mov w, #64 ; min ethernet frame size
call NICWrite
inc nicIOAddr ; ($06) TBCR1
mov w, #0
call NICWrite
jmp :transmit
:notRunt bank NIC_BANK
mov nicIOAddr, #$05 ; TBCR0
bank IP_BANK
mov w, #(6+6+2)
add w, ipLengthLSB
call NICWrite ; should not affect carry flag
inc nicIOAddr ; ($06) TBCR1
bank IP_BANK
mov w, ipLengthMSB
snc
inc wreg
call NICWrite
:transmit clr nicIOAddr ; CR
mov w, #%00000110 ; Page0, transmit
jmp NICWrite
; ******************************************************************************
_NICBufCopy
; Copy one part of the NIC's SRAM buffer to another part
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = source address in NIC's SRAM
; {nicCopyDestMSB,nicCopyDestLSB} = destination address in NIC's SRAM
; {nicCopyLenMSB,nicCopyLenLSB} = length of buffer to copy
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$0B ; RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
; initialize RDMA to get source byte
:loop clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
mov nicIOAddr, #$0A ; RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite
mov nicIOAddr, #$10 ; RDMA
call NICRead
mov nicCopyTemp, w ; store source byte temporarily
; initialize RDMA to write byte to destination
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopyDestLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopyDestMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
mov w, nicCopyTemp
call NICWrite
; increment source and destination pointers
inc nicCopySrcLSB
snz
inc nicCopySrcMSB
inc nicCopyDestLSB
snz
inc nicCopyDestMSB
; check if source page pointer hit receive buffer ceiling
mov w, nicCopySrcMSB
xor w, #RXBUF_END
jnz :here
mov nicCopySrcMSB, #RXBUF_START
; loop as many times as there are bytes to copy
:here decsz nicCopyLenLSB
jmp :loop
test nicCopyLenMSB
snz
retp
dec nicCopyLenMSB
jmp :loop
; ******************************************************************************
_NICBufWrite
; Writes a byte to the SRAM buffer in the NIC
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = address in buffer memory to write to
; w = byte to write
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
mov nicCopyTemp, w
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
mov w, nicCopyTemp
jmp NICWrite
; ******************************************************************************
_NICBufRead
; Reads a byte from the SRAM buffer in the NIC
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = address in buffer memory to read from
; OUTPUT: w = byte read
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #1 ; one-byte DMA
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite
mov nicIOAddr, #$10 ; RDMA
jmp NICRead
; ******************************************************************************
_NICBufIPAddrWr
; Writes the source and destination IP addresses to the SRAM buffer in the NIC
; INPUT: {nicCopySrcMSB,nicCopySrcLSB} = address in buffer memory to write to
; myIP3-0 = <source_IP>
; remoteIP3-0 = <destination_IP>
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, nicCopySrcLSB
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #(4+4) ; 8-byte DMA
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite
mov nicIOAddr, #$10 ; RDMA
; <source_IP>
call NICWriteSrcIP
; <destination_IP>
jmp NICWriteDestIP
; ******************************************************************************
_NICWriteSrcIP
; Write Source IP address to NIC's buffer using remote DMA. NIC must be pre-
; initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
jnb flags.GOT_IP_ADDR, :noIP
bank IP_BANK
mov w, myIP3
call NICWrite
bank IP_BANK
mov w, myIP2
call NICWriteAgain
mov w, myIP1
call NICWriteAgain
mov w, myIP0
jmp NICWriteAgain
:noIP mov w, #0 ; 0.0.0.0
call NICWrite
mov w, #0
call NICWriteAgain
call NICWriteAgain
jmp NICWriteAgain
; ******************************************************************************
_NICWriteDestIP
; Write Destination IP address to NIC's buffer using remote DMA. NIC must be
; pre-initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
mov w, remoteIP3
call NICWrite
bank IP_BANK
mov w, remoteIP2
call NICWriteAgain
bank IP_BANK
mov w, remoteIP1
call NICWriteAgain
bank IP_BANK
mov w, remoteIP0
jmp NICWriteAgain
; ******************************************************************************
_NICWriteSrcEth
; Write Source Ethernet address to NIC's buffer using remote DMA. NIC must be
; pre-initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #SX_ETH_ADDR0
call NICWrite
mov w, #SX_ETH_ADDR1
call NICWriteAgain
mov w, #SX_ETH_ADDR2
call NICWriteAgain
mov w, #SX_ETH_ADDR3
call NICWriteAgain
mov w, #SX_ETH_ADDR4
call NICWriteAgain
mov w, #SX_ETH_ADDR5
jmp NICWriteAgain
; ******************************************************************************
_NICWriteDestEth
; Write Destination Ethernet address to NIC's buffer using remote DMA. NIC must
; be pre-initialized for this.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, nicRemoteEth0
call NICWrite
mov w, nicRemoteEth1
call NICWriteAgain
mov w, nicRemoteEth2
call NICWriteAgain
mov w, nicRemoteEth3
call NICWriteAgain
mov w, nicRemoteEth4
call NICWriteAgain
mov w, nicRemoteEth5
jmp NICWriteAgain
; ******************************************************************************
_NICDMAInit
; Setup the NIC's RSAR and RBCR register in preparation for remote DMA.
; INPUT: nicCurrPktPtr = beginning of packet
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite
; initialize DMA to <type> field in ethernet frame
mov nicIOAddr, #$08 ; RSAR0
mov w, #(4+6+6) ; point to <type> field
call NICWrite
inc nicIOAddr ; ($09) RSAR1
mov w, nicCurrPktPtr
call NICWrite
inc nicIOAddr ; ($0A) RBCR0
mov w, #$FF
call NICWrite
inc nicIOAddr ; ($0B) RBCR1
mov w, #$0F
jmp NICWrite
ORG $400 ; Page2
ARPSendResponse jmp _ARPSendResponse
ARPSendStPacket jmp _ARPSendStPacket
CheckIPDatagram jmp _CheckIPDatagram
CheckIPDestAddr jmp _CheckIPDestAddr
ICMPProcPktIn jmp _ICMPProcPktIn
; ******************************************************************************
NICRead_2
; Shortform for calling NICRead(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICRead
; ******************************************************************************
NICReadAgain_2
; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICReadAgain
; ******************************************************************************
NICPseudoRead_2
; Shortform for calling NICPseudoRead(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICPseudoRead
; ******************************************************************************
NICPseudoRead6_2
; Shortform for calling NICPseudoRead(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICPseudoRead6
; ******************************************************************************
NICWrite_2
; Shortform for calling NICWrite(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICWrite
; ******************************************************************************
NICWriteAgain_2
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
NICDumpRxFrame_2
; Shortform for calling NICDumpRxFrame(), which is in Page1 (This is in Page2)
; ******************************************************************************
jmp @NICDumpRxFrame
; ******************************************************************************
ARPCheckCache
; Checks if remote(destination) host IP address is in cache. If so, update
; packet Ethernet address in NIC with cache entry, else send ARP request.
; INPUT: remoteIP0-3, host1IP0-3
; OUTPUT: none
; ******************************************************************************
jnb arpFlags.ARP_STL_TX, :cacheGo ; do not check cache if stalled packet to be txed
call ARPUpdateEthAddr ; now update stalled packet's ethernet address
clr arpFlags ; reset ARP
retp
:cacheGo ; check if remote host IP is in cache
bank IP_BANK
mov w, remoteIP0
bank ARP_BANK
xor w, host1IP0
jnz :cacheNoMatch ; no match
mov w, host1IP1
bank IP_BANK
xor w, remoteIP1
jnz :cacheNoMatch ; no match
mov w, remoteIP2
bank ARP_BANK
xor w, host1IP2
jnz :cacheNoMatch ; no match
mov w, host1IP3
bank IP_BANK
xor w, remoteIP3
jz :cacheMatch ; match! remoteIP0-3 = host1IP0-3
:cacheNoMatch setb arpFlags.ARP_REQ_SENT ; indicate an ARP request is sent
jmp ARPSendRequest ; do an ARP request now to get the remote Eth address
:cacheMatch clr arpFlags ; reset ARP
jmp ARPUpdateEthAddr ; check, update remote Eth address of pending packet in NIC
; ******************************************************************************
ARPUpdateEthAddr
; Updates Eth Address of pending packet to be txed in NIC's buffer with that in
; ARP cache
; INPUT: host1Eth0-5, stPktTxBufStart
; OUTPUT: none
; ******************************************************************************
bank NIC_BANK
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_2
mov nicIOAddr, #$08 ; RSAR0
mov w, #0
call NICWrite_2
inc nicIOAddr ; ($09) RSAR1
bank ARP_BANK
mov w, stPktTxBufStart ; stPktTxBufStart contains page number of stalled pkt
call NICWrite_2
inc nicIOAddr ; ($0A) RBCR0
mov w, #(6+6) ; 12-byte DMA
call NICWrite_2
inc nicIOAddr ; ($0B) RBCR1
mov w, #0 ; MSB is always zero
call NICWrite_2
clr nicIOAddr ; CR
mov w, #%00010010 ; Page0, remote write
call NICWrite_2
mov nicIOAddr, #$10 ; RDMA
; <destination_Eth>
bank ARP_BANK
mov w, host1Eth0
call NICWrite_2
bank ARP_BANK
mov w, host1Eth1
call NICWriteAgain_2
mov w, host1Eth2
call NICWriteAgain_2
mov w, host1Eth3
call NICWriteAgain_2
mov w, host1Eth4
call NICWriteAgain_2
mov w, host1Eth5
jmp NICWriteAgain_2
; ******************************************************************************
ARPCheckIfIs
; Checks received packet to see if it is ARP.
; Sends an ARP response if its a request.
; Updates cache if it's an ARP response.
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: remoteIP0-3 (:rcvdARPRequest), host1Eth0-5 (:rcvdARPResponse)
; ******************************************************************************
clrb flags.RX_IS_ARP
call @NICDMAInit
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite_2
mov nicIOAddr, #$10 ; RDMA
; check if ethernet <type> field contains ARP identifier (0x0806)
call NICRead_2
xor w, #$08
jnz :outtaHere
call NICReadAgain_2
xor w, #$06
jnz :outtaHere
setb flags.RX_IS_ARP ; yes, it's ARP, indicate that for main loop
; ignore <hardware_type>,<protocol_type>,<HLEN>,<PLEN>
call NICPseudoRead6_2
; Checks if the ARP packet received is a request or a response
call NICReadAgain_2
xor w, #$00
jnz :outtaHere ; not ARP at all
call NICReadAgain_2
mov globTemp1, w ; store ARP opcode
xor w, #$01 ; check if ARP Request (0x0001)
jz :rcvdARPRequest ; yes, process ARP request
mov w, globTemp1
xor w, #$02 ; check if ARP Response (0x0002)
jz :rcvdARPResponse ; yes, process ARP response
jmp :outtaHere ; no, not ARP at all
:rcvdARPRequest
; ignore <sender_HA>
call NICPseudoRead6_2
; if a TCP connection has been established, check to see if remote
; IP is the one we've established connection with, otherwise, make
; sure {remoteIP3-0} isn't clobbered
bank TCP_BANK
cjne tcpState, #TCP_ST_ESTABED, :senderIP
; check <source_IP>
mov fsr, #remoteIP3
call ARPCompare4
jnz :outtaHere
jmp :targetHA
; record the sender's IP addr (<sender_IP>)
:senderIP bank IP_BANK
call NICReadAgain_2
mov remoteIP3, w
call NICReadAgain_2
mov remoteIP2, w
call NICReadAgain_2
mov remoteIP1, w
call NICReadAgain_2
mov remoteIP0, w
; ignore <target_HA>
:targetHA call NICPseudoRead6_2
; check if <target_IP> is me
mov fsr, #myIP3
call ARPCompare4
jnz :outtaHere
; so it's for me!
call ARPSendResponse
jmp NICDumpRxFrame_2 ; we're done with this packet, dump it
:rcvdARPResponse
; <sender_HA>
; record sender's Eth address in ARP cache
mov fsr, #host1Eth0
mov globTemp1, #6
:ethLoop call NICReadAgain_2
mov indf, w
inc fsr
decsz globTemp1
jmp :ethLoop
; <sender_IP>
; check if sender's IP corrresponds to IP address in ARP cache
mov fsr, #host1IP3
call ARPCompare4
jz :ipAddrResolved
; whoops, sender doesn't correspond to who we're trying to resolve!
; if we're waiting for an ARP response (no problem here)
jb arpFlags.ARP_REQ_SENT, :outtaHere
; otherwise, we need to invalidate the now corrupted ARP cache
clr host1IP3
clr host1IP1
clr host1IP2
clr host1IP0
jmp :outtaHere
:ipAddrResolved setb arpFlags.ARP_RSP_RCVD ; indicate we got a successfull ARP response
:outtaHere snb flags.RX_IS_ARP ; check if packet was ARP
call NICDumpRxFrame_2 ; if it was ARP, we dump it; otherwise, don't touch it
retp
; ******************************************************************************
ARPCompare4
; Compares data from NICReadAgain() against a 4-byte word store consequtively
; in the SX's data memory
; INPUT: fsr = points to beginning of 4-byte word to compare against
; OUTPUT: z: 1 = match, 0 = not match
; ******************************************************************************
mov globTemp1, #4
:loop call NICReadAgain_2
xor w, indf
sz
retp ; mis-match
inc fsr
decsz globTemp1
jmp :loop
stz
retp
; ******************************************************************************
ARPSendCommon1
; Helper function for ARPSendRequest and ARPSendResponse
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; <type> = 0x0806
mov w, #$08
call NICWriteAgain_2
mov w, #$06
call NICWriteAgain_2
; <hardware_type> = 0x0001
mov w, #$00
call NICWriteAgain_2
mov w, #$01
call NICWriteAgain_2
; <protocol_type> = 0x0800
mov w, #$08
call NICWriteAgain_2
mov w, #$00
call NICWriteAgain_2
; <HLEN> = 0x06
mov w, #$06
call NICWriteAgain_2
; <PLEN> = 0x04
mov w, #$04
jmp NICWriteAgain_2
; ******************************************************************************
ARPSendCommon2
; Helper function for ARPSendRequest and ARPSendResponse
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; <sender_HA>
call @NICWriteSrcEth
; <sender_IP>
call @NICWriteSrcIP
; <target_HA>
call @NICWriteDestEth
; <target_IP>
call @NICWriteDestIP
; whew! the ethernet frame is now complete, get ready to send ...
bank NIC_BANK ; NICWriteDestIP changes bank
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_2
mov nicIOAddr, #$05 ; TBCR0
mov w, #$40 ; min ethernet packet size
call NICWrite_2
inc nicIOAddr ; ($06) TBCR1
mov w, #$00
call NICWrite_2
clr nicIOAddr ; CR
mov w, #%00000110 ; transmit
jmp NICWrite_2
; ******************************************************************************
ARPSendRequest
; Stores remote IP address for which pending packet is intended in ARP cache and
; sends an ARP Request
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
mov w, remoteIP3 ; store IP address of target in ARP cache
bank ARP_BANK
mov host1IP3, w
bank IP_BANK
mov w, remoteIP2
bank ARP_BANK
mov host1IP2, w
bank IP_BANK
mov w, remoteIP1
bank ARP_BANK
mov host1IP1, w
bank IP_BANK
mov w, remoteIP0
bank ARP_BANK
mov host1IP0, w
bank NIC_BANK
mov w, #$FF
mov nicRemoteEth0, w ; setup broadcast Ethernet address
mov nicRemoteEth1, w
mov nicRemoteEth2, w
mov nicRemoteEth3, w
mov nicRemoteEth4, w
mov nicRemoteEth5, w
mov w, #TXBUF3_START ; point to ARP transmit buffer
call @NICInitTxFrame
call ARPSendCommon1
; <operation> = 0x0001 , ARP request opcode
mov w, #$00
call NICWriteAgain_2
mov w, #$01
call NICWriteAgain_2
call ARPSendCommon2
bank TIMER_BANK ; initialise the APR timeout timer
clr arpTimerMSB
clr arpTimerLSB
retp
; ******************************************************************************
_ARPSendResponse
; Send an ARP Response in reply to a ARP request.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #TXBUF3_START ; point to ARP transmit buffer
call @NICInitTxFrame
call ARPSendCommon1
; <operation> = 0x0002
mov w, #$00
call NICWriteAgain_2
mov w, #$02
call NICWriteAgain_2
jmp ARPSendCommon2
; ******************************************************************************
_ARPSendStPacket
; Sends the ARP stalled packet if any and also check if a timeout on waiting for
; an ARP response occured. If timeout, clear ARP cache
; INPUT: none
; OUTPUT: none
; ******************************************************************************
sb arpFlags.ARP_REQ_SENT ; check if we are waiting for an ARP response
retp ; no, nothing to do here
jnb arpFlags.ARP_RSP_RCVD, :checkARPTimeout ; if no ARP response rcvd, check if timeout
; yes we have rcvd a response so now we can send the stalled packet
clr arpFlags ; reset ARP
setb arpFlags.ARP_STL_TX ; indicate that stalled packet is to be transmitted
; re-initialize the NIC's TPSR
bank NIC_BANK
clr nicIOAddr ; CR
:wait call NICRead_2
jb wreg.2, :wait ; wait for prior transmission to complete
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_2
mov nicIOAddr, #$04 ; TPSR
bank ARP_BANK
mov w, stPktTxBufStart ; load stalled packet's address pointer
call NICWrite_2
; read the NIC's transmit buffer to find out the IP <length>
; so that we can re-initialize the NIC's TBCR
bank ARP_BANK
mov w, stPktTxBufStart
bank NIC_BANK
mov nicCopySrcMSB, w
mov nicCopySrcLSB, #(6+6+2+2) ; IP <length> (MSB)
call @NICBufRead
bank IP_BANK
mov ipLengthMSB, w
bank NIC_BANK
inc nicCopySrcLSB ; IP <length> (LSB)
call @NICBufRead
bank IP_BANK
mov ipLengthLSB, w
jmp @NICSendTxFrame ; transmit the stalled ethernet frame
:checkARPTimeout
bank TIMER_BANK
csae arpTimerMSB, #ARP_TIMEOUT ; has the timer expired?
retp ; no, just return
clr arpFlags ; yes, reset ARP flags
; Very important now! Clear the ARP cache, since it acted as a temporary storage of the
; requested IP address. If we do not clear the cache now, the next re-transmit routine will
; find a match in the ARP cache since the original IP is still there!
bank ARP_BANK
clr host1IP3
clr host1IP2
clr host1IP1
clr host1IP0
retp
; ******************************************************************************
_CheckIPDatagram
; Checks to see if received ethernet frame contains an IP datagram. If not, the
; frame will be discarded since this stack doesn't deal with any other kinds of
; data-link layer protocol.
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: none
; ******************************************************************************
mov w, #~((1<<RX_IS_ICMP)|(1<<RX_IS_UDP)|(1<<RX_IS_TCP)|(1<<RX_IS_IP_BCST))
and flags, w
call @NICDMAInit
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite_2
mov nicIOAddr, #$10 ; RDMA
; check if ethernet <type> field contains IP identifier (0x0800)
call NICRead_2
xor w, #$08
jnz :outtaHere
call NICReadAgain_2
xor w, #$00
jnz :outtaHere
; check <version> and <HLEN>
call NICReadAgain_2
xor w, #$45 ; version = 4, header length = 5
jnz :outtaHere
; ignore <service_type>
call NICPseudoRead_2
; record <total_length>
call NICReadAgain_2
bank IP_BANK
mov ipLengthMSB, w
call NICReadAgain_2
mov ipLengthLSB, w
; adjust {ipLengthMSB,ipLengthLSB} to reflect number of data bytes
sub ipLengthLSB, #20
sc
dec ipLengthMSB
; ignore <identification>
REPT 2
call NICPseudoRead_2
ENDR
; check against IP packet fragmentation
call NICReadAgain_2
jb wreg.5, :outtaHere ; <flags>
call NICReadAgain_2
xor w, #0 ; <fragment_offset>
jnz :outtaHere
; ignore <time_to_live>
call NICPseudoRead_2
; record <protocol>
call NICReadAgain_2
mov ipProtocol, w
; ignore <header_checksum>
REPT 2
call NICPseudoRead_2
ENDR
; if a TCP connection has been established, check to see if remote
; IP is the one we've established connection with, otherwise, make
; sure {remoteIP3-0} isn't clobbered
bank TCP_BANK
cjne tcpState, #TCP_ST_ESTABED, :srcIP
; check <source_IP>
bank IP_BANK
call NICReadAgain_2
xor w, remoteIP3
jnz :outtaHere
call NICReadAgain_2
xor w, remoteIP2
jnz :outtaHere
call NICReadAgain_2
xor w, remoteIP1
jnz :outtaHere
call NICReadAgain_2
xor w, remoteIP0
jnz :outtaHere
jmp :destIP
; record <source_IP>
:srcIP bank IP_BANK
call NICReadAgain_2
mov remoteIP3, w
call NICReadAgain_2
mov remoteIP2, w
call NICReadAgain_2
mov remoteIP1, w
call NICReadAgain_2
mov remoteIP0, w
; check <destination_IP>
:destIP clr globTemp2
mov w, myIP3
call CheckIPDestAddr
jnz :outtaHere
mov w, myIP2
call CheckIPDestAddr
jnz :outtaHere
mov w, myIP1
call CheckIPDestAddr
jnz :outtaHere
mov w, myIP0
call CheckIPDestAddr
jnz :outtaHere
xor globTemp2, #4
snz
setb flags.RX_IS_IP_BCST ; IP broadcast addr detected
; ok! determine which higher-level protocol
mov w, #1 ; ICMP
xor w, ipProtocol
snz
setb flags.RX_IS_ICMP
mov w, #17 ; UDP
xor w, ipProtocol
snz
setb flags.RX_IS_UDP
mov w, #6 ; TCP
xor w, ipProtocol
snz
setb flags.RX_IS_TCP
retp
:outtaHere jmp NICDumpRxFrame_2 ; if it ain't IP, forget it!
; ******************************************************************************
_CheckIPDestAddr
; Helper function for CheckIPDatagram
; Check for a match of the IP <destination_IP> field
; INPUT: w = byte of IP to check against
; OUTPUT: z = set if match
; globTemp2 = incremented if matched against 0xFF
; ******************************************************************************
mov globTemp1, w
call NICReadAgain_2
xor globTemp1, w
snz
retp
xor w, #$FF ; IP broadcast addr
sz
retp
inc globTemp2
stz
retp
; ******************************************************************************
_ICMPProcPktIn
; Process ICMP message. Only Echo Request ICMP messages are handled. All others
; are ignored. If Echo Request message is detected, this will generate the echo
; reply.
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: none
; ******************************************************************************
; check <type>
call NICReadAgain_2
xor w, #$08 ; echo-request
jnz :outtaHere
bank IP_BANK
add ipLengthLSB, #(2+20)
snc
inc ipLengthMSB
bank NIC_BANK
mov nicCopySrcMSB, nicCurrPktPtr ; point to receive buffer
mov nicCopySrcLSB, #(4+6+6) ; don't copy NIC header, eth src & destn
mov nicCopyDestMSB, #TXBUF1_START ; point to transmit buffer
mov nicCopyDestLSB, #(6+6)
bank IP_BANK
mov w, ipLengthMSB
bank NIC_BANK
mov nicCopyLenMSB, w
bank IP_BANK
mov w, ipLengthLSB
bank NIC_BANK
mov nicCopyLenLSB, w
mov w, #TXBUF1_START ; point to transmit buffer
bank ARP_BANK
mov stPktTxBufStart ,w ; Store ICMP packet start address
bank NIC_BANK
call @NICInitTxFrame
call @NICBufCopy
; change ICMP <type> to echo reply (0)
mov nicCopySrcMSB, #TXBUF1_START ; point to transmit buffer
mov nicCopySrcLSB, #(6+6+2+20) ; point to ICMP <type>
mov w, #$00
call @NICBufWrite
; generate ICMP <checksum>
mov nicCopySrcMSB, #TXBUF1_START ; point to transmit buffer
bank IP_BANK
mov w, ipLengthMSB
bank NIC_BANK
mov nicCopyLenMSB, w
bank IP_BANK
mov w, ipLengthLSB
bank NIC_BANK
mov nicCopyLenLSB, w
sub nicCopyLenLSB, #(2+20+4)
sc
dec nicCopyLenMSB
call @ICMPGenCheckSum
; adjust IP <source_IP> and <destination_IP>
mov nicCopySrcMSB, #TXBUF1_START ; point to transmit buffer
mov nicCopySrcLSB, #(6+6+2+12) ; point to IP <source_IP>
call @NICBufIPAddrWr
bank IP_BANK
sub ipLengthLSB, #2
sc
dec ipLengthMSB
call @NICSendTxFrame
:outtaHere jmp NICDumpRxFrame_2
ORG $600 ; Page3
TCPTransmit jmp _TCPTransmit
TCPReTransmit jmp _TCPReTransmit
TCPAppPassiveOpen jmp _TCPAppPassiveOpen
TCPAppActiveOpen jmp _TCPAppActiveOpen
TCPAppClose jmp _TCPAppClose
; ******************************************************************************
NICWrite_3
; Shortform for calling NICWrite(), which is in Page1 (This is in Page3)
; ******************************************************************************
jmp @NICWrite
; ******************************************************************************
NICWriteAgain_3
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page3)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
NICDumpRxFrame_3
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page3)
; ******************************************************************************
jmp @NICDumpRxFrame
; ******************************************************************************
ICMPGenCheckSum
; Goes through the ICMP message stored in the NIC's SRAM buffer and computes
; the ICMP message checksum.
; INPUT: nicCopySrcMSB = transmit buffer page
; {nicCopyLenMSB,nicCopyLenLSB} = (length of ICMP message - 4)
; OUTPUT: {ipCheckSumMSB,ipCheckSumLSB} = new checksum value
; ******************************************************************************
call IPCheckSumInit
bank NIC_BANK
mov nicCopyTemp, nicCopySrcMSB
clr nicIOAddr ; CR
mov w, #%00100010 ; Page0, abort DMA
mov nicIOAddr, #$08 ; RSAR0
mov w, #(6+6+2+20+4) ; point to ICMP <identifier>
call NICWrite_3
inc nicIOAddr ; ($09) RSAR1
mov w, nicCopySrcMSB
call NICWrite_3
inc nicIOAddr ; ($0A) RBCR0
mov w, nicCopyLenLSB
call NICWrite_3
inc nicIOAddr ; ($0B) RBCR1
mov w, nicCopyLenMSB
call NICWrite_3
clr nicIOAddr ; CR
mov w, #%00001010 ; Page0, remote read
call NICWrite_3
mov nicIOAddr, #$10 ; RDMA
; configure data bus for input
_mode DIR_W
mov w, #$FF ; input
mov !NIC_DATA_PORT, w
; put addr out on addr bus
mov w, NIC_CTRL_PORT
and w, #%11100000
or w, nicIOAddr
mov NIC_CTRL_PORT, w
inc nicCopyLenMSB ; in order to loop easier later
:loop call @NICReadAgain
call IPCheckSumAcc
bank NIC_BANK
decsz nicCopyLenLSB
jmp :loop
decsz nicCopyLenMSB
jmp :loop
mov nicCopySrcMSB, nicCopyTemp
mov nicCopySrcLSB, #(6+6+2+20+2)
bank IP_BANK
mov w, /ipCheckSumMSB
call @NICBufWrite
inc nicCopySrcLSB
bank IP_BANK
mov w, /ipCheckSumLSB
call @NICBufWrite
retp
; ******************************************************************************
IPCheckSumInit
; Initializes the IP checksum routine.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
clr ipCheckSumMSB
clr ipCheckSumLSB
clrb flags.IP_CHKSUM_LSB ; next byte is MSB
retp
; ******************************************************************************
IPCheckSumAcc
; Accumulate the IP checksum value by adding the next 16-bit number
; IP header checksum (also used to compute ICMP checksum) is computed by doing
; the one's complement of the one's complement sum of the 16-bit numbers in the
; header.
; INPUT: w = byte to accumulate
; flags.IP_CHKSUM_LSB = set if processing LSB, clear if processing MSB
; OUTPUT: {ipCheckSumMSB,ipCheckSumLSB} = new checksum value
; ******************************************************************************
bank IP_BANK
jnb flags.IP_CHKSUM_LSB, :msb ; are we processing an MSB?
:lsb add ipCheckSumLSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc ipCheckSumMSB ; yes
snz
inc ipCheckSumLSB
jmp :done
:msb add ipCheckSumMSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc ipCheckSumLSB ; yes, this time it is added to the LSB
snz
inc ipCheckSumMSB
:done xor flags, #(1<<IP_CHKSUM_LSB)
retp
; ******************************************************************************
IPGenCheckSum
; Generate the IP header checksum
; INPUT: {ipLengthMSB,ipLengthLSB} = IP <total_length>
; {ipIdentMSB,ipIdentLSB} = IP <identification>
; ipProtocol = 1(ICMP)/ 6(TCP)/ 17(UDP)
; OUTPUT: {ipCheckSumMSB,ipCheckSumLSB} = new checksum value
; ******************************************************************************
bank IP_BANK
call IPCheckSumInit
mov w, #$45 ; Version 4, 5x 32 bit words in IP header
call IPCheckSumAcc
mov w, #$00
call IPCheckSumAcc
mov w, ipLengthMSB
call IPCheckSumAcc
mov w, ipLengthLSB
call IPCheckSumAcc
mov w, ipIdentMSB
call IPCheckSumAcc
mov w, ipIdentLSB
call IPCheckSumAcc
mov w, #%01000000 ; Don't fragment
call IPCheckSumAcc
mov w, #0
call IPCheckSumAcc
mov w, #IP_TTL
call IPCheckSumAcc
mov w, ipProtocol
call IPCheckSumAcc
jnb flags.GOT_IP_ADDR, :remoteIP ; IP = 0.0.0.0 if no IP
mov w, myIP3
call IPCheckSumAcc
mov w, myIP2
call IPCheckSumAcc
mov w, myIP1
call IPCheckSumAcc
mov w, myIP0
call IPCheckSumAcc
:remoteIP mov w, remoteIP3
call IPCheckSumAcc
mov w, remoteIP2
call IPCheckSumAcc
mov w, remoteIP1
call IPCheckSumAcc
mov w, remoteIP0
call IPCheckSumAcc
retp
; ******************************************************************************
IPStartPktOut
; Starts an outgoing IP packet by constructing the IP packet header
; INPUT: {ipLengthMSB,ipLengthLSB} = IP <total_length>
; {ipIdentMSB,ipIdentLSB} = IP <identification>
; ipProtocol = 1(ICMP)/ 6(TCP)/ 17(UDP)
; {ipCheckSumMSB,ipCheckSumLSB} = IP <header_checksum>
; OUTPUT: none
; ******************************************************************************
bank IP_BANK
mov w, #6 ; TCP protocol
xor w, ipProtocol
sz
mov w, #TXBUF1_START ; default tx buffer is TXBUF1
snz
mov w, #TXBUF2_START ; unless it's TCP, then we use TXBUF2
bank ARP_BANK
sb arpFlags.ARP_REQ_SENT ; check if a prev packet is stalled
mov stPktTxBufStart, w ; no, store new address pointer to new packet
call @NICInitTxFrame
; <type> = 0x0800
mov w, #$08
call NICWrite_3
mov w, #$00
call NICWriteAgain_3
; <version> = 4, <HLEN> = 5
mov w, #$45
call NICWriteAgain_3
; <service_type>
mov w, #$00 ; normal precedence, D=T=R=0
call NICWriteAgain_3
; <total_length>
bank IP_BANK
mov w, ipLengthMSB
call NICWriteAgain_3
mov w, ipLengthLSB
call NICWriteAgain_3
; <identification>
mov w, ipIdentMSB
call NICWriteAgain_3
mov w, ipIdentLSB
call NICWriteAgain_3
; <flags>, <fragment_offset>
mov w, #%01000000 ; do not fragment
call NICWriteAgain_3
mov w, #0 ; offset = 0
call NICWriteAgain_3
; <time_to_live>
mov w, #IP_TTL
call NICWriteAgain_3
; <protocol>
mov w, ipProtocol
call NICWriteAgain_3
; <header_checksum>
mov w, /ipCheckSumMSB
call NICWriteAgain_3
mov w, /ipCheckSumLSB
call NICWriteAgain_3
; <source_IP>
call @NICWriteSrcIP
; <destination_IP>
call @NICWriteDestIP
retp
; ******************************************************************************
TCPProcPktIn
; Process a received TCP packet. This function implements the TCP state machine
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call @TCPRxHeader ; receive the header
snz ; is the packet OK?
retp ; no, return
; check the special states
bank TCP_BANK
cje tcpState, #TCP_ST_CLOSED, :CLOSED
cje tcpState, #TCP_ST_LISTEN, :LISTEN
cje tcpState, #TCP_ST_SYNSENT, :SYNSENT
cje tcpState, #TCP_ST_FINWAIT1, :FINWAIT1
cje tcpState, #TCP_ST_FINWAIT2, :FINWAIT2
cje tcpState, #TCP_ST_LASTACK, :LASTACK
; check the flags
bank TCB_BANK
snb tcbFlags.TCP_FLAG_RST ; is the reset flag set?
jmp :gotoClosed ; yes, drop packet and close the connection
snb tcbFlags.TCP_FLAG_SYN ; is the SYN bit set?
jmp @TCPSendReset ; yes, drop the packet and send a reset
sb tcbFlags.TCP_FLAG_ACK ; is the ACK bit set?
jmp NICDumpRxFrame_3 ; no, drop the packet
call @TCPChkSeq ; check if received packet is expected
; we only accept ACKs of complete packets. Assume the ACK is for our last packet
bank TCP_BANK
mov tcpState, #TCP_ST_ESTABED ; switch to the established state
:noOutstanding ; does the packet contain data? (Determine this from the length)
test tcpLengthMSB
jnz :packetHasData ; MSB is not zero
test tcpLengthLSB ; MSB is zero
jz :noData ; MSB = LSB = 0
:packetHasData call @TCPAppRxBytes ; inform app how many bytes available
_bank TCP_BANK
inc tcpLengthMSB
:processData call @NICReadAgain ; receive a byte
call @TCPAppRxData ; pass the byte to the application
_bank TCP_BANK ; _bank cus we called TCPAppRxData priorly
decsz tcpLengthLSB
jmp :processData
decsz tcpLengthMSB
jmp :processData
inc tcpLengthLSB ; indicate for later there was data received
:noData call @TCPAckUpdate
; send an ACK packet
bank TCP_BANK
test tcpLengthLSB ; was data received?
jz :checkFIN ; no, it was an ACK packet. Just return
call @TCPAppRxDone ; indicate the packet was OK to the application
_bank TCP_BANK ; _bank cus we called TCPAppRxDone priorly
jb tcpRxFlags.TCP_FLAG_FIN, :doClose ; if FIN bit set, close the connection
jmp @TCPSendAck
:checkFIN bank TCP_BANK
jb tcpRxFlags.TCP_FLAG_FIN, :doClose; is the FIN bit set?
jmp NICDumpRxFrame_3
:doClose call @TCPIncRcvNxt ; ACK the FIN
bank TCP_BANK
mov tcpState, #TCP_ST_CLOSEWAIT ; change state
jmp @TCPSendAck
:gotoClosed bank TCP_BANK
mov tcpState, #TCP_ST_CLOSED ; go to the closed state
jmp NICDumpRxFrame_3 ; discard received packet
:FINWAIT1 call NICDumpRxFrame_3
bank TCP_BANK
sb tcpRxFlags.TCP_FLAG_ACK ; check for ACK of FIN
retp
mov tcpState, #TCP_ST_FINWAIT2 ; rcved ACK of FIN
retp
:FINWAIT2 call NICDumpRxFrame_3
bank TCP_BANK
sb tcpRxFlags.TCP_FLAG_FIN ; check for FIN
retp
mov tcpState, #TCP_ST_CLOSED ; rcved FIN
call @TCPIncRcvNxt ; ACK the FIN
jmp @TCPSendAck
:LASTACK ; ignore the packet
; should check the packet is actually an ACK
mov tcpState, #TCP_ST_CLOSED ; go to the closed state
jmp NICDumpRxFrame_3
:CLOSED jmp @TCPSendReset ; we shouldn't receive packets while closed
:LISTEN call NICDumpRxFrame_3 ; discard received packet
bank TCB_BANK
snb tcbFlags.TCP_FLAG_RST ; check for an RST
retp ; ignore a packet with RST
snb tcbFlags.TCP_FLAG_ACK ; check for an ACK
jmp @TCPSendReset ; bad ACK, send a RST
call @TCPCopySeqToNxt
call @TCPSendSynAck
bank TCP_BANK
mov tcpState, #TCP_ST_SYNRCVED ; change state
retp
:SYNSENT call NICDumpRxFrame_3
bank TCB_BANK
jb tcbFlags.TCP_FLAG_RST, :rst ; is the reset bit set?
sb tcbFlags.TCP_FLAG_SYN ; if SYN bit not set,
retp ; ignore packet
sb tcbFlags.TCP_FLAG_ACK ; if ACK bit not set,
retp ; ignore packet
; check that SND.UNA <= SEG.ACK <= SND.NXT
bank TCP_BANK
mov tcpState, #TCP_ST_ESTABED ; the connection is now estabished
bank IP_BANK
clr ipLengthMSB ; set the received data length to one
mov ipLengthLSB, #1 ; "
call @TCPAckUpdate
jmp @TCPSendAck
:rst bank TCP_BANK
mov tcpState, #TCP_ST_CLOSED ; close the TCP
retp
; ******************************************************************************
_TCPTransmit
; See if the application has any data to transmit. If there are no outstanding
; packets and the application has data, then transmit a packet.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK ; _bank cus we called TCPAppInit priorly
cje tcpState, #TCP_ST_SYNSENT, :timeout ; check for SYN timeout
cje tcpState, #TCP_ST_CLOSEWAIT, :closeWait
cjae tcpState, #TCP_ST_ESTABED, :ok
retp
:ok test tcpUnAckMSB ; are there any bytes unacknowledged?
jnz :timeout
test tcpUnAckLSB
jnz :timeout
cje tcpState, #TCP_ST_FINWAIT1, :finTimeout ; check for FIN timeout
:askAppTxData cse tcpState, #TCP_ST_ESTABED
retp ; only ask if connection is established
call @TCPAppTxBytes ; no, ask the application if it wants to transmit
_bank TCP_BANK ; _bank cus we called TCPAppTxBytes priorly
mov w, tcpUnAckMSB
or w, tcpUnAckLSB
jnz :appHasTxData
retp ; nope, it doesn't; so we're done here then
:appHasTxData ; start a TCP packet
mov tcpLengthLSB, tcpUnAckLSB ; tcpLength = # bytes to transmit
mov tcpLengthMSB, tcpUnAckMSB
bank TCB_BANK
mov tcbFlags, #((1<<TCP_FLAG_PSH)|(1<<TCP_FLAG_ACK))
call @TCPCheckSumInit
call @TCPStartPktOut ; send the header
call @TCPUpdateSeq ; Update the outgoing sequence no
; insert the packet data while computing the checksum over the data
bank TCP_BANK
mov tcpTmpLSB, tcpUnAckLSB
mov tcpTmpMSB, tcpUnAckMSB
inc tcpTmpMSB ; so that we can loop easier later
:dataLoop call @TCPAppTxData
_banky TCP_BANK ; cus we called TCPAppTxData priorly
call NICWriteAgain_3 ; which doesn't clobber w, which is nice
call @TCPCheckSumAcc ; accumulate the checksum
decsz tcpTmpLSB
jmp :dataLoop
decsz tcpTmpMSB
jmp :dataLoop
; now go back and fill in the TCP checksum
bank NIC_BANK
mov nicCopySrcMSB, #TXBUF2_START
mov nicCopySrcLSB, #(6+6+2+20+16) ; TCP <checksum> (MSB)
bank TCP_BANK
mov w, /tcpCheckSumMSB
call @NICBufWrite
bank NIC_BANK
inc nicCopySrcLSB
bank TCP_BANK
mov w, /tcpCheckSumLSB
call @NICBufWrite
call @NICSendTxFrame ; end and send the packet
bank TIMER_BANK ; initialise the restart timer
clr tcpTimerMSB
clr tcpTimerLSB
retp
:finTimeout bank TIMER_BANK
csae tcpTimerMSB, #TCP_RESTART_EXP ; has the restart timer expired?
retp ; no
clr tcpTimerMSB ; yes, initialise the restart timer
clr tcpTimerLSB
jmp @TCPSendFin
:timeout bank TIMER_BANK
csae tcpTimerMSB, #TCP_RESTART_EXP ; has the restart timer expired?
retp ; no
clr tcpTimerMSB ; yes, initialise the restart timer
clr tcpTimerLSB
jmp @TCPReTransmit ; transmit the packet again
:closeWait mov tcpState, #TCP_ST_LASTACK
jmp @TCPSendFin ; send FIN, ACK
; ******************************************************************************
_TCPReTransmit
; This is called to retransmit the TCP packet that's already setup in the NIC's
; TCP transmit buffer. Remember that a UDP/ICMP/ARP packet may have been sent
; after the TCP packet was initially sent, so we have to re-setup the NIC
; carefully.
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; re-initialize the NIC's TPSR
bank NIC_BANK
clr nicIOAddr ; CR
:wait call @NICRead
jb wreg.2, :wait ; wait for prior transmission to complete
mov w, #%00100010 ; Page0, abort DMA
call NICWrite_3
mov nicIOAddr, #$04 ; TPSR
mov w, #TXBUF2_START
call NICWrite_3
; read the NIC's TCP transmit buffer to find out the IP <length>
; so that we can re-initialize the NIC's TBCR
mov nicCopySrcMSB, #TXBUF2_START
mov nicCopySrcLSB, #(6+6+2+2) ; IP <length> (MSB)
call @NICBufRead
bank IP_BANK
mov ipLengthMSB, w
bank NIC_BANK
inc nicCopySrcLSB ; IP <length> (LSB)
call @NICBufRead
bank IP_BANK
mov ipLengthLSB, w
jmp @NICSendTxFrame ; re-transmit the ethernet frame
; ******************************************************************************
_TCPAppPassiveOpen
; Do a passive open. i.e. listen for connections on a given port.
; [TCP API Function]
; INPUT: {tcbLocalPortMSB,tcbLocalPortLSB} = TCP port to listen on
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK
mov tcpState, #TCP_ST_LISTEN
clr tcpUnAckMSB
clr tcpUnAckLSB
retp
; ******************************************************************************
_TCPAppActiveOpen
; Do a active open. i.e. initiate a connect to a remote TCP.
; [TCP API Function]
; INPUT: {remoteIP0-3} = destination IP addr
; {tcbLocalPortMSB,tcbLocalPortLSB} = local TCP port
; {tcpRemotePortMSB,tcbRemotePortLSB} = remote TCP port
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK
clr tcpUnAckMSB
clr tcpUnAckLSB
mov tcpState, #TCP_ST_SYNSENT
bank TCB_BANK
mov tcbFlags, #(1<<TCP_FLAG_SYN)
jmp @TCPSendSyn
; ******************************************************************************
_TCPAppClose
; Force the current connection to close
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank TCP_BANK
mov tcpState, #TCP_ST_FINWAIT1
clr tcpUnAckMSB
clr tcpUnAckLSB
bank TCB_BANK
mov tcbFlags, #((1<<TCP_FLAG_FIN)|(1<<TCP_FLAG_ACK))
jmp @TCPSendEmptyPkt
ORG $800 ; Page4
TCPRxHeader jmp _TCPRxHeader
TCPChkSeq jmp _TCPChkSeq
TCPRestorePrev jmp _TCPRestorePrev
TCPUpdateSeq jmp _TCPUpdateSeq
; ******************************************************************************
NICReadAgain_4
; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page4)
; ******************************************************************************
jmp @NICReadAgain
; ******************************************************************************
NICWriteAgain_4
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page4)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
NICDumpRxFrame_4
; Shortform for calling NICDumpRxFrame(), which is in Page1 (This is in Page4)
; ******************************************************************************
jmp @NICDumpRxFrame
; ******************************************************************************
TCPAddRcvNxt
; Add an 16-bit number to RCV.NXT
; INPUT: {ipLengthMSB,ipLengthLSB} = number to add
; OUTPUT: {tcbRcvNxt1-4}
; ******************************************************************************
bank IP_BANK
mov w, ipLengthLSB
bank TCB_BANK
add tcbRcvNxt1, w
bank IP_BANK
mov w, ipLengthMSB
snc
mov w, ++ipLengthMSB
bank TCB_BANK
add tcbRcvNxt2, w
sc
retp
incsz tcbRcvNxt3
retp
inc tcbRcvNxt4
retp
; ******************************************************************************
TCPIncRcvNxt
; Increment RCV.NXT by one
; INPUT: none
; OUTPUT: {tcbRcvNxt1-4}
; ******************************************************************************
bank TCB_BANK
inc tcbRcvNxt1
sz
retp
incsz tcbRcvNxt2
retp
incsz tcbRcvNxt3
retp
inc tcbRcvNxt4
retp
; ******************************************************************************
TCPIncSndUna
; Increment SND.UNA by one
; INPUT: none
; OUTPUT: {tcbSndUna1-4}
; ******************************************************************************
bank TCB_BANK
inc tcbSndUna1
sz
retp
incsz tcbSndUna2
retp
incsz tcbSndUna3
retp
inc tcbSndUna4
retp
; ******************************************************************************
TCPCopySeqToNxt
; Copy {tcpTmpSeq4-1} -> {tcbRcvNxt4-1}
; INPUT: {tcpTmpSeq4-1}
; OUTPUT: {tcbRcvNxt4-1}
; ******************************************************************************
bank TCP_BANK
mov w, tcpTmpSeq4
bank TCB_BANK
mov tcbRcvNxt4, w
bank TCP_BANK
mov w, tcpTmpSeq3
bank TCB_BANK
mov tcbRcvNxt3, w
bank TCP_BANK
mov w, tcpTmpSeq2
bank TCB_BANK
mov tcbRcvNxt2, w
bank TCP_BANK
mov w, tcpTmpSeq1
bank TCB_BANK
mov tcbRcvNxt1, w
retp
; ******************************************************************************
TCPAckUpdate
; Update SND.UNA and RCV.NXT
; INPUT: {tcpTmpAck4-1}
; {tcpTmpSeq4-1}
; {ipLengthMSB,ipLengthLSB} = length of received TCP data
; OUTPUT: {tcpSndUna4-1}
; {tcpRcvNxt4-1}
; ******************************************************************************
call @TCPCopySeqToNxt ; set RCV.NXT = SEG.SEQ
jmp @TCPAddRcvNxt ; add the length of the received packet to the ACK
; ******************************************************************************
TCPCmpNxtSeq
; Check if RCV.NXT == SEG.SEQ
; INPUT: {tcpTmpSeq4-1} = SEG.SEQ
; {tcbRcvNxt4-1} = RCV.NXT
; OUTPUT: z is set if RCV.NXT == SEG.SEQ
; ******************************************************************************
bank TCB_BANK
mov w, tcbRcvNxt1
bank TCP_BANK
xor w, tcpTmpSeq1
sz
retp
bank TCB_BANK
mov w, tcbRcvNxt2
bank TCP_BANK
xor w, tcpTmpSeq2
sz
retp
bank TCB_BANK
mov w, tcbRcvNxt3
bank TCP_BANK
xor w, tcpTmpSeq3
sz
retp
bank TCB_BANK
mov w, tcbRcvNxt4
bank TCP_BANK
xor w, tcpTmpSeq4
retp
; ******************************************************************************
TCPSendEmptyPkt
; Constructs and sends a TCP packet containing no data
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbSndUna4-1} = sequence number
; {tcbRcvNxt4-1} = acknowledgement number
; tcbFlags = code flags
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
call @TCPCheckSumInit
bank TCP_BANK
clr tcpLengthMSB
clr tcpLengthLSB
call @TCPStartPktOut
call @NICSendTxFrame
bank TIMER_BANK
clr tcpTimerMSB
clr tcpTimerLSB
retp
; ******************************************************************************
TCPSendReset
; Send a reset packet with <SEQ=SEG.ACK><CTL=RST> and discard the received
; packet
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbRcvNxt4-1} = acknowledgement number
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
bank TCB_BANK
mov tcbFlags, #(1<<TCP_FLAG_RST)
call @TCPSendEmptyPkt
jmp NICDumpRxFrame_4 ; discard the received pkt
; ******************************************************************************
TCPSendSyn
; Send a SYN packet
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbRcvNxt4-1} = acknowledgement number
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT: {tcpSndUna4-1} = new sequence number
; ******************************************************************************
bank TCB_BANK
mov tcbFlags, #(1<<TCP_FLAG_SYN)
jmp TCPSendISN
; ******************************************************************************
TCPSendISN
; Send the TCP initial sequence number
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbRcvNxt4-1} = acknowledgement number
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT: {tcpSndUna4-1} = new sequence number
; ******************************************************************************
; obtain a random number for starting sequence number
bank NIC_BANK
mov w, nicCurrPktPtr
xor w, nicRemoteEth0
bank IP_BANK
xor w, ipIdentLSB
bank TCB_BANK
mov tcbSndUna4, w
mov tcbSndUna3, w
mov tcbSndUna2, w
mov tcbSndUna1, w
call @TCPIncRcvNxt
call @TCPSendEmptyPkt
jmp @TCPIncSndUna
; ******************************************************************************
TCPSendSynAck
; Send an SYN-ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=SYN>
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbRcvNxt4-1} = acknowledgement number
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT: {tcpSndUna4-1} = new sequence number
; ******************************************************************************
bank TCB_BANK
mov tcbFlags, #((1<<TCP_FLAG_SYN)|(1<<TCP_FLAG_ACK))
jmp @TCPSendISN
; ******************************************************************************
TCPSendAck
; Send an ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> and discard the
; received packet
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbSndUna4-1} = sequence number
; {tcbRcvNxt4-1} = acknowledgement number
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
bank TCB_BANK
mov tcbFlags, #(1<<TCP_FLAG_ACK)
call @TCPSendEmptyPkt
jmp NICDumpRxFrame_4 ; discard the received pkt
; ******************************************************************************
TCPSendFin
; Send a FIN packet and discard the received packet
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcbSndUna4-1} = sequence number
; {tcbRcvNxt4-1} = acknowledgement number
; tcbFlags = code flags
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
bank TCB_BANK
mov tcbFlags, #(1<<TCP_FLAG_FIN)|(1<<TCP_FLAG_ACK)
call @TCPSendEmptyPkt
jmp NICDumpRxFrame_4 ; discard the received pkt
; ******************************************************************************
TCPCheckSumInit
; Clear TCP checksum value to prepare for new checksum calculation
; INPUT: none
; OUTPUT: {tcpCheckSumMSB,tcpCheckSumLSB}
; ******************************************************************************
bank TCP_BANK
clr tcpCheckSumMSB
clr tcpCheckSumLSB
clrb flags.TCP_CHKSUM_LSB ; next byte is MSB
retp
; ******************************************************************************
TCPCheckSumAcc
; Accumulate the TCP checksum. Checksum is computed by doing the one's
; complement of the one's complement sum of 16-bit numbers
; INPUT: w = byte to accumulate
; flags.TCP_CHKSUM_LSB = set if processing LSB, clear if processing MSB
; OUTPUT: {tcpCheckSumMSB,tcpCheckSumLSB}
; ******************************************************************************
bank TCP_BANK
jnb flags.TCP_CHKSUM_LSB, :msb ; are we processing an MSB?
:lsb add tcpCheckSumLSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc tcpCheckSumMSB ; yes
snz
inc tcpCheckSumLSB
jmp :done
:msb add tcpCheckSumMSB, w ; add it to the checksum
sc ; was there a carry?
jmp :done
inc tcpCheckSumLSB ; yes, this time it is added to the LSB
snz
inc tcpCheckSumMSB
:done xor flags, #(1<<TCP_CHKSUM_LSB)
retp
; ******************************************************************************
TCPCheckSumAddHdr
; Add to the TCP checksum, the pseudo-header fields
; INPUT: {myIP0-3} = source IP addr
; {remoteIP0-3} = destination IP addr
; {tcpLengthMSB,tcpLengthLSB} = length of TCP header and data
; OUTPUT: {tcpCheckSumMSB,tcpCheckSumLSB}
; ******************************************************************************
bank TCP_BANK
; <TCP_length>
mov w, tcpLengthMSB
call TCPCheckSumAcc
mov w, tcpLengthLSB
call TCPCheckSumAcc
; <zero>,<protocol>
mov w, #0
call TCPCheckSumAcc
mov w, #6
call TCPCheckSumAcc
; <source_IP>
bank IP_BANK
mov w, myIP3
call TCPCheckSumAcc
bank IP_BANK
mov w, myIP2
call TCPCheckSumAcc
bank IP_BANK
mov w, myIP1
call TCPCheckSumAcc
bank IP_BANK
mov w, myIP0
call TCPCheckSumAcc
; <destination_IP>
bank IP_BANK
mov w, remoteIP3
call TCPCheckSumAcc
bank IP_BANK
mov w, remoteIP2
call TCPCheckSumAcc
bank IP_BANK
mov w, remoteIP1
call TCPCheckSumAcc
bank IP_BANK
mov w, remoteIP0
call TCPCheckSumAcc
retp
; ******************************************************************************
TCPTxByte
; Transmit a TCP byte accumulating the checksum each time
; INPUT: w = byte to send
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w
call @TCPCheckSumAcc
mov w, globTemp1
call NICWriteAgain_4
retp
; ******************************************************************************
TCPStartPktOut
; Constructs the TCP and IP headers
; INPUT: {remoteIP0-3} = destination IP addr for TCP pkt
; {tcpLengthMSB,tcpLengthLSB} = length of TCP data (just data)
; {tcpCheckSumMSB,tcpCheckSumLSB} = TCP checksum computed over just data
; {tcbSndUna4-1} = sequence number
; {tcbRcvNxt4-1} = acknowledgement number
; tcbFlags = code flags
; {tcbLocalPortMSB,tcbLocalPortLSB} = TCP Source Port
; {tcbRemotePortMSB,tcbRemotePortLSB} = TCP Destination Port
; OUTPUT:
; ******************************************************************************
; tcpLength += <TCP header length>
_bank TCP_BANK
mov w, #(TCP_HDR_LENGTH<<2) ; add in size of TCP hdr (20)
add tcpLengthLSB, w
snc
inc tcpLengthMSB ; tcpLength now is the length of TCP hdr and data
; IP <total_length> = tcpLength + <IP header length>
mov w, #20 ; add in size of IP hdr (20)
add w, tcpLengthLSB
bank IP_BANK
mov ipLengthLSB, w
bank TCP_BANK
mov w, tcpLengthMSB
bank IP_BANK
mov ipLengthMSB, w
snc
inc ipLengthMSB
; update IP <identifier>
inc ipIdentLSB
snz
inc ipIdentMSB
; set IP <protocol> for TCP
mov ipProtocol, #6
; compute IP <header_checksum>
call @IPGenCheckSum
; now we're ready to construct the IP header
call @IPStartPktOut
; then construct the TCP header
; TCP <source_port>,<destination_port>,<sequence_number>,
; <acknowledgement_number>,<hlen>,<code>,<window>
bank TCB_BANK
mov tcbOffset, #(TCP_HDR_LENGTH<<4)
mov tcbSendWinMSB, #((TCP_WINDOW_SIZE&$FF00)>>8)
mov tcbSendWinLSB, #(TCP_WINDOW_SIZE&$00FF)
mov globTemp3, #TCB_BANK ; send the TCB fields
:loop mov fsr, globTemp3
mov w, indf ; load the value
call @TCPTxByte ; transmit and accumulate header checksum
inc globTemp3
cse globTemp3, #TCB_END ; is the loop finished?
jmp :loop
; TCP <checksum>
call @TCPCheckSumAddHdr
bank TCP_BANK
mov w, /tcpCheckSumMSB
call NICWriteAgain_4
mov w, /tcpCheckSumLSB
call NICWriteAgain_4
; TCP <urgent_ptr>
mov w, #0
call NICWriteAgain_4
call NICWriteAgain_4
retp
; ******************************************************************************
_TCPRxHeader
; Process the TCP header of a received TCP packet
; INPUT: none
; OUTPUT: Z is set to 1 if the packet is invalid, 0 otherwise
; {tcbRemotePortMSB,tcbRemotePortLSB}
; {tcbSendWinMSB,tcbSendWinLSB}
; tcbOffset
; tcbFlags
; tcpRxFlags
; {tcpTmpSeq4-1} = <sequence_number>
; {tcpTmpAck4-1} = <acknowledgement_number>
; {tcpLengthMSB,tcpLengthLSB} = length of TCP data
; {ipLengthMSB,ipLengthLSB} = length of TCP data
; ******************************************************************************
; Check port and refuse packet if not for current connection
bank TCP_BANK
cjne tcpState, #TCP_ST_LISTEN, :checkSrc
; <source_port>
:readSrc ; read the source port
bank TCB_BANK
call NICReadAgain_4
mov tcbRemotePortMSB, w
call NICReadAgain_4
mov tcbRemotePortLSB, w
jmp :checkDest
:checkSrc ; check the source port is for the current connection
bank TCB_BANK
call NICReadAgain_4
xor w, tcbRemotePortMSB
sz ; is the high byte the same?
jmp :ignorePacket ; no, ignore the packet
call NICReadAgain_4 ; get the low byte
xor w, tcbRemotePortLSB
sz ; is the low byte the same?
jmp :ignorePacket ; no, ignore the packet
; <destination_port>
:checkDest ; check the destination port matches our port
call NICReadAgain_4
xor w, tcbLocalPortMSB
sz ; is the high byte the same?
jmp :ignorePacket ; no, ignore the packet
call NICReadAgain_4 ; get the low byte
xor w, tcbLocalPortLSB
sz ; Is the low byte the same?
jmp :ignorePacket ; no, ignore the packet
; <sequence_number>
bank TCP_BANK
call NICReadAgain_4
mov tcpTmpSeq4, w
call NICReadAgain_4
mov tcpTmpSeq3, w
call NICReadAgain_4
mov tcpTmpSeq2, w
call NICReadAgain_4
mov tcpTmpSeq1, w
; <acknowledgement_number>
call NICReadAgain_4
mov tcpTmpAck4, w
call NICReadAgain_4
mov tcpTmpAck3, w
call NICReadAgain_4
mov tcpTmpAck2,w
call NICReadAgain_4
mov tcpTmpAck1, w
; <hlen>
call NICReadAgain_4 ; receive the data offset. Used to skip the options
and w, #TCP_OFFSET_MASK ; mask out the offset
bank TCB_BANK
mov tcbOffset, w
clc
rr tcbOffset ; shift right to get the number of bytes
rr tcbOffset
; ipLength = tcpLength = length of TCP data
mov w, tcbOffset ; size of TCP header in bytes
bank IP_BANK
sub ipLengthLSB, w ; subtract out size of TCP header
mov w, ipLengthLSB
bank TCP_BANK
mov tcpLengthLSB, w
bank IP_BANK
sc
dec ipLengthMSB
mov w, ipLengthMSB
bank TCP_BANK
mov tcpLengthMSB, w
; <code>
call NICReadAgain_4 ; receive the flags
mov tcpRxFlags, w
bank TCB_BANK
mov tcbFlags, w ; take a copy of the flags
; <window>
call NICReadAgain_4
mov tcbSendWinMSB, w ; receive the window
call NICReadAgain_4
mov tcbSendWinLSB, w
; <checksum>,<urgent_ptr>,<options>
; skip over these
bank TCB_BANK
sub tcbOffset, #((TCP_HDR_LENGTH<<2)-4)
:loop call NICReadAgain_4
decsz tcbOffset
jmp :loop
clz
retp
:ignorePacket ; ignore the rest of the packet.
call NICDumpRxFrame_4
; ## Need to send a reset in response to this packet.
; ## Unfortunately sending a packet would overwrite the TCB.
; ## Need to create a second TCB?
stz
retp
; ******************************************************************************
_TCPUpdateSeq
; Add an 16-bit number to outgoing Sequence nr
; INPUT: {tcbSndUna1-4}{tcpUnAckMSB/LSB} = number to add
; OUTPUT: {tcbSndUna1-4}
; ******************************************************************************
bank TCP_BANK
mov w, tcpUnAckLSB
bank TCB_BANK
add tcbSndUna1, w
bank TCP_BANK
mov w, tcpUnAckMSB
snc
mov w, ++tcpUnAckMSB
bank TCB_BANK
add tcbSndUna2, w
sc
retp
incsz tcbSndUna3
retp
inc tcbSndUna4
retp
; ******************************************************************************
_TCPChkSeq
; This is called in the TCPProcPktIn to check if the received packet is the one
; expected or if it is a previous unacked packet.
; [TCP API Function]
; INPUT: none
; OUTPUT: z flag is set if the received packet is incorrect
; ******************************************************************************
call TCPCmpNxtSeq ; Check if received is expected
jz :equal ; z is set if RCV.NXT == SEG.SEQ
_bank TCP_BANK
test tcpUnAckLSB
sz
jmp :outstanding
test tcpUnAckMSB
snz
jmp :noOutstanding
:outstanding call TCPRestorePrev ; RCV.NXT = RCV.NXT - tcpUnAck
call TCPCmpNxtSeq ; Check if received is ack on previous packet
jnz :noOutstanding ; z is set if RCV.NXT == SEG.SEQ
setb z ; Return value = "OK"
retp
:equal _bank TCP_BANK
clr tcpUnAckLSB
clr tcpUnAckMSB ; Z is set (return value = "OK")
retp
:noOutstanding clrb LED_PIN ; DEBUG TODO
jmp $ ; DEBUG
; ******************************************************************************
_TCPRestorePrev
; Subtract an 16-bit number from RCV.NXT
; INPUT: {tcpUnAckLSB,tcpUnAckMSB} = number to add
; OUTPUT: {tcbRcvNxt1-4}
; ******************************************************************************
bank TCP_BANK
mov w, tcpUnAckLSB
bank TCB_BANK
sub tcbRcvNxt1, w
bank TCP_BANK
mov w, tcpUnAckMSB
jc :dontBorrow
mov w, ++tcpUnAckMSB ; if prev.sub was negative
test w ; carry on inc?
jz :exit ; yes (w = $100)
:dontBorrow
bank TCB_BANK
sub tcbRcvNxt2, w
snc ; c=0 => negative result
retp
:exit bank TCB_BANK
test tcbRcvNxt3
snz
dec tcbRcvNxt4
dec tcbRcvNxt3
retp
ORG $A00 ; Page5
E2Read8Ack jmp _E2Read8Ack
E2Read8NoAckStop jmp _E2Read8NoAckStop
E2RecvAck jmp _E2RecvAck
E2SendAck jmp _E2SendAck
E2SendNotAck jmp _E2SendNotAck
E2SendRdCmd jmp _E2SendRdCmd
E2SendWrCmd jmp _E2SendWrCmd
E2SetAddr jmp _E2SetAddr
Bin8ToBCD jmp _Bin8ToBCD
BCDToASCII jmp _BCDToASCII
; ******************************************************************************
TCPAppInit
; Called repeatedly as long as TCP connection state is closed
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank TCB_BANK
mov tcbLocalPortLSB, #HTTP_PORT_LSB
mov tcbLocalPortMSB, #HTTP_PORT_MSB
bank HTTP_BANK
clr httpParseState
clr httpURIHash
jmp @TCPAppPassiveOpen
; ******************************************************************************
TCPAppTxBytes
; Called before transmitting a TCP packet to see if the application has any
; data it wishes to send. The application cannot send more than TCP_SEG_SIZE
; bytes at one go.
; [TCP API Function]
; INPUT: none
; OUTPUT: {tcpUnAckMSB,tcpUnAckLSB} = number of bytes to transmit
; ******************************************************************************
bank HTTP_BANK
cje httpParseState, #2, :state2
cje httpParseState, #3, :state3
cje httpParseState, #4, :state4
retp
:state2 ; check how much there is to send
bank EEPROM_BANK
csae e2FileLenMSB, #(HTTP_SEG_SIZE>>8)
jmp :lastSegment ; msb#1 < msb#2
cse e2FileLenMSB, #(HTTP_SEG_SIZE>>8)
jmp :notLast ; msb#1 > msb#2
csa e2FileLenLSB, #(HTTP_SEG_SIZE&$00FF)
jmp :lastSegment ; #1 <= #2
:notLast ; not the last segment so send as much as possible (i.e. full segment)
sub e2FileLenLSB, #(HTTP_SEG_SIZE&$00FF) ; e2FileLen -= HTTP_SEG_SIZE
sc ;
dec e2FileLenMSB ;
sub e2FileLenMSB, #(HTTP_SEG_SIZE>>8) ;
bank TCP_BANK
mov tcpUnAckMSB, #(HTTP_SEG_SIZE>>8)
mov tcpUnAckLSB, #(HTTP_SEG_SIZE&$00FF)
retp
:lastSegment ; last segment so send whatever is leftover
mov w, e2FileLenMSB
bank TCP_BANK
mov tcpUnAckMSB, w
bank EEPROM_BANK
mov w, e2FileLenLSB
bank TCP_BANK
mov tcpUnAckLSB, w
bank HTTP_BANK
inc httpParseState ; next-state = 3
retp
:state3 ; no more to send so we close
call @TCPAppClose
retp
:state4 retp
; ******************************************************************************
TCPAppTxData
; This routine is called once for each byte the application has says it wishes
; to transmit.
; [TCP API Function]
; INPUT: none
; OUTPUT: w = data byte to transmit
; ******************************************************************************
bank HTTP_BANK
cje httpURIHash, #URI1, :specialFile ; resource.htm
cje httpURIHash, #URI2, :specialFile ; temperature.htm
call @E2Read8Ack
retp
:specialFile call @E2Read8Ack
mov globTemp1, w ; save temporarily
csb globTemp1, #$F0
jmp :match
mov w, globTemp1
retp
:match cjne globTemp1, #$F0, :subMatch ; 0xF0 is the magic number
:firstMatch bank HTTP_BANK
mov globTemp2, httpURIHash
mov fsr, #bcd3
cjne globTemp2, #URI1, :here
mov w, pageCount
inc pageCount
jmp :here1
:here _bank ADC_BANK
mov w, adc
:here1 call @Bin8ToBCD
mov w, bcd3+0
call @BCDToASCII
retp
:subMatch sub globTemp1, #$F0
_bank MISC_BANK
mov fsr, #bcd3
add fsr, globTemp1
mov w, indf
call @BCDToASCII
retp
; ******************************************************************************
TCPAppTxDone
; This is called following the last call to TCPAppTxData(). It signifies the
; transmitted data has successfully reached the remote host
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
retp
; ******************************************************************************
TCPAppRxBytes
; Indicator to the application that a packet has been received and that
; TCPAppRxByte is about to be called as many times as they are bytes of data
; [TCP API Function]
; INPUT: {tcpAppRxBytesMSB,tcpAppRxBytesLSB} = number of received data bytes
; OUTPUT: none
; ******************************************************************************
retp
; ******************************************************************************
TCPAppRxData
; Called once for each byte received in a packet.
; [TCP API Function]
; INPUT: w = received data byte
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w
bank HTTP_BANK
test httpParseState
jz :state0
cje httpParseState, #1, :state1
cjae httpParseState, #2, :state2
:state0 cse globTemp1, #' '
retp
inc httpParseState ; next-state = 1
retp
:state1 cje globTemp1, #' ', :gotURI
add httpURIHash, globTemp1
retp
:gotURI ; obtain pointer to file
mov w, httpURIHash
bank EEPROM_BANK
mov e2AddrLSB, w
clr e2AddrMSB
clc ; e2Addr = httpURIHash * 2
rl e2AddrLSB ;
rl e2AddrMSB ;
call @E2Init ; reset EEPROM
call @E2SetAddr
call @E2SendRdCmd
call @E2Read8Ack
mov e2AddrMSB, w
call @E2Read8NoAckStop
mov e2AddrLSB, w
; start reading from file
call @E2SetAddr
call @E2SendRdCmd
; file length
call @E2Read8Ack
mov e2FileLenMSB, w
call @E2Read8Ack
mov e2FileLenLSB, w
; file checksum (ignore)
call @E2Read8Ack
call @E2Read8Ack
inc httpParseState ; next-state = 2
retp
:state2 retp
; ******************************************************************************
TCPAppRxDone
; This is called following the last call to TCPAppRxData(). It signifies the
; end of the received packet
; [TCP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
retp
; ******************************************************************************
E2Delay600ns
; Delay 600ns
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #6
:loop decsz wreg
jmp :loop
retp
; ******************************************************************************
E2Delay900ns
; Delay 900ns
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #8
:loop decsz wreg
jmp :loop
retp
; ******************************************************************************
E2Delay1300ns
; Delay 1300ns
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov w, #13
:loop decsz wreg
jmp :loop
retp
; ******************************************************************************
E2SDAInput
E2SDAOutputHi
; Set SDA as input
; INPUT: none
; OUTPUT: none
; ******************************************************************************
mov !E2_PORT, #E2_DDR_SDA_IN
retp
; ******************************************************************************
E2SDAOutputLo
; Set SDA as output-low
; INPUT: none
; OUTPUT: none
; ******************************************************************************
clrb E2SDA_PIN
mov !E2_PORT, #E2_DDR_SDA_OUT
retp
; ******************************************************************************
E2GenStartCond
; Generate START condition
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2SDAOutputHi
setb E2SCL_PIN
call E2Delay600ns
call E2SDAOutputLo
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay600ns
retp
; ******************************************************************************
E2GenStopCond
; Generate STOP condition
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2SDAOutputLo
setb E2SCL_PIN
call E2Delay600ns
call E2SDAOutputHi
call E2Delay1300ns
retp
; ******************************************************************************
E2Write8
; Write 8 bits out the I2C bus
; INPUT: w = data to write
; OUTPUT: none
; ******************************************************************************
mov globTemp1, w ; data buffer
mov globTemp2, #8 ; bit counter
:loop call E2Delay900ns
sb globTemp1.7
call E2SDAOutputLo
snb globTemp1.7
call E2SDAOutputHi
call E2Delay900ns
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
rl globTemp1
decsz globTemp2
jmp :loop
retp
; ******************************************************************************
E2Read8
; Read 8 bits from the I2C bus
; INPUT: none
; OUTPUT: w = data read
; ******************************************************************************
call E2SDAInput
mov globTemp2, #8 ; bit counter
:loop call E2Delay900ns
sb E2SDA_PIN
clc
snb E2SDA_PIN
stc
rl globTemp1
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
decsz globTemp2
jmp :loop
mov w, globTemp1
retp
; ******************************************************************************
_E2Read8Ack
; Read 8 bits from the I2C bus and send ACK
; INPUT: none
; OUTPUT: w = data read
; ******************************************************************************
call E2Read8
mov globTemp1, w
call E2SendAck
mov w, globTemp1
retp
; ******************************************************************************
_E2Read8NoAckStop
; Read 8 bits from the I2C bus and send a no-ACK and stop-condition
; (terminates sequential read mode on EEPROM)
; INPUT: none
; OUTPUT: w = data read
; ******************************************************************************
call E2Read8
mov globTemp1, w
call E2SendNotAck
call E2GenStopCond
mov w, globTemp1
retp
; ******************************************************************************
_E2RecvAck
; Receive ACK bit from I2C receiver
; INPUT: none
; OUTPUT: z: 1 = received ACK, 0 = didn't receive ACK
; ******************************************************************************
call E2SDAInput
call E2Delay900ns
setb E2SCL_PIN
sb E2SDA_PIN
stz
snb E2SDA_PIN
clz
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
retp
; ******************************************************************************
_E2SendAck
; Send ACK bit as acknowledge
; INPUT: none
; ******************************************************************************
call E2SDAOutputLo
call E2Delay900ns
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
retp
; ******************************************************************************
_E2SendNotAck
; Send ACK bit as not-acknowledge
; INPUT: none
; ******************************************************************************
call E2SDAOutputHi
call E2Delay900ns
setb E2SCL_PIN
call E2Delay600ns
clrb E2SCL_PIN
call E2Delay900ns
retp
; ******************************************************************************
_E2SendRdCmd
; Tell I2C device we wish to read from it for this transaction
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2GenStartCond
mov w, #E2_CMD_RD
call E2Write8
call E2RecvAck
retp
; ******************************************************************************
_E2SendWrCmd
; Tell I2C Device we wish to write to it for this transaction
; INPUT: none
; OUTPUT: none
; ******************************************************************************
call E2GenStartCond
mov w, #E2_CMD_WR
call E2Write8
call E2RecvAck
retp
; ******************************************************************************
_E2SetAddr
; Set address pointer
; INPUT: {e2AddrMSB, e2AddrLSB} = address to set to
; OUTPUT: none
; ******************************************************************************
call E2SendWrCmd
_bank EEPROM_BANK
mov w, e2AddrMSB
call E2Write8
call E2RecvAck
mov w, e2AddrLSB
call E2Write8
call E2RecvAck
retp
; ******************************************************************************
_Bin8ToBCD
; Converts 8-bit binary number to unpacked BCD
; INPUT: w = binary number to convert
; fsr = pointer to MSD (lowest addr) of a 3-byte buffer
; OUTPUT: [fsr] = unpacked BCD
; ******************************************************************************
clr indf
inc fsr
clr indf
inc fsr ; LSD
mov indf, w
:loopHun mov w, #100
mov w, indf-w
jnc :loopTen
mov indf, w
dec fsr
dec fsr ; MSD
inc indf
inc fsr
inc fsr ; LSD
jmp :loopHun
:loopTen mov w, #10
mov w, indf-w
sc
jmp :exit
mov indf, w
dec fsr
inc indf
inc fsr
jmp :loopTen
:exit retp
; ******************************************************************************
_BCDToASCII
; Converts an unpacked BCD number to an ASCII character
; INPUT: w = unpacked BCD
; OUTPUT: w = ASCII character
; ******************************************************************************
mov globTemp1, w
mov w, #'0'
add w, globTemp1
retp
ORG $C00 ; Page6
IF DHCP
DHCPREQUESTSend jmp _DHCPREQUESTSend
ENDIF
; ******************************************************************************
NICReadAgain_6
; Shortform for calling NICReadAgain(), which is in Page1 (This is in Page6)
; ******************************************************************************
jmp @NICReadAgain
; ******************************************************************************
NICWriteAgain_6
; Shortform for calling NICWriteAgain(), which is in Page1 (This is in Page6)
; ******************************************************************************
jmp @NICWriteAgain
; ******************************************************************************
UDPStartPktOut
; Starts an outgoing UDP packet by constructing an IP and UDP packet header
; [UDP API Function]
; INPUT: {remoteIP0-3} = destination IP addr for UDP pkt
; {udpTxSrcPortMSB,udpTxSrcPortLSB} = UDP Source Port
; {udpTxDestPortMSB,udpTxDestPortLSB} = UDP Destination Port
; {udpTxDataLenMSB,udpTxDataLenLSB} = UDP Data Length (just data)
; OUTPUT: none
; ******************************************************************************
; compute IP <total_length>
_bank UDP_BANK
mov w, udpTxDataLenLSB
bank IP_BANK
mov ipLengthLSB, w
bank UDP_BANK
mov w, udpTxDataLenMSB
bank IP_BANK
mov ipLengthMSB, w
add ipLengthLSB, #(20+8) ; add in size of UDP hdr (8) and IP hdr (20)
snc
inc ipLengthMSB
; update IP <identifier>
inc ipIdentLSB
snz
inc ipIdentMSB
; set IP <protocol> for UDP
mov ipProtocol, #17
; compute IP <header_checksum>
call @IPGenCheckSum
; now we're ready to construct the IP header
call @IPStartPktOut
; then construct the UDP header
bank UDP_BANK
; UDP <source_port>
mov w, udpTxSrcPortMSB
call NICWriteAgain_6
mov w, udpTxSrcPortLSB
call NICWriteAgain_6
; UDP <destination_port>
mov w, udpTxDestPortMSB
call NICWriteAgain_6
mov w, udpTxDestPortLSB
call NICWriteAgain_6
; UDP <length>
mov w, #8
add w, udpTxDataLenLSB
mov w, udpTxDataLenMSB
snc
inc wreg
call NICWriteAgain_6
mov w, #8
add w, udpTxDataLenLSB
call NICWriteAgain_6
; UDP <checksum> = 0
mov w, #$0
call NICWriteAgain_6
call NICWriteAgain_6
retp
; ******************************************************************************
UDPEndPktOut
; Wraps up and transmits the UDP packet
; [UDP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank NIC_BANK
jmp @NICSendTxFrame
; ******************************************************************************
UDPProcPktIn
; Processes an Incoming UDP packet
; INPUT: nicCurrPktPtr = points to beginning of received packet
; OUTPUT: none
; ******************************************************************************
bank UDP_BANK
; UDP <source_port>
call NICReadAgain_6
mov udpRxSrcPortMSB, w
call NICReadAgain_6
mov udpRxSrcPortLSB, w
; UDP <destination_port>
call NICReadAgain_6
xor w, udpRxDestPortMSB
jnz :outtaHere
call NICReadAgain_6
xor w, udpRxDestPortLSB
jnz :outtaHere
; UDP <message_length>
call NICReadAgain_6
mov udpRxDataLenMSB, w
call NICReadAgain_6
mov udpRxDataLenLSB, w
; ignore UDP <checksum>
REPT 2
call NICReadAgain_6
ENDR
; UDP <data>
snb flags.RX_IS_IP_BCST
call UDPProcBcstPktIn
sb flags.RX_IS_IP_BCST
call @UDPAppProcPktIn
:outtaHere call @NICDumpRxFrame
retp
IF DHCP
; ******************************************************************************
UDPProcBcstPktIn
; The only kind of broadcast UDP packets accepted are DHCP messages: DHCPOFFER
; and DHCPACK
; INPUT: none
; OUTPUT: myIP0-3
; ******************************************************************************
clrb flags.GOT_DHCP_OFFER
clrb flags.GOT_IP_ADDR
call NICReadAgain_6
xor w, #2 ; check <op> = BOOTP reply
jnz :outtaHere
call NICReadAgain_6
xor w, #1 ; check <htype> = 1
jnz :outtaHere
call NICReadAgain_6
xor w, #6 ; check <hlen> = 6
jnz :outtaHere
; ignore <hops>
call NICReadAgain_6
; check <transaction_id> = 0xABABABAB
REPT 4
call NICReadAgain_6
xor w, #$AB
jnz :outtaHere
ENDR
; ignore <seconds>, <flags>, <client_IP>
mov globTemp1, #(2+2+4)
:loop1 call NICReadAgain_6
decsz globTemp1
jmp :loop1
; record <your_IP>
bank IP_BANK
call NICReadAgain_6
mov myIP3, w
call NICReadAgain_6
mov myIP2, w
call NICReadAgain_6
mov myIP1, w
call NICReadAgain_6
mov myIP0, w
; check if it is non-zero
mov w, myIP3
or w, myIP2
or w, myIP1
or w, myIP0
jz :outtaHere
; skip <server_IP>, <router_IP>, <client_hw_addr>,
; <sever_host_name>, <boot_filename>, <option_magic_cookie>
mov globTemp1, #(4+4+16+64+128+4)
:loop2 call @NICPseudoRead
decsz globTemp1
jmp :loop2
; <option-message_type>
call NICReadAgain_6
xor w, #53 ; DHCP Message Type
jnz :outtaHere
call NICReadAgain_6
xor w, #1
jnz :outtaHere
call NICReadAgain_6
xor w, #2 ; DHCPOFFER
snz
setb flags.GOT_DHCP_OFFER
xor w, #2
xor w, #5 ; DHCPACK
snz
setb flags.GOT_IP_ADDR
; now search for that dang(!) <option-server_id>
:loop4 call NICReadAgain_6
xor w, #54 ; Server Identifier
jz :foundServId
call NICReadAgain_6 ; length
mov globTemp1, w
:loop3 call @NICPseudoRead
decsz globTemp1
jmp :loop3
jmp :loop4
:foundServId call NICReadAgain_6 ; ignore length
bank DHCP_BANK
call NICReadAgain_6
mov dhcpServerId3, w
call NICReadAgain_6
mov dhcpServerId2, w
call NICReadAgain_6
mov dhcpServerId1, w
call NICReadAgain_6
mov dhcpServerId0, w
:outtaHere retp
; ******************************************************************************
DHCPSendCommon1
; Helper function for DHCPDISCOVERSend and DHCPREQUESTSend
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; set ethernet addr to broadcast addr
bank NIC_BANK
mov w, #$FF
mov nicRemoteEth0, w
mov nicRemoteEth1, w
mov nicRemoteEth2, w
mov nicRemoteEth3, w
mov nicRemoteEth4, w
mov nicRemoteEth5, w
; set IP addr to broadcast addr
bank IP_BANK
mov w, #$FF
mov remoteIP3, w
mov remoteIP2, w
mov remoteIP1, w
mov remoteIP0, w
; tell ARP not to send out an ARP REQUEST for this pkt
setb arpFlags.ARP_BYPASS
bank UDP_BANK
clr udpTxSrcPortMSB ; DHCP client
mov udpTxSrcPortLSB, #68 ;
clr udpTxDestPortMSB ; DHCP server
mov udpTxDestPortLSB, #67 ;
call @UDPStartPktOut
; <op>
mov w, #1
call NICWriteAgain_6
; <htype>
mov w, #1
call NICWriteAgain_6
; <hlen>
mov w, #6
call NICWriteAgain_6
; <hops>
mov w, #0
call NICWriteAgain_6
; <transaction_id> = 0xABABABAB
mov w, #$AB
REPT 4
call NICWriteAgain_6
ENDR
; <seconds> = 256
mov w, #1
REPT 2
call NICWriteAgain_6
ENDR
; <flags>
mov w, #$80
call NICWriteAgain_6
mov w, #0
call NICWriteAgain_6
retp
; ******************************************************************************
DHCPSendCommon2
; Helper function for DHCPDISCOVERSend and DHCPREQUESTSend
; INPUT: none
; OUTPUT: none
; ******************************************************************************
; <client_hw_addr>
mov w, #SX_ETH_ADDR0
call NICWriteAgain_6
mov w, #SX_ETH_ADDR1
call NICWriteAgain_6
mov w, #SX_ETH_ADDR2
call NICWriteAgain_6
mov w, #SX_ETH_ADDR3
call NICWriteAgain_6
mov w, #SX_ETH_ADDR4
call NICWriteAgain_6
mov w, #SX_ETH_ADDR5
call NICWriteAgain_6
; <client_hw_addr>,<server_host_name>,<boot_filename>
mov globTemp1, #(10+64+128)
mov w, #0
:loop2 call NICWriteAgain_6
decsz globTemp1
jmp :loop2
; <option_magic_cookie>
mov w, #99
call NICWriteAgain_6
mov w, #130
call NICWriteAgain_6
mov w, #83
call NICWriteAgain_6
mov w, #99
call NICWriteAgain_6
retp
; ******************************************************************************
DHCPDISCOVERSend
; Send DHCPDISCOVER message
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank UDP_BANK
clr udpTxDataLenMSB
mov udpTxDataLenLSB, #(240+3+0+1) ; without requested-IP option
;mov udpTxDataLenLSB, #(240+3+6+1) ; with requested-IP option
call DHCPSendCommon1
; <client_IP>, <your_IP>,<server_IP>,<router_IP> = 0
mov globTemp1, #(4+4+4+4)
mov w, #0
:loop1 call NICWriteAgain_6
decsz globTemp1
jmp :loop1
call DHCPSendCommon2
; <option-message_type>
mov w, #53
call NICWriteAgain_6
mov w, #1
call NICWriteAgain_6
mov w, #1 ; DHCPDISCOVER
call NICWriteAgain_6
; <option-requested_IP> -- optional
;mov w, #50
;call NICWriteAgain_6
;mov w, #4
;call NICWriteAgain_6
;mov w, #SX_IP_ADDR3
;call NICWriteAgain_6
;mov w, #SX_IP_ADDR2
;call NICWriteAgain_6
;mov w, #SX_IP_ADDR1
;call NICWriteAgain_6
;mov w, #SX_IP_ADDR0
;call NICWriteAgain_6
; <option-end_option> -- not optional
mov w, #255
call NICWriteAgain_6
; and ... that should do it!
call @UDPEndPktOut
clrb arpFlags.ARP_BYPASS
retp
; ******************************************************************************
_DHCPREQUESTSend
; Send DHCPREQUEST message
; INPUT: none
; OUTPUT: none
; ******************************************************************************
bank UDP_BANK
mov udpTxDataLenMSB, #((240+3+6+6+1)>>8)
mov udpTxDataLenLSB, #((240+3+6+6+1)&$FF)
call DHCPSendCommon1
; <client_IP>
bank IP_BANK
mov w, myIP3
call NICWriteAgain_6
mov w, myIP2
call NICWriteAgain_6
mov w, myIP1
call NICWriteAgain_6
mov w, myIP0
call NICWriteAgain_6
; <your_IP>,<server_IP>,<router_IP> = 0
mov globTemp1, #(4+4+4)
mov w, #0
:loop1 call NICWriteAgain_6
decsz globTemp1
jmp :loop1
call DHCPSendCommon2
; <option-message_type>
mov w, #53
call NICWriteAgain_6
mov w, #1
call NICWriteAgain_6
mov w, #3 ; DHCPREQUEST
call NICWriteAgain_6
; <option-server_id>
mov w, #54 ; option server identifier
call NICWriteAgain_6
mov w, #4 ; length
call NICWriteAgain_6
bank DHCP_BANK
mov w, dhcpServerId3
call NICWriteAgain_6
mov w, dhcpServerId2
call NICWriteAgain_6
mov w, dhcpServerId1
call NICWriteAgain_6
mov w, dhcpServerId0
call NICWriteAgain_6
; <option-requested_IP> -- not optional
mov w, #50
call NICWriteAgain_6
mov w, #4
call NICWriteAgain_6
bank IP_BANK
mov w, myIP3
call NICWriteAgain_6
mov w, myIP2
call NICWriteAgain_6
mov w, myIP1
call NICWriteAgain_6
mov w, myIP0
call NICWriteAgain_6
; <option-end_option> -- not optional
mov w, #255
call NICWriteAgain_6
; and ... that should do it!
call @UDPEndPktOut
clrb arpFlags.ARP_BYPASS
retp
ELSE ; DHCP
; ******************************************************************************
UDPProcBcstPktIn
; The only kind of broadcast UDP packets accepted are DHCP messages: DHCPOFFER
; and DHCPACK
; INPUT: none
; OUTPUT: none
; ******************************************************************************
retp ; cus DHCP not enabled
ENDIF ; DHCP
ORG $E00 ; Page7 (Holly Cow! Still not done??)
; ******************************************************************************
UDPAppInit
; Application UDP Initialization code (Example)
; This function is called automatically once by the stack during startup
; [UDP API Function]
; INPUT: none
; OUTPUT: none
; ******************************************************************************
_bank UDP_BANK
mov udpRxDestPortMSB, #UDP_RX_DEST_MSB
mov udpRxDestPortLSB, #UDP_RX_DEST_LSB
retp
; ******************************************************************************
UDPAppProcPktIn
; Application Incoming UDP packet handler (Example)
; This function is called whenever an application (matches udpRxDestPortxSB)
; packet is received. The appplication can call NICReadAgain() to extract
; sequentially extract each byte of the <data> field in the UDP packet.
; [UDP API Function]
; INPUT: {udpRxDataLenMSB,udpRxDataLenLSB} = number of bytes in UDP <data>
; {udpRxSrcPortMSB,udpRxSrcPortLSB} = UDP <source_port>
; OUTPUT: none
; ******************************************************************************
call @NICReadAgain
IF CREDENCE
xor re, w ; toggle I/O pins
_bank MISC_BANK
mov ledPort, re
ELSE
and w, #%01000000
xor ra, w ; toggle I/O pins
_bank MISC_BANK
mov ledPort, ra
ENDIF
_bank UDP_BANK
clr udpTxSrcPortMSB
clr udpTxSrcPortLSB
mov udpTxDestPortMSB, udpRxSrcPortMSB
mov udpTxDestPortLSB, udpRxSrcPortLSB
mov udpTxDataLenMSB, #0
mov udpTxDataLenLSB, #2
call @UDPStartPktOut
IF CREDENCE
mov w, re ; send new port state
ELSE
mov w, ra ; send new port state
ENDIF
call @NICWriteAgain
mov w, #$00 ; one-byte padding
call @NICWriteAgain
call @UDPEndPktOut
retp
; ***********
; *** END ***
; ***********
END
file: /Techref/scenix/lib/io/osi3/tcpip/isx_1_6_8.src, 128KB, , updated: 2002/2/21 11:32, local time: 2024/11/19 10:45,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/Techref/scenix/lib/io/osi3/tcpip/isx_1_6_8.src"> scenix lib io osi3 tcpip isx_1_6_8</A> |
Did you find what you needed?
|