; ***************************************************************************************** ; Copyright © 1999 Scenix Semiconductor, Inc. All rights reserved. ; ; Scenix Semiconductor, Inc. assumes no responsibility or liability for ; the use of this software. ; Scenix Semiconductor conveys no license, implicitly or otherwise, under ; any intellectual property rights. ; Information contained in this publication regarding TCP/IP stack, ; and the like is intended through suggestion only and may ; be superseded by updates. Scenix Semiconductor makes no representation ; or warranties with respect to the accuracy or use of these information, ; or infringement of patents arising from such use or otherwise. ; Patent Pending. ;***************************************************************************************** ; ; Filename: TCP.src ; ; Authors: ; Chris Waters (SMTP, HTTP, TCP/IP, PPP, UART) ; Abraham Si (POP3) ; ; Revision: 1.0.4 ; ; Part: SX52BD ; Freq: 50Mhz ; Compiled using Parallax SX-Key software v1.07 and SASM 1.40 ; To compile this software using SXKEY search for the word 'SXKEY_CHANGE' and make the commented ; changes. ; ; Date Written: Oct. 1999 ; ; Last Revised: ; 8 Nov 1999 - Added bank instruction to AppBytesToSend for HTTP. (CJW). ; 19 Nov 1999 - Integrated UDP demo code. Bug fixes. (CJW). ; 20 Jan 2000 - Changed device directive for 2.0 silicon. (CJW). ; 11 may 2000 - Updated to assemble correctly with SASM (version 1.44.6). Also verified with ; Parallax's software Rev. 1.19 ; ; Program Description: ; HTTP server ; SMTP client, send email when threshold crossed if ADCDEMO is enabled; ; else send email when connected and when PING'ed ; POP3 client, can retrieve up to 255 messages. Attachments are okay. ; received emails are not buffered, but just sent out immediately to ; the debug port. ; ; Revision History: 1.00 initial public release ; ;***************************************************************************************** ;***************************************************************************************** ; Target SX ; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD/ES, ; SX48BD, SX52BD/ES or SX52BD. For SX48BD/ES and SX52BD/ES, uncomment both defines, ; SX48_52 and SX48_52_ES. ;***************************************************************************************** ;SX18_20 ;SX28 SX48_52 ;SX48_52_ES ;***************************************************************************************** ; Assembler Used ; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler ; enabled by default. ;***************************************************************************************** ;SX_Key ;********************************************************************************* ; Assembler directives: ; high speed external osc, turbo mode, 8-level stack, and extended option reg. ; ; SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default. ; SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default. ; ;********************************************************************************* IFDEF SX_Key ;SX-Key Directives IFDEF SX18_20 ;SX18AC or SX20AC device directives for SX-Key device SX18L,oschs2,turbo,stackx_optionx ENDIF IFDEF SX28 ;SX28AC device directives for SX-Key device SX28L,oschs2,turbo,stackx_optionx ENDIF IFDEF SX48_52_ES ;SX48BD/ES or SX52BD/ES device directives for SX-Key device oschs,turbo,stackx,optionx ELSE IFDEF SX48_52 ;SX48/52/BD device directives for SX-Key device oschs2 ENDIF ENDIF freq 50_000_000 ELSE ;SASM Directives IFDEF SX18_20 ;SX18AC or SX20AC device directives for SASM device SX18,oschs2,turbo,stackx,optionx ENDIF IFDEF SX28 ;SX28AC device directives for SASM device SX28,oschs2,turbo,stackx,optionx ENDIF IFDEF SX48_52_ES ;SX48BD/ES or SX52BD/ES device directives for SASM device SX52,oschs,turbo,stackx,optionx ELSE IFDEF SX48_52 ;SX48BD or SX52BD device directives for SASM device SX52,oschs2 ENDIF ENDIF irc_cal IRC_SLOW freq 50_000_000 ENDIF ; set POP3DEMO & DEBUG =1 since the email will be pumped out from the debug port ;=============================================================================== ; Options ; ; Use these defines to enable or disable different features in the code. ;=============================================================================== ;DEBUG ; Set to enable the debugging information. WIN32 ; Set if the host is a Windows computer. WIN98 ; Set if the host is Windows 98. UDP ; Enable UDP. TCP ; Enable TCP. ;UDPDEMO ; Enable the UDP demonstration code. (Requires UDP.) HTTPDEMO ; Enable the web server. (Requires TCP.) JAVADEMO ; Enable the Java sprinkler demo. (Requires UDP.) ;SMTPDEMO ; Enable the SMTP client. (Requires TCP.) ;POP3DEMO ; Enable the POP3 client (Requires TCP.) ;POP3DEBUG ; Send POP3 state information to the debug port. ;ADCDEMO ; Enable the A/D for the SMTP client demo. IFDEF POP3DEMO id 'POP3 ' ENDIF IFDEF SMTPDEMO id 'SMTP ' ENDIF IFDEF HTTPDEMO id 'HTTP ' ENDIF reset ResetVector ;***************************************************************************************** ; Macros ;***************************************************************************************** ;********************************************************************************* ; Macro: _bank ; Sets the bank appropriately for all revisions of SX. ; ; This is required since the bank instruction has only a 3-bit operand, it cannot ; be used to access all 16 banks of the SX48/52. For this reason FSR.4 (for SX48/52BD/ES) ; or FSR.7 (SX48/52bd production release) needs to be set appropriately, depending ; on the bank address being accessed. This macro fixes this. ; ; So, instead of using the bank instruction to switch between banks, use _bank instead. ; ;********************************************************************************* _bank macro 1 NOEXPAND bank \1 IFDEF SX48_52 IFDEF SX48_52_ES IF \1 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE 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 ENDIF ENDIF endm ;********************************************************************************* ; Macro: _mode ; Sets the MODE register appropriately for all revisions of SX. ; ; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand. ; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have ; the added ability of reading or writing some of the MODE registers, and therefore use ; 5-bits of the MODE register. The MOV M,W instruction modifies all 8-bits of the ; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE ; register is written with the correct value. This macro fixes this. ; ; So, instead of using the MODE or MOV M,# instructions to load the M register, use ; _mode instead. ; ;********************************************************************************* _mode macro 1 IFDEF SX48_52 mov w,#\1 ;loads the M register correctly for the SX48BD and SX52BD mov m,w ELSE mov m,#\1 ;loads the M register correctly for the SX18AC, SX20AC ;and SX28AC ENDIF endm top = 0 indent = 1 IFDEF DEBUG ; uncomment only if you have the debug monitor utility DEBUGP MACRO 2 ; mov DebugScratch1,w ; IF \2 = 1 ; mov w,#(\1|$80) ; ELSE ; mov w,#\1 ; ENDIF ; call @DebugSendByte ; mov w,DebugScratch1 ENDM DEBUGW MACRO 2 ; mov DebugScratch1,w ; IF \2 = 1 ; mov w,#(\1|$80) ; ELSE ; mov w,#\1 ; ENDIF ; call @DebugSendByte ; mov w,DebugScratch1 ; call @DebugSendByte ; mov w,DebugScratch1 ENDM ELSE DEBUGP MACRO 2 ENDM DEBUGW MACRO 2 ENDM ENDIF POP3W MACRO mov DebugScratch1,w ; save W register call @DebugSendByte mov w,DebugScratch1 ; restore W _bank POP3Vars ENDM IFDEF POP3DEBUG POP3DW MACRO mov DebugScratch1,w ; save W register call @DebugSendByte mov w,DebugScratch1 ; restore W _bank POP3Vars ENDM ELSE POP3DW MACRO ENDM ENDIF ;----------------------------------------------------------------------------- ; UART ring macro ; Advance the pointer through the ring, wrapping around if necessary ; This could be more efficient for aligned and power of 2 ring sizes. ;----------------------------------------------------------------------------- ringadv MACRO 3 ; Arguments ptr,base,size inc \1 ; Increment the pointer ; Check for wrap around mov w,\1 ; Load the pointer xor w,#(\2+\3) ; Check if ptr = base+size mov w,#\2 snb status.2 mov \1,w ; Equal, set ptr to base ENDM ;=============================================================================== ; Constants ;=============================================================================== ;------------------------------------------------------------------------------- ; Physical layer constants ;------------------------------------------------------------------------------- ; Ring buffer sizes rx_ring_size equ 7 ; Size (in bytes) of the rx ring buffer. tx_ring_size equ 7 ; Size of the tx ring buffer. ; N.B. In windows 95 the FIFO support must be turned off in the system control panel. ; PPP UART = 57600 baud baud_bit = 2 start_delay = 4+2+1 int_period = 217 ; Port Assignment: Bit variables rx_pin EQU rd.7 ; UART receive input tx_pin EQU rd.6 ; UART transmit output rts_pin EQU rd.5 ; UART RTS cts_pin EQU rd.1 ; UART CTS ;------------------------------------------------------------------------------- ; PPP constants ;------------------------------------------------------------------------------- ; PPP packet data format PPPFlag = $7E PPPEscape = $7D PPPXor = $20 PPPAddress = $FF PPPControl = $03 PPPLCPPrefix = $C0 PPPLCP = $21 PPPIPCPPrefix = $80 PPPIPCP = $21 PPPIPPrefix = $00 PPPIP = $21 ; PPP LCP codes PPPConfigureRequest= 1 PPPConfigureAck = 2 PPPConfigureNak = 3 PPPConfigureReject= 4 PPPTerminateRequest= 5 PPPTerminateAck = 6 PPPCodeReject = 7 PPPProtocolReject= 8 PPPEchoRequest = 9 PPPEchoReply = 10 PPPDiscardRequest= 11 ; PPP state machine states. PPPStateInitial = 0 PPPStateStarting= 1 PPPStateClosed = 2 PPPStateStopped = 3 PPPStateClosing = 4 PPPStateStopping= 5 PPPStateReqSent = 6 PPPStateAckRcvd = 7 PPPStateAckSent = 8 PPPStateOpened = 9 ; PPP receive packet state machine. PPPStateFlag = 0 PPPStateAddress = 1 PPPStateControl = 2 PPPStateProto1 = 3 PPPStateProto2 = 4 PPPStateLCPCode = 5 PPPStateLCPID = 6 PPPStateLCPLen1 = 7 PPPStateLCPLen2 = 8 PPPStateData = 9 PPPStateFCS1 = 10 PPPStateFCS2 = 11 PPPStateUnknownCode= 12 ; PPP events. PPP_NONE = 0 PPP_RCA = 2 PPP_RCN = 3 PPP_RTR = 4 PPP_RTA = 5 PPP_RUC = 6 PPP_RXJ_GOOD = 7 PPP_RXJ_BAD = 8 PPP_RXR = 9 PPP_RCR_GOOD = 10 PPP_RCR_BAD = 11 PPP_TO_GOOD = 12 PPP_TO_BAD = 13 PPP_DATA = 14 ; PPP IPCP options PPPAddressOption = 3 ; Frame Check Sequence PPPValidFCSh = $F0 PPPValidFCSl = $B8 PPPRestartTimeout = 3 ; Number of seconds before restart timer expires PPPRestartExpire = 8 ; 1/(PPPRestartTimeout * 1/(ClockRate) * 256 * 256 * int_period) PPPRestartCountDefault = 6 ; Number of times to send configure-req before giving up. ;------------------------------------------------------------------------------- ; IP constants ;------------------------------------------------------------------------------- IPVersion = 4 ; IP version number. IPIHL = 5 ; Header length in number of 32 bit words. IPVIHL = (IPVersion<<4) | IPIHL IPTOS = 0 ; Type of service. Equals zero for routine service. IPFlagsField = 0 ; May fragment, last fragment. IPFrag1 = (IPFlagsField<<5) IPFrag2 = 0 ; No fragments so fragment offset equals zero. IPTTL = 15 ; Time to live (number of hops packet allowed to travel). IPProtocolICMP = 1 ; ICMP protocol. IPProtocolUDP = 17 ; UDP protcol. IPProtocolTCP = 6 ; TCP protocol. ICMPEchoRequest = 8 ; ICMP echo request packet. ICMPEchoReply = 0 ; ICMP echo reply packet. ICMPEchoHL = 8 ; Length of an ICMP echo header. IPAddress1 = 192 ; IP address of the SX. IPAddress2 = 168 IPAddress3 = 11 IPAddress4 = 1 UDPHLength = 8 ; Length of the UDP header. ;------------------------------------------------------------------------------- ; TCP constants ;------------------------------------------------------------------------------- ; TCP state machine states. TCPStateClosed = 0 TCPStateListen = 1 TCPStateSynSent = 2 TCPStateSynReceived = 3 TCPStateEstablished = 4 TCPStateFinWait1 = 5 TCPStateFinWait2 = 6 TCPStateCloseWait = 7 TCPStateClosing = 8 TCPStateLastAck = 9 TCPStateTimeWait = 10 ; Bit positions in the flag byte. TCPFlagURG = 5 TCPFlagACK = 4 TCPFlagPSH = 3 TCPFlagRST = 2 TCPFlagSYN = 1 TCPFlagFIN = 0 ; TCP Options TCPOptionEnd = 0 TCPOptionNOP = 1 TCPOptionMSS = 2 ; Maximum segment size option. TCPHeaderLength = 5 ; Normal TCP header length. TCPOffsetMask = $F0 ; This is the maximum number of data bytes we will accept at once. TCPWindow = 254 TCPRestartExpire = 16 CharCR = $0d ; Carriage return. CharLF = $0a ; Linefeed. ;------------------------------------------------------------------------------- ; Java Demo constants ;------------------------------------------------------------------------------- IFDEF JAVADEMO sprinklerPort = 7024 sprinklerZones = 3 sprinklerOn = 1 sprinklerOff = 0 sprinkler15 = 0 sprinkler30 = 1 sprinkler45 = 2 commandGet = 10 commandSet = 11 ENDIF ;------------------------------------------------------------------------------- ; HTTP constants ;------------------------------------------------------------------------------- IFDEF HTTPDEMO ; Port number for HTTP server. HTTPPorth = 0 HTTPPortl = 80 ; States for parsing HTTP headers. HTTPParseMethod = 0 HTTPParseURI = 1 HTTPParseVersion = 2 HTTPParseHeader = 3 HTTPParseFinished = 4 HTTPParse1 = 5 HTTPParse2 = 6 HTTPParse3 = 7 HTTPParse4 = 8 HTTPMethodNone = 0 HTTPMethodGET = 'G' HTTPMethodPOST = 'P' HTTPMethodHEAD = 'H' HTTP404Hash = $AA ; Hash of the URL /404.html HTTPVarStart = '|' ENDIF ;------------------------------------------------------------------------------- ; SMTP constants ;------------------------------------------------------------------------------- IFDEF SMTPDEMO SMTPPorth = 0 SMTPPortl = 25 ; The state reflects the last message received from the SMTP server. SMTPStateClosed = 0 SMTPStateHello = 1 SMTPStateHelloAck = 2 SMTPStateMail = 3 SMTPStateMailAck = 4 SMTPStateRcpt = 5 SMTPStateRcptAck = 6 SMTPStateData = 7 SMTPStateDataAck = 8 SMTPStateMesg = 9 SMTPStateMesgAck = 10 SMTPStateQuit = 11 SMTPStateQuitAck = 12 SMTPStateFinished = 13 ; IP address of the SMTP server. SMTPAddress1 = 192 SMTPAddress2 = 168 SMTPAddress3 = 11 SMTPAddress4 = 2 ENDIF ;----------------------------------------------;------------------------------------------------------------------------------- ; POP3 constants ;------------------------------------------------------------------------------- IFDEF POP3DEMO POP3Porth = 0 POP3Portl = 110 ; The state reflects the last message received from the POP3 server. POP3StateClosed = 0 POP3StateUser = 1 POP3StateUserAck = 2 POP3StatePass = 3 POP3StatePassAck = 4 POP3StateStat = 5 POP3StateStatAck = 6 POP3StateRetr = 7 POP3StateRetrAck = 8 POP3StateMsg = 9 POP3StateDele = 10 POP3StateDeleAck = 11 POP3StateQuit = 12 POP3StateQuitAck = 13 POP3StateFinished = 14 POP3MsgSubStStart =0 ;start of Msg sub state POP3MsgSubSt1CR =1 ; 1st CR POP3MsgSubSt1LF =2 ; 1st LF detected POP3MsgSubStDot =3 ; <CR><LF>. detected POP3MsgSubSt2CR =4 ; <CR><LF>.<CR> POP3MsgSubStDotDot =6 ; byte-stuffed termination octet, strip it off ; IP address of the POP3 server. POP3Address1 = 192 POP3Address2 = 168 POP3Address3 = 11 POP3Address4 = 2 ENDIF ;------------------------------------------------------------------------------- ; UDP Demo constants ;------------------------------------------------------------------------------- IFDEF UDPDEMO DemoPort = 280 ; Port number for SX demo. DemoMemDump = $10 ; Command to dump memory. DemoMemSet = $20 ; Command to set a memory location. DemoMemGet = $30 ; Command to get a memory location. ENDIF ;------------------------------------------------------------------------------- ; Demo board constants ;------------------------------------------------------------------------------- ;StatusPort = re ; Port for the status LEDs. StatusPort EQU re ; Port for the status LEDs. LED1 = 7 LED0 = 6 LEDUP = 5 ; Link Up LED. LEDRx = 4 ; Traffic LED. LEDTx = 3 ; Traffic LED. LEDErr = 2 ; Error LED. ;=============================================================================== ; Variables ;=============================================================================== org $0A ;------------------------------------------------------------------------------- ; Global variables ;------------------------------------------------------------------------------- Scratch0 equ 0ah ; Scratch0 is preserved across SOME function calls. Scratch1 equ 0bh ; Scratch1 is never preserved. Scratch2 equ 0ch ; Scratch2 IFDEF DEBUG DebugScratch0 equ 0dh DebugScratch1 equ 0eh ENDIF ;------------------------------------------------------------------------------- ; PPP variables ;------------------------------------------------------------------------------- org $10 PPPVars = $ PPPFlags ds 1 ; Flags for the PPP state machine. inLCP = 0 ; 1 if the state machine is in LCP negotiation. inIPCP = 1 ; 1 if the state machine is in IPCP negotiation. inIP = 2 ; 1 if the IP layer is running. timerRunning = 3 ; 1 if the restart timer is running. linkUp = 4 ; 1 if the PPP link is up. addressOption = 5 ; 1 if we are handling a pesky address option. PPPState ds 1 ; The state of the PPP state machine. PPPRxState ds 1 ; State of the receive state machine. PPPEvent ds 1 ; The last PPP event. PPPDelayEvent ds 1 ; Temporarily hold a delayed event. PPPProto1 ds 1 ; The first byte of the received protocol PPPProto2 ds 1 ; The second byte of the received protocol PPPIdentifier ds 1 ; The received identifier. PPPLengthh ds 1 ; The high byte of the received length. PPPLengthl ds 1 ; The low byte of the received length. PPPTxFCSh ds 1 ; High byte of the Tx frame check sequence. PPPTxFCSl ds 1 ; Low byte of the Tx frame check sequence. PPPRxFCSh ds 1 ; High byte of the Rx frame check sequence. PPPRxFCSl ds 1 ; Low byte of the Rx frame check sequence. PPPFCSA ds 1 ; Temporary variable shared by both FCS routines. PPPRestartCount ds 1 ; Restart count for PPP state machine. org $20 IPVars = $ IPFlags ds 1 ; Flags used for IP Receive. echoPacket = 0 ; Last packet was ICMP echo. UDPPacket = 1 ; Current packet is UDP. TCPPacket = 2 ; Current packet is TCP. ICMPPacket = 3 ; ICMP packet. Will be refined to echoPacket when known. anyPacket = 4 ; Any packet was received. checksumBit = 7 ; Used while computing the checksum. IPTxVars = $ IPProtocol ds 1 ; The protocol contained in the packet. IPDestAddress1 ds 1 ; IP destination address. IPDestAddress2 ds 1 IPDestAddress3 ds 1 IPDestAddress4 ds 1 IPSrcAddress1 = IPDestAddress1 ; IP source address. IPSrcAddress2 = IPDestAddress2 IPSrcAddress3 = IPDestAddress3 IPSrcAddress4 = IPDestAddress4 IPLengthMSB ds 1 ; MSB of length of the packet in bytes. IPLengthLSB ds 1 ; LSB of length of the packet in bytes. IPChecksumMSB ds 1 ; High byte of the IP checksum. IPChecksumLSB ds 1 ; Low byte of the IP checksum. IPIDCounter ds 1 ; Counter for the identification field. IPRxLengthMSB ds 1 ; Length of the packet in bytes. IPRxLengthLSB ds 1 ; Length of the packet in bytes. IFDEF TCP TCPChecksumMSB ds 1 ; TCP Checksum. Must be in the IPVars bank. TCPChecksumLSB ds 1 ; TCP Checksum. ENDIF ;------------------------------------------------------------------------------- ; UDP variables ;------------------------------------------------------------------------------- org $30 UDPVars = $ UDPSrcPortl ds 1 ; The source port. UDPSrcPorth ds 1 UDPDestPortl ds 1 ; The destination port. UDPDestPorth ds 1 UDPLengthMSB ds 1 ; Temporary storage for the length. UDPLengthLSB ds 1 UDPRxLength ds 1 ; Length of a received UDP packet. org $40 PPPTimer = $ PPPTimer1 ds 1 ; Restart timer for the PPP state machine. PPPTimer2 ds 1 ; Restart timer for the PPP state machine. PPPTimer3 ds 1 ; Restart timer for the PPP state machine. ;------------------------------------------------------------------------------- ; TCP variables ;------------------------------------------------------------------------------- IFDEF TCP TCPVars = $ TCPState ds 1 ; State machine state. TCPInfo ds 1 TCPTMP_SEQ4 ds 1 ; TMP.SEQ. 1=LSB, 4=MSB. TCPTMP_SEQ3 ds 1 ; Temporary information from the received packet. TCPTMP_SEQ2 ds 1 TCPTMP_SEQ1 ds 1 TCPTMP_ACK4 ds 1 ; TMP.ACK. TCPTMP_ACK3 ds 1 ; Temporary information from the received packet. TCPTMP_ACK2 ds 1 TCPTMP_ACK1 ds 1 TCPOutstanding ds 1 ; The number of unacknowledged bytes. TCPRxFlags ds 1 ; Copy of the received flags field. org $50 ; The ordering of these variables is significant. It is the same as the TCP ; header. This simplifies the send-packet code. TCB = $ TCPLocalPorth ds 1 ; The source port. TCPLocalPortl ds 1 TCPRemotePorth ds 1 ; The destination port. TCPRemotePortl ds 1 TCPSND_UNA4 ds 1 ; SND.UNA. 1=LSB, 4=MSB. TCPSND_UNA3 ds 1 ; The oldest unacknowledged byte. TCPSND_UNA2 ds 1 TCPSND_UNA1 ds 1 TCPRCV_NXT4 ds 1 ; RCV.NXT. 1=LSB, 4=MSB. TCPRCV_NXT3 ds 1 ; The next byte to receive. TCPRCV_NXT2 ds 1 TCPRCV_NXT1 ds 1 TCPOffset ds 1 ; Length of the TCP options. TCPFlags ds 1 ; Flags field. TCP_WND_MSB ds 1 ; The send window (MSB). TCP_WND_LSB ds 1 ; the send window (LSB). TCBEnd = $ ENDIF ;------------------------------------------------------------------------------- ; SMTP variables ;------------------------------------------------------------------------------- org $60 IFDEF SMTPDEMO SMTPVars = $ SMTPState ds 1 ; SMTP transaction state machine. SMTPRxCount ds 1 ; Count of the number of bytes received in a packet. SMTPTxCount ds 1 ; Count of the number of bytes sent in a packet. SMTPCommand ds 1 ; Last SMTP command received. SMTPEOL ds 1 ; Flag to indicate if EOL reached. SMTPTxPointer ds 1 ; Pointer to the message to transmit. ENDIF ;------------------------------------------------------------------------------- ; POP3 variables ;------------------------------------------------------------------------------- org $60 IFDEF POP3DEMO POP3Vars = $ POP3State ds 1 ; POP3 transaction state machine. POP3MsgSubSt ds 1 ; substate of POP3 Msg state POP3MsgSubStLast ds 1 ; the last substate since AckPacketOk POP3RxCount ds 1 ; Count of the number of bytes received in a packet. POP3TxCount ds 1 ; Count of the number of bytes sent in a packet. ; either +ok -err or +ok n (for stat command) POP3Command1 ds 1 ; Last POP3 command received. POP3Command2 ds 1 POP3Command3 ds 1 POP3Command4 ds 1 POP3Command5 ds 1 POP3Command6 ds 1 POP3Command7 ds 1 POP3TxMsgNo ds 1 POP3EOL ds 1 ; Flag to indicate if EOL reached. POP3TxPointer ds 1 ; Pointer to the message to transmit. POP3MsgEndFlag ds 1 ; message end =1, set when <LF>.<CR> is detected, actual change of state done in AppPacketOk org $70 POP3MoreVars = $ POP3RxMsgNo ds 1 ; reuse of memory, not use at the same time org $75 ; position at the 5 th byte, so commands like "RETR xxx" will be easily used POP3TxMsgDigit1 ds 1 POP3TxMsgDigit2 ds 1 POP3TxMsgDigit3 ds 1 ENDIF ;------------------------------------------------------------------------------- ; HTTP variables ;------------------------------------------------------------------------------- org $60 IFDEF HTTPDEMO HTTPVars = $ HTTPParseState ds 1 ; State of the HTTP header parser. HTTPURIHash ds 1 ; Hash of the current URI. HTTPMethod ds 1 ; HTTP method. HTTPDone ds 1 HTTPLengthMSB ds 1 HTTPLengthLSB ds 1 E2Bank = $ E2DeviceRD = %10100001 E2DeviceWR = %10100000 E2FileSizeH ds 1 E2FileSizeL ds 1 E2FileChecksumH ds 1 E2FileChecksumL ds 1 E2AddrH ds 1 E2AddrL ds 1 E2DataBits ds 1 E2BitCount ds 1 E2Delay ds 1 ;E2Port = re E2Port EQU re E2SCLPin = E2Port.0 E2SDAPin = E2Port.1 E2SCLMask = %00000001 E2SDAMask = %00000010 E2SDAInDDR = %00000010 E2SDAOutDDR = %00000000 E2PortInit = %00000011 E2Size = 8192 ENDIF ;------------------------------------------------------------------------------- ; A/D Demo variables ;------------------------------------------------------------------------------- IFDEF ADCDEMO org $70 ADCVars = $ isrFlags ds 1 adcComplete ds 1 adcValue ds 1 adcCount ds 1 adcAcc ds 1 adcTemp ds 1 ADCWarning ds 1 ADCTxCount ds 1 ENDIF ;------------------------------------------------------------------------------- ; Java Demo variables ;------------------------------------------------------------------------------- IFDEF JAVADEMO org $80 JavaVars = $ lawnOn ds 1 lawnTime ds 1 pathOn ds 1 pathTime ds 1 flowerOn ds 1 flowerTime ds 1 JavaTxEnd = $ ENDIF ;------------------------------------------------------------------------------- ; Serial UART variables ;------------------------------------------------------------------------------- org $E0 serial = $ ; Serial UART bank ; save_bank ds 1 tx_high ds 1 ;hi byte to transmit tx_low ds 1 ;low byte to transmit tx_count ds 1 ;number of bits sent tx_divide ds 1 ;xmit timing (/16) counter rx_count ds 1 ;number of bits received rx_divide ds 1 ;receive timing counter rx_byte ds 1 ;buffer for incoming byte flags ds 1 ; Flags rx_flag EQU flags.0 ;signals when byte is received. rx_over EQU 1 ;signals an overflow. rx_ring_ip ds 1 ;receive ring in pointer rx_ring_op ds 1 ;receive ring out pointer rx_ring_cnt ds 1 ;receive ring contents count tx_ring_ip ds 1 ;transmit ring in pointer tx_ring_op ds 1 ;transmit ring out pointer tx_ring_cnt ds 1 ;transmit ring contents count org $F0 uart_rx_ring = $ ; UART ring buffers uart_tx_ring = $ rx_ring ds rx_ring_size ;space for the rx and tx ring buffers tx_ring ds tx_ring_size uart_temp ds 1 ; Temporary byte for UART. uart_temp_isr ds 1 ; Temporary byte for UART for use in ISR. IFDEF DEBUG org $D0 ;variables debug_serial = $ ;UART bank debug_tx_high ds 1 ;hi byte to transmit debug_tx_low ds 1 ;low byte to transmit debug_tx_count ds 1 ;number of bits sent debug_tx_divide ds 1 ;xmit timing (/16) counter debug_rx_count ds 1 ;number of bits received debug_rx_divide ds 1 ;receive timing counter debug_rx_byte ds 1 ;buffer for incoming byte debug_rx_flag EQU flags.6 ;signals when byte is received debug_save_bank ds 1 debug_save_mode ds 1 ;Save the mode register ENDIF ;=============================================================================== ; Interrupt service routine ;=============================================================================== org $0 ; The ISR starts at location 0. jmp @SerialISR ; Use the UART VP ;=============================================================================== ; Jump table ;=============================================================================== ResetVector jmp @_ResetVector org $C00 PPPInit jmp @_PPPInit PPPOpen jmp @_PPPOpen PPPRxData jmp @_PPPRxData PPPClose jmp @_PPPClose PPPSendConfReq jmp @_PPPSendConfReq PPPSendConfRej jmp @_PPPSendConfRej PPPSendCodeRej jmp @_PPPSendCodeRej PPPSendConfAck jmp @_PPPSendConfAck PPPSendTermReq jmp @_PPPSendTermReq PPPSendPacket jmp @_PPPSendPacket PPPStartIPPacket jmp @_PPPStartIPPacket PPPClosePacket jmp @_PPPClosePacket PPPSendPartialPacket jmp @_PPPSendPartialPacket PPPReceive jmp @_PPPReceive PPPTxFCSInit jmp @_PPPTxFCSInit PPPTxFCSData jmp @_PPPTxFCSData PPPRxFCSInit jmp @_PPPRxFCSInit PPPRxFCSData jmp @_PPPRxFCSData PPPCheckFCS jmp @_PPPCheckFCS IPStartPacket jmp @_IPStartPacket IPReceivePacket jmp @_IPReceivePacket IPRxHeader jmp @_IPRxHeader IPRxClosePacket jmp @_IPRxClosePacket IPChecksum jmp @_IPChecksum IFDEF UDP UDPStartPacket jmp @_UDPStartPacket UDPRxHeader jmp @_UDPRxHeader ENDIF IFDEF TCP TCPActiveOpen jmp @_TCPActiveOpen TCPPassiveOpen jmp @_TCPPassiveOpen TCPClosePacket jmp @_TCPClosePacket TCPTransmit jmp @_TCPTransmit TCPRxHeader jmp @_TCPRxHeader TCPSendHeader jmp @_TCPSendHeader TCPSendSyn jmp @_TCPSendSyn TCPSendSynAck jmp @_TCPSendSynAck TCPSendAck jmp @_TCPSendAck TCPAddRCV_NXT jmp @_TCPAddRCV_NXT TCPAddSND_UNA jmp @_TCPAddSND_UNA TCPInitChecksum jmp @_TCPInitChecksum TCPTxByte jmp @_TCPTxByte TCPProcessPacket jmp @_TCPProcessPacket TCPSendEmptyHeader jmp @_TCPSendEmptyHeader TCPClose jmp @_TCPClose TCPChecksum jmp @_TCPChecksum TCPAckUpdate jmp @_TCPAckUpdate AppInit jmp @_AppInit AppBytesToSend jmp @_AppBytesToSend AppBytesAvailable jmp @_AppBytesAvailable AppNak jmp @_AppNak AppAck jmp @_AppAck AppTxByte jmp @_AppTxByte AppRxByte jmp @_AppRxByte AppPacketOK jmp @_AppPacketOK AppPacketBad jmp @_AppPacketBad ENDIF IPRxData PhyRxByte jmp @_PhyRxByte IPTxData PhyTxByte jmp @_PhyTxByte PhyTxByteNoFCS jmp @_PhyTxByteNoFCS PhyNoTransTxByte jmp @_PhyNoTransTxByte PhyRxTest jmp @_PhyRxTest ModemConnect jmp @_ModemConnect GetByte jmp @_GetByte SendByte jmp @_SendByte SerialInit jmp @_SerialInit IFDEF DEBUG DebugSendByte jmp @_DebugSendByte DebugSerialISR jmp @_DebugSerialISR ENDIF IFDEF POP3DEMO get_tens jmp @_get_tens get_hundreds jmp @_get_hundreds times_10 jmp @_times_10 ENDIF IFDEF ADCDEMO ADCSendWarning jmp @_ADCSendWarning ENDIF ; ; Subroutine - Get byte via serial port ; _GetByte _bank serial :wait mov w,rx_ring_cnt ; Get the number of bytes in the rx ring snz ; Is the receive ring empty? jmp :wait ; Yes, block until not empty mov w,rx_ring_op ; Load the ring out pointer mov fsr,w mov w,indf ; Get character from buffer mov uart_temp,w ; Save character _bank serial ringadv rx_ring_op,rx_ring,rx_ring_size ; Advance ring pointer dec rx_ring_cnt ; Decrement rx char count snz clrb StatusPort.LEDRx snz ; Is the count zero? clrb cts_pin ; Yes. Set the CTS pin to restart the DTE. _bank uart_rx_ring mov w,uart_temp ; Return byte in W retp ; ; Subroutine - Send byte via serial port ; _SendByte _bank uart_tx_ring setb StatusPort.LEDTx ; Set the traffic LED. mov uart_temp,w ; Move the byte to the ring bank. _bank serial :wait csne tx_ring_cnt,#tx_ring_size ; Compare to the ring size jmp :wait ; No, block until there is room mov w,tx_ring_ip ; Get buffer pointer mov fsr,w mov w,uart_temp mov indf,w ; Save temp in the ring _bank serial ; Ensure we are using the serial variables ringadv tx_ring_ip,tx_ring,tx_ring_size ; Advance ring pointer inc tx_ring_cnt ; Increment tx char count retp ;leave and fix page bits _SerialInit mov FSR,#$10 ;reset all ram starting at 10h :zero_ram CLR IND ;clear using indirect addressing IJNZ FSR,:zero_ram ;repeat until done mov rd,#%01000100 ; The both UARTs use port D. mov !rd,#%10110001 ; Set RD in/out directions. _bank serial clr rx_ring_cnt ; The receive ring is empty. mov w,#rx_ring mov rx_ring_ip,w ; Set the in and out pointers to the start of mov rx_ring_op,w ; the receive ring. clr tx_ring_cnt ; The transmit ring is empty. mov w,#tx_ring mov tx_ring_ip,w ; Set the in and out pointers to the start of mov tx_ring_op,w ; the transmit ring. mov !option,#%10011111 ; Enable rtcc interrupt. clrb cts_pin ; Raise CTS to start the DTE. retp ;=============================================================================== ; Debugging code. A second UART VP is used to send debugging messages to a ; terminal. ;=============================================================================== IFDEF DEBUG ; If we are debugging then enable a second UART for transmitting debug info. debug_rx_pin EQU rd.0 ;UART receive input debug_tx_pin EQU rd.3 ;UART transmit output ; *** 57600 baud debug_baud_bit = 2 debug_start_delay = 4+2+1 debug_int_period = 217 ; ; Subroutine - Send byte via serial port ; _DebugSendByte _bank debug_serial :wait test debug_tx_count ; Wait for not busy jnz :wait not w ; Ready bits (inverse logic) mov debug_tx_high,w ; Store data byte setb debug_tx_low.7 ; Set up start bit mov debug_tx_count,#10 ; 1 start + 8 data + 1 stop bit retp ; ; Serial ISR for the debug UART ; _DebugSerialISR _bank debug_serial ; Switch to serial register bank :transmit clrb debug_tx_divide.debug_baud_bit ; Clear xmit timing count flag inc debug_tx_divide ; Only execute the transmit routine stz ; Set zero flag for test snb debug_tx_divide.debug_baud_bit ; Every 2^baud_bit interrupt test debug_tx_count ; Are we sending? JZ :rxdone ; If not, go to :receive clc ; Yes, ready stop bit rr debug_tx_high ; And shift to next bit rr debug_tx_low dec debug_tx_count ; Decrement bit counter movb debug_tx_pin,/debug_tx_low.6 ; Output next bit retp :rxdone retp ENDIF ;=============================================================================== ; PPP subroutines ;=============================================================================== org $4 ;------------------------------------------------------------------------------- ; Subroutine: PPPOpen ; Subroutine: PPPRxData ; ; Open a PPP connection to the peer. This entire routine must fit into a page ; and not cross any page boundaries. It must also be in the first half of a page. ; ; The same state machine is used to negotiate both the LCP and NCP (IPCP) ; parameters. The flags: PPPFlags.inLCP and PPPFlags.inIPCP indicate the ; current type of negotiation. ; ; W on entry: - ; W on exit : z is set to 1 if a packet contained IP data, 0 otherwise. ; Variables : - ;------------------------------------------------------------------------------- _PPPOpen DEBUGP $0B,0 _bank PPPVars mov PPPFlags,#(1<<inLCP) ; Initialize the state. initMachine setb StatusPort.LED0 ; Turn on the negotiation LED. mov PPPState,#PPPStateReqSent ; Reset the state machine. mov PPPRxState,#PPPStateFlag ; Reset the Rx state machine. mov PPPRestartCount,#PPPRestartCountDefault call @PPPSendConfReq ; Send a configure-request _PPPRxData DEBUGP $0E,0 :eventLoop ; Wait for an event _bank PPPVars mov PPPEvent,#PPP_NONE ; Assume there will be no event. call @PhyRxTest ; See if any bytes in receive buffer. sz jmp :pollTimer ; No bytes waiting. Check timer. call @PPPReceive ; Process the received byte. test PPPEvent ; Check for zero. sz ; Was the event non-zero? jmp :stateJump ; Yes. Process it. :pollTimer sb PPPFlags.inIP jmp :cont clz retp ; Check if the restart timer has expired. :cont sb PPPFlags.timerRunning ; Is the restart timer running? jmp :timerEnd ; No. _bank PPPTimer csae PPPTimer3,#PPPRestartExpire ; Has the restart timer expired? jmp :timerEnd ; No. clr PPPTimer1 ; Yes. Initialise the restart timer. clr PPPTimer2 clr PPPTimer3 _bank PPPVars test PPPRestartCount ; Is the restart count zero? sz mov w,#PPP_TO_GOOD ; Signal the event. snz mov w,#PPP_TO_BAD ; Signal the event. mov PPPEvent,w jmp :stateJump :timerEnd _bank PPPVars ; Call application specific short routine here. jmp :eventLoop ; Continue polling. :stateJump mov w,PPPState DEBUGW $0D,0 _bank PPPVars add PC,PPPState ; Find the state in the jump table. jmp :Initial jmp :Starting jmp :Closed jmp :Stopped jmp :Closing jmp :Stopping jmp :ReqSent jmp :AckRcvd jmp :AckSent jmp :Opened ; Not reached. The routine returns from one of the states. ; At this point W holds the latest event. :Initial jmp :eventLoop :Starting jmp :eventLoop :Closed jmp :eventLoop :Stopped jmp :eventLoop :Closing jmp :eventLoop :Stopping jmp :eventLoop ; State 6 ============================================================ :ReqSent cje PPPEvent,#PPP_RCR_BAD,:6ConfigureReject cje PPPEvent,#PPP_RCR_GOOD,:6ConfigureReq cje PPPEvent,#PPP_RCA,:6ConfigureAck cje PPPEvent,#PPP_TO_GOOD,:6TimerGood cje PPPEvent,#PPP_TO_BAD,:7TimerBad mov w,PPPEvent DEBUGW $04,0 jmp :eventLoop :6ConfigureReject ; We received a configure request with unacceptable options. call @PPPSendConfRej ; Send a configure reject. jmp :eventLoop ; Stay in this state. :6ConfigureReq ; We received an acceptable configure request. call @PPPSendConfAck ; Send a configure acknowledge. mov PPPState,#PPPStateAckSent ; Switch states. jmp :eventLoop :6ConfigureAck ; We received a configure acknowledgement for our request. ; ?? Initialize-Restart-Count mov PPPState,#PPPStateAckRcvd ; Switch states. jmp :eventLoop :6TimerGood ; The timer expired. Send the configure request packet again. call @PPPSendConfReq ; Retransmit the confReq jmp :eventLoop :6TimerBad DEBUGP $12,0 ; No peer. retp ; Return unsuccessful ; State 7 ============================================================ :AckRcvd cje PPPEvent,#PPP_RCR_BAD,:7ConfigureReject cje PPPEvent,#PPP_RCR_GOOD,:7ConfigureReq cje PPPEvent,#PPP_RCA,:7ConfigureAck cje PPPEvent,#PPP_TO_GOOD,:7TimerGood cje PPPEvent,#PPP_TO_BAD,:7TimerBad mov w,PPPEvent DEBUGW $04,0 jmp :eventLoop :7ConfigureReject ; We received a configure request with unacceptable options. call @PPPSendConfRej ; Send a configure reject. jmp :eventLoop ; Stay in this state. :7ConfigureReq ; We received an acceptable configure request. call @PPPSendConfAck ; Send a configure acknowledge. mov PPPState,#PPPStateOpened ; Switch states. jmp :Opened ; Jump straight to the state. :7ConfigureAck ; We received a configure acknowledgement but there was ; no request outstanding. Indicates some sort of problem. jmp :eventLoop :7TimerGood ; The timer expired. Send the configure request packet again. call @PPPSendConfReq ; Retransmit the confReq mov PPPState,#PPPStateReqSent ; Switch states. jmp :eventLoop :7TimerBad DEBUGP $12,0 ; No peer. retp ; Return unsuccessful ; State 8 ============================================================ :AckSent cje PPPEvent,#PPP_RCR_BAD,:8ConfigureReject cje PPPEvent,#PPP_RCR_GOOD,:8ConfigureReq cje PPPEvent,#PPP_RCA,:8ConfigureAck cje PPPEvent,#PPP_TO_GOOD,:8TimerGood cje PPPEvent,#PPP_TO_BAD,:8TimerBad mov w,PPPEvent DEBUGW $04,0 jmp :eventLoop :8ConfigureReject ; We received a configure request with unacceptable options. call @PPPSendConfRej ; Send a configure reject. mov PPPState,#PPPStateReqSent ; Switch states. jmp :eventLoop :8ConfigureReq ; We received an acceptable configure request. call @PPPSendConfAck ; Send a configure acknowledge. jmp :eventLoop ; Stay in this state. :8ConfigureAck ; We received a configure acknowledgement. Negotiation is ; complete. mov PPPState,#PPPStateOpened ; Switch states. jmp :Opened ;Jump straight to the state. :8TimerGood ; The timer expired. Send the configure request packet again. call @PPPSendConfReq ; Retransmit the confReq jmp :eventLoop :8TimerBad DEBUGP $12,0 ; No peer. retp ; Return unsuccessful ; State 9 ============================================================ :Opened clrb PPPFlags.timerRunning ; Stop the restart timer. sb PPPFlags.inLCP ; Are we currently doing LCP negotiation? jmp :IPCP ; No. Try IPCP. ; The LCP layer is now up. Start the network layer. clrb PPPFlags.inLCP ; We are finished with LCP negotiation. setb PPPFlags.inIPCP ; Start the IPCP negotiation. jmp initMachine ; Restart the state machine with IPCP. :IPCP sb PPPFlags.inIPCP ; Are we currently doing IPCP negotiation? jmp :IP ; No. Try IP ; PPP with IP is now up. DEBUGW $05,0 _bank PPPVars setb PPPFlags.linkUp setb StatusPort.LEDUp ; Light up the link up LED. nop nop clrb StatusPort.LED0 ; Turn of the negotiation LED. setb PPPFlags.inIP clrb PPPFlags.inIPCP retp ; The link is up, return successfully. :IP sb PPPFlags.inIP ; Is an IP connection open? jmp :error ; No. Report an error. cje PPPEvent,#PPP_DATA,:data ; Was the event IP data? cje PPPEvent,#PPP_RTR,:TermReq ; Was the event a terminate request? clrb z retp :data setb z retp ; Return. :TermReq mov PPPFlags,#0 ; Signal the link is down. clrb StatusPort.LEDUp clrb z retp :error setb StatusPort.LEDErr DEBUGP $11,0 jmp :error ;------------------------------------------------------------------------------- ; Subroutine: PPPInit ; ; Initialize the PPP layer. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPInit _bank PPPVars mov PPPState,#PPPStateInitial ; Reset the state machine. retp ;------------------------------------------------------------------------------- ; Subroutine: PPPSendConfReq ; ; Send a configure-request packet to the peer. This packet contains the LCP ; options that we wish to negotiate. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPSendConfReq _bank PPPTimer mov w,#0 mov PPPTimer1,w ; Initialise the restart timer. mov PPPTimer2,w mov PPPTimer3,w _bank PPPVars dec PPPRestartCount ; Decrement the restart count. setb PPPFlags.timerRunning ; Start the restart timer. snb PPPFlags.inLCP ; Are we in LCP negotiation? mov w, #_PPPConfReqPacketLCP&255 ; Load the packet offset snb PPPFlags.inIPCP ; Are we in IPCP negotiation? mov w, #_PPPConfReqPacketIPCP&255 ; Load the packet offset jmp @PPPSendPacket ; Send the packet. ; Return directly from PPPSendPacket ;------------------------------------------------------------------------------- ; Subroutine: PPPStartIPPacket ; ; Send an IP packet header. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPStartIPPacket mov w, #_PPPIPPacket&255 ; Load the packet offset jmp @PPPSendPartialPacket ; Send the packet. ; Return directly from PPPSendPacket ;------------------------------------------------------------------------------- ; Subroutine: PPPSendTermReq ; ; Send a terminate-request packet to the peer. This packet tells the peer we ; are closing the link. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPSendTermReq snb PPPFlags.inLCP ; Are we in LCP negotiation? mov w, #_PPPTermReqPacketLCP&255 ; Load the packet offset snb PPPFlags.inIPCP ; Are we in IPCP negotiation? mov w, #_PPPTermReqPacketIPCP&255 ; Load the packet offset jmp @PPPSendPacket ; Send the packet. ; Return directly from PPPSendPacket ;------------------------------------------------------------------------------- ; Subroutine: PPPSendPacket ; ; Send a canned packet. Scratch0 holds the address of the next byte. ; ; W on entry: start address of the packet to send ; W on exit : - ; Variables : Scratch0, Scratch1 ;------------------------------------------------------------------------------- _PPPSendPacket call @PPPSendPartialPacket jmp @PPPClosePacket ;------------------------------------------------------------------------------- ; Subroutine: PPPXxFCSInit ; ; Initialize the frame check sequence. ; ; W on entry: - ; W on exit : - ; Variables : PPPFCSh,PPPFCSl ;------------------------------------------------------------------------------- _PPPRxFCSInit mov w,#$ff ; Init FCS to $FFFF mov PPPRxFCSh,w mov PPPRxFCSl,w retp _PPPTxFCSInit mov w,#$ff ; Init FCS to $FFFF mov PPPTxFCSh,w mov PPPTxFCSl,w retp ;------------------------------------------------------------------------------- ; Subroutine: PPPCheckFCS ; ; Check that the FCS for a PPP frame is valid. The two FCS bytes must be the ; next bytes to be received. ; ; W on entry: - ; W on exit : z is set to 1 if the frame is valid, 0 otherwise. ; Variables : - ;------------------------------------------------------------------------------- _PPPCheckFCS call @PhyRxByte ; Receive the FCS. call @PhyRxByte _bank PPPVars cse PPPRxFCSh,#PPPRxFCSh ; Test the upper byte. jmp :invalid cse PPPRxFCSl,#PPPRxFCSl ; Test the lower byte. jmp :invalid setb z retp :invalid clrb z retp ;------------------------------------------------------------------------------- ; Subroutine: PPPFCSData ; ; Accumulate a byte for the FCS. The FCS is computed a byte at a time as the ; data is transmitted. ; ; W is preserved by this routine. ; ; W on entry: data byte ; W on exit : data byte ; Variables : Scratch1,PPPFCSl,PPPFCSh,PPPFCSA ;------------------------------------------------------------------------------- _PPPTxFCSData _bank PPPVars mov Scratch1,w ;Save W xor PPPTxFCSl,w ;FCSl[=X] = FCSl xor w mov w,<>PPPTxFCSl ;w = FCSl[32107654] and w,#%11110000 ;w = FCSl[3210oooo] xor PPPTxFCSl,w ;FCSl = FCSl xor (FCSl shl 4) ;Calculate A = FCSh mov w,<>PPPTxFCSl ;w = FCSl[32107654] mov PPPFCSA,w ;A = FCSl[32107654] mov w,>>PPPFCSA ;w = FCSl[x3210765] and w,#%00000111 ;w = FCSl[ooooo765] xor w,PPPTxFCSl ;w = FCSl xor (FCSl shr 5) mov PPPFCSA,w ;store w into A = new FCSh value ;Calculate new FCSl value rl PPPTxFCSl rl PPPTxFCSl mov w,<<PPPTxFCSl ;w = FCSl[43210xxx] and w,#%11111000 ;w = FCSl[43210ooo] xor w,PPPTxFCSh ;w = (FCSl shl 3) xor FCSh mov PPPTxFCSl,w ;Store w into FCSl mov w,<>PPPFCSA ;w = A[32107654] and w,#%00001111 ;w = A[oooo7654] xor PPPTxFCSl,w ;FCSl = (FCSl shl 3) xor FCSh xor (A shr 4) ;Store new FCSh value mov w,PPPFCSA ;A holds FCSh value mov PPPTxFCSh,w ;Store A in FCSh mov w,Scratch1 retp _PPPRxFCSData mov Scratch1,w ;Save w. ; DEBUGW $70,indent mov w,Scratch1 ;Restore w _bank PPPVars xor PPPRxFCSl,w ;FCSl[=X] = FCSl xor w mov w,<>PPPRxFCSl ;w = FCSl[32107654] and w,#%11110000 ;w = FCSl[3210oooo] xor PPPRxFCSl,w ;FCSl = FCSl xor (FCSl shl 4) ;Calculate A = FCSh mov w,<>PPPRxFCSl ;w = FCSl[32107654] mov PPPFCSA,w ;A = FCSl[32107654] mov w,>>PPPFCSA ;w = FCSl[x3210765] and w,#%00000111 ;w = FCSl[ooooo765] xor w,PPPRxFCSl ;w = FCSl xor (FCSl shr 5) mov PPPFCSA,w ;store w into A = new FCSh value ;Calculate new FCSl value rl PPPRxFCSl rl PPPRxFCSl mov w,<<PPPRxFCSl ;w = FCSl[43210xxx] and w,#%11111000 ;w = FCSl[43210ooo] xor w,PPPRxFCSh ;w = (FCSl shl 3) xor FCSh mov PPPRxFCSl,w ;Store w into FCSl mov w,<>PPPFCSA ;w = A[32107654] and w,#%00001111 ;w = A[oooo7654] xor PPPRxFCSl,w ;FCSl = (FCSl shl 3) xor FCSh xor (A shr 4) ;Store new FCSh value mov w,PPPFCSA ;A holds FCSh value mov PPPRxFCSh,w ;Store A in FCSh mov w,Scratch1 ;Restore w retp ;------------------------------------------------------------------------------- ; Canned PPP packets. The packet is terminated with a word with $f in the high nibble. ;------------------------------------------------------------------------------- _PPPCannedPackets = $ _PPPConfReqPacketLCP dw $FF, $03, $C0, $21, $01, $01, $00, $04 + $f00 _PPPCodeRejPacketLCP dw $FF, $03, $C0, $21, $07, $01 + $f00 _PPPConfRejPacketLCP dw $FF, $03, $C0, $21, $04 + $f00 _PPPConfAckPacketLCP dw $FF, $03, $C0, $21, $02 + $f00 _PPPTermReqPacketLCP dw $FF, $03, $C0, $21, $05, $01, $00, $04 + $f00 _PPPConfReqPacketIPCP dw $FF, $03, $80, $21, $01, $01, $00, $0A, $03, $06, IPAddress1, IPAddress2, IPAddress3, IPAddress4 + $f00 _PPPCodeRejPacketIPCP dw $FF, $03, $80, $21, $07, $01 + $f00 _PPPConfRejPacketIPCP dw $FF, $03, $80, $21, $04 + $f00 _PPPConfAckPacketIPCP dw $FF, $03, $80, $21, $02 + $f00 _PPPTermReqPacketIPCP dw $FF, $03, $80, $21, $05, $01, $00, $04 + $f00 _PPPIPPacket dw $FF, $03, $00, $21 + $f00 IFDEF POP3DEMO _POP3CannedPackets = $ _POP3USER dw 'USER eSX',CharCR,CharLF + $f00 _POP3PASS dw 'PASS eSX',CharCR,CharLF + $f00 _POP3STAT dw 'STAT',CharCR,CharLF + $f00 ; the zero is added in the following 2 canned messages so that the AppBytesToSend routine has the correct count _POP3RETR dw 'RETR ',0,0,0,CharCR,CharLF + $f00 _POP3DELE dw 'DELE ',0,0,0,CharCR,CharLF + $f00 _POP3QUIT dw 'QUIT',CharCR,CharLF + $f00 _POP3NONE dw $f00 ENDIF ;------------------------------------------------------------------------------- ; Subroutine: PPPReceive ; ; Receive a packet a byte at a time using a state machine. If the received byte ; indicates an event then PPPEvent is set. ; ; W on entry: - ; W on exit : Received byte (when receiving data). ; Variables : SCRATCH0,PPPRxState,PPPEvent ;------------------------------------------------------------------------------- org $200 _PPPReceive _bank serial sb flags.rx_over jmp :go DEBUGP $13,0 ; UART buffer overflow. _bank PPPVars clrb flags.rx_over :go call @PhyRxByte ; Receive a byte. mov Scratch0,w ; Save the byte. DEBUGW $03,1 _bank PPPVars mov w,PPPRxState ; Load the receiver state. DEBUGW $07,1 _bank PPPVars jmp @:tableStart ; The jump table must not cross a 256-word boundary. :tableStart jmp PC+W ; Jump into the table. jmp :Flag jmp :Address jmp :Control jmp :Proto1 jmp :Proto2 jmp :LCPCode jmp :LCPID jmp :LCPLen1 jmp :LCPLen2 jmp :Data jmp :FCS1 jmp :FCS2 :Flag cse SCRATCH0,#PPPFlag ; We expect the flag retp ; Didn't get it mov PPPRxState,#PPPStateAddress; Goto the next state. call @PPPRxFCSInit ; Initialize the FCS. DEBUGP $0C,0 _bank PPPVars retp :Address cje SCRATCH0,#PPPAddress,:GotAddress ; We expect the address cje SCRATCH0,#PPPFlag,:GotFlag ; Did we get a flag? jmp :resetRx ; Didn't get it :GotAddress mov PPPRxState,#PPPStateControl ; Goto the next state. :GotFlag ; Stay in this state. retp :Control cse SCRATCH0,#PPPControl ; We expect the address jmp :resetRx ; Didn't get it mov PPPRxState,#PPPStateProto1 ; Goto the next state. retp :Proto1 mov PPPProto1,SCRATCH0 ; Save the first byte of the protocol. mov PPPRxState,#PPPStateProto2 retp :Proto2 cje PPPProto1,#PPPLCPPrefix,:ProtoLCPPrefix cje PPPProto1,#PPPIPCPPrefix,:ProtoIPCPPrefix cje PPPProto1,#PPPIPPrefix,:ProtoIP ; Unknown protocol. mov PPPRxState,#PPPStateFlag ; Reset the receive state machine. ; Silently discard the packet. The discard is done by restarting the state machine ; and assuming it won't resynchronize until the next legitimate packet. retp :ProtoLCPPrefix cje SCRATCH0,#PPPLCP,:ProtoLCP ;cje ... ; Unknown protocol. mov PPPRxState,#PPPStateFlag ; Reset the receive state machine. ; Silently discard the packet. retp :ProtoLCP mov PPPRxState,#PPPStateLCPCode ; Goto the next state retp :ProtoIPCPPrefix cje SCRATCH0,#PPPLCP,:ProtoIPCP ;cje ... ; Unknown protocol. mov PPPRxState,#PPPStateFlag ; Reset the receive state machine. ; Silently discard the packet. retp :ProtoIPCP sb PPPFlags.inIPCP ; Are we doing IPCP? jmp :ResetRx ; No. Discard the packet. mov PPPRxState,#PPPStateLCPCode ; Goto the next state retp :ProtoIPPrefix cje SCRATCH0,#PPPIP,:ProtoIP ; Unknown protocol. mov PPPRxState,#PPPStateFlag ; Reset the receive state machine. ; Silently discard the packet. retp :ProtoIP mov PPPEvent,#PPP_DATA mov PPPRxState,#PPPStateFlag ; Restart the recevie state machine. retp :LCPCode cje SCRATCH0,#PPPConfigureRequest,:PPPConfigureRequest cje SCRATCH0,#PPPConfigureAck,:PPPConfigureAck cje SCRATCH0,#PPPTerminateRequest,:PPPTermReq mov PPPDelayEvent,#PPP_RUC ; By default we assume it was an unknown code mov PPPRxState,#PPPStateLCPID ; Goto the next state retp :PPPConfigureRequest ; We received a configure request. Since we don't accept any options we ; need to see if any options are included. For now assume it is bad. mov PPPDelayEvent,#PPP_RCR_BAD mov PPPRxState,#PPPStateLCPID ; Goto the next state retp :PPPConfigureAck ; We received a configure acknowledge. Since we negotiate no options it ; should be empty. mov PPPDelayEvent,#PPP_RCA mov PPPRxState,#PPPStateLCPID ; Goto the next state retp :PPPTermReq mov PPPDelayEvent,#PPP_RTR mov PPPRxState,#PPPStateLCPID ; Goto the next state retp :LCPID mov PPPIdentifier,Scratch0 ; Save the identifier mov PPPRxState,#PPPStateLCPLen1 ; Goto the next state retp :LCPLen1 mov PPPLengthh,Scratch0 ; Save the length mov PPPRxState,#PPPStateLCPLen2 ; Goto the next state retp :LCPLen2 mov PPPLengthl,Scratch0 ; Save the length mov PPPRxState,#PPPStateData ; Goto the next state mov PPPEvent,PPPDelayEvent ; Now send the delayed event. cje PPPEvent,#PPP_RCR_BAD,:CheckLength ; Make sure the confReq is really unacceptable retp :CheckLength cse PPPLengthl,#4 ; Is the LSB of the length 4? jmp :unacc ; No, now check if the option is really unacceptable. test PPPLengthh ; Is the MSB zero? sz retp ; No, we're done. mov PPPEvent,#PPP_RCR_GOOD ; We received a length 4 confReq. It is good. retp :unacc ; The option appears unacceptable. However... we must accept the IPCP Address option ; from Windows 95 or else it complains that we can't do IP. So... sb PPPFlags.inIPCP ; Are we doing IPCP? retp ; No, return. ; ## Assume the packet is good if we are in IPCP. IFNDEF WIN98 cse PPPLengthl,#10 ; Is the length 10? retp ; No, it can't be an address option only. ENDIF mov PPPEvent,#PPP_NONE ; Yes, Signal no event, we need to wait for the option. setb PPPFlags.addressOption ; Provisionally mark the event as good. retp :Data sb PPPFlags.inIPCP ; Are we doing IPCP? jmp :ResetRx ; No, gobble the packet. sb PPPFlags.addressOption ; Are we processing a potential good request jmp :ResetRx ; No csne Scratch0,#PPPEscape ; Is it the escape character? retp ; Get more data. cse Scratch0,#PPPAddressOption ; Is it the address option? jmp :ResetRx ; No, discard the rest of the packet. mov PPPEvent,#PPP_RCR_GOOD ; Signal a good conf-req. retp ; All of these cause the state machine to restart and gobble data up to ; the start of the next packet. :FCS1 :FCS2 :ResetRx clrb PPPFlags.addressOption mov PPPRxState,#PPPStateFlag ; Restart the receive state machine. retp ;------------------------------------------------------------------------------- ; Subroutine: PPPClosePacket ; ; Close a packet by sending the FCS and flag character. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPClosePacket _bank PPPVars mov w,/PPPTxFCSl ; Send the complemented FCS LSB first. call @PhyTxByteNoFCS ; Transmit it over the physical layer. _bank PPPVars mov w,/PPPTxFCSh call @PhyTxByteNoFCS mov w,#PPPFlag ; Send the flag character call @PhyNoTransTxByte ; Transmit without transparency. _bank PPPVars retp ;------------------------------------------------------------------------------- ; Subroutine: PPPClose ; ; Close the open PPP connection. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPClose _bank PPPVars call @PPPSendTermReq ; Send the terminate request. clrb PPPFlags.inIPCP ; Switch back to LCP mode. setb PPPFlags.inLCP call @PPPSendTermReq ; Send the terminate request. ; We don't bother waiting for a reply clrb PPPFlags.linkUp DEBUGW $06,0 retp ;------------------------------------------------------------------------------- ; Subroutine: PPPSendCodeReject ; ; Send a code-reject packet to the peer. The information field of the packet ; contains the received packet, starting at the information field, and not ; including the FCS. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPSendCodeRej snb PPPFlags.inLCP ; Are we in LCP negotiation? mov w, #_PPPCodeRejPacketLCP&255 ; Load the packet offset snb PPPFlags.inIPCP ; Are we in IPCP negotiation? mov w, #_PPPCodeRejPacketIPCP&255 ; Load the packet offset call @PPPSendPartialPacket ; Send the first part of the packet. _bank PPPVars ; Set the bank clc mov w,#4 ; The extra header adds 4 to the length add PPPLengthl,w ; Add the length snc ; Was there a carry? inc PPPLengthh ; Yes, increment the high byte mov w,PPPLengthh ; Load the length mov w,PPPLengthh ; Load the length call @PhyTxByte mov w,PPPLengthl ; Load the length call @PhyTxByte ; Send the received packet. jmp @PPPClosePacket ; Send the FCS and flag character. ;------------------------------------------------------------------------------- ; Subroutine: PPPSendConfAck ; ; Send a configure-acknowledge packet to the peer. The packet must contain ; the same identifier as the packet being acknowledged. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPSendConfAck DEBUGP $10,0 _bank PPPVars snb PPPFlags.inLCP ; Are we in LCP negotiation? mov w, #_PPPConfAckPacketLCP&255 ; Load the packet offset snb PPPFlags.inIPCP ; Are we in IPCP negotiation? mov w, #_PPPConfAckPacketIPCP&255 ; Load the packet offset call @PPPSendPartialPacket _bank PPPVars ; Set the bank mov w,PPPIdentifier ; Load the ID from the received packet. call @PhyTxByte ; Transmit. _bank PPPVars mov w,PPPLengthh ; Load the length from the received packet. call @PhyTxByte ; Transmit. _bank PPPVars mov w,PPPLengthl ; Load the ID from the received packet. call @PhyTxByte ; Transmit. ; Now use the length to count in the received bytes and retransmit them. ; First subtract the header length from the length. _bank PPPVars mov w,#4-1 ; Load the header length. sub PPPLengthl,w ; Subtract from the LSB sc dec PPPLengthh mov w,#0 ; Load the MSB of the header length sub PPPLengthh,w ; Subtract the MSB ; If this is an address option then send the option. sb PPPFlags.addressOption ; Is it an address option? jmp :midloop ; No. mov w,#PPPAddressOption call @PhyTxByte ; Send it _bank PPPVars dec PPPLengthl ; Decrement the length by 1. clrb PPPFlags.addressOption ; Clear the address option flag. jmp :midloop ; Now loop for each received byte. :loop call @PhyRxByte ; Receive a byte. call @PhyTxByte ; Retransmit the byte. _bank PPPVars :midloop dec PPPLengthl ; Decrement the count. sz ; Is it zero? jmp :loop ; No, loop again. test PPPLengthh ; Is the MSB zero? snz jmp :done ; Yes, we're done. dec PPPLengthh ; No, decrement. dec PPPLengthl jmp :loop :done jmp @PPPClosePacket ; Send the FCS and close the packet. ; Return directly from PPPClosePacket ;------------------------------------------------------------------------------- ; Subroutine: PPPSendConfRej ; ; Send a configure-reject packet. The rejected options fields are copied from ; the received packet. ; ; The first portion of the packet, up to and including the Code, is canned ; and sent as a string. The identifier and length are copied from the recieved ; packet. Then the options are copied from the received packet to the new packet. ; ; The remaineder of the received packet (FCS and flag) are discarded by the ; next state. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _PPPSendConfRej DEBUGP $0F,0 _bank PPPVars snb PPPFlags.inLCP ; Are we in LCP negotiation? mov w, #_PPPConfRejPacketLCP&255 ; Load the packet offset snb PPPFlags.inIPCP ; Are we in IPCP negotiation? mov w, #_PPPConfRejPacketIPCP&255 ; Load the packet offset call @PPPSendPartialPacket ; Send the first part of the packet. _bank PPPVars ; Set the bank mov w,PPPIdentifier ; Load the ID from the received packet. call @PhyTxByte ; Transmit. _bank PPPVars mov w,PPPLengthh ; Load the length from the received packet. call @PhyTxByte ; Transmit. _bank PPPVars mov w,PPPLengthl ; Load the ID from the received packet. call @PhyTxByte ; Transmit. ; Now use the length to count in the received bytes and retransmit them. ; First subtract the header length from the length. _bank PPPVars mov w,#4 ; Load the header length minus 1. sub PPPLengthl,w ; Subtract from the LSB sc dec PPPLengthh mov w,#0 ; Load the MSB of the header length sub PPPLengthh,w ; Subtract the MSB ; Now loop for each received byte. :loop call @PhyRxByte ; Receive a byte. call @PhyTxByte ; Retransmit the byte. _bank PPPVars dec PPPLengthl ; Decrement the count. sz ; Is it zero? jmp :loop ; No, loop again. test PPPLengthh ; Is the MSB zero? snz jmp :done ; Yes, we're done. dec PPPLengthh ; No, decrement. dec PPPLengthl jmp :loop :done jmp @PPPClosePacket ; Send the FCS and flag character. ;------------------------------------------------------------------------------- ; Subroutine: PPPSendPartialPacket ; ; Send a canned packet without adding the frame check sequence. ; ; W on entry: start address of the packet to send ; W on exit : - ; Variables : Scratch0, Scratch1 ;------------------------------------------------------------------------------- _PPPSendPartialPacket mov Scratch0,w ; Save the start address. DEBUGW $08,0 mov w,#PPPFlag ; Send the start flag call @PhyNoTransTxByte ; Transmit without transparency. _bank PPPVars call @PPPTxFCSInit ; Initialize the FCS mov w,#0 mov m,w :loop mov m, #(_PPPCannedPackets>>8) ; Load the mode register. mov w,Scratch0 ; Load the pointer iread ; Read the next byte. _bank PPPVars call @PhyTxByte ; Transmit it over the physical layer. _bank PPPVars mov w,m ; Load the mode register. test w sz ; If it is not zero then exit. jmp :done ; We're done transmitting. inc Scratch0 ; Increment the pointer jmp :loop :done retp ;------------------------------------------------------------------------------- ; Subroutine: ModemConnect ; ; Pretend that we are a modem so that Windows 95 Dialup Networking will talk to ; us. The strategy is simple: ; ; while there is input ; if it starts with ATDT ; send CONNECT ; exit. PPP layer can start ; else ; send OK ; ; W on entry: - ; W on exit : - ; Variables : - ; Bank on exit : - ;------------------------------------------------------------------------------- ; org $f00 _ModemConnect call @GetByte ; Load the first byte to prime the pump. mov Scratch0,w ; Save the first byte. jmp :loop :ok mov w,#'O' ; Send OK. call @SendByte mov w,#'K' call @SendByte mov w,#13 ; Send a carriage return call @SendByte :loop mov Scratch1,Scratch0 ; Shift the bytes. call @GetByte mov Scratch0,w ; Save the second byte. cjne Scratch1,#'A',:loop ; Did we get an 'A'? cjne Scratch0,#'T',:loop ; Was it followed by a 'T'? call @GetByte ; Get another byte mov Scratch0,w cjne Scratch0,#'D',:ok ; Did we get a 'D'? call @GetByte ; Get another byte mov Scratch0,w cjne Scratch0,#'T',:ok ; Did we get a 'T'? mov w,#'C' ; Send CONNECT. call @SendByte mov w,#'O' call @SendByte mov w,#'N' call @SendByte mov w,#'N' call @SendByte mov w,#'E' call @SendByte mov w,#'C' call @SendByte mov w,#'T' call @SendByte mov w,#13 ; Send a carriage return call @SendByte retp IFDEF POP3DEMO ;------------------------------------------------------------------------------- ; Subroutine: AppInit ; ; Called once at startup to allow the application to initialise itself. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppInit mov w,#'I' POP3W _bank POP3Vars mov POP3State,#POP3StateClosed ; Start in the closed state. clr POP3TxMsgNo clr POP3MsgEndFlag clr POP3MsgSubSt ;start of Msg sub state clr POP3MsgSubStLast clr POP3RxCount clr POP3TxCount mov POP3TxPointer,#_POP3NONE clr POP3EOL _bank IPVars mov IPDestAddress1,#POP3Address1 ; POP3 server IP address. mov IPDestAddress2,#POP3Address2 mov IPDestAddress3,#POP3Address3 mov IPDestAddress4,#POP3Address4 ; Randomly choose our own port. _bank PPPTimer mov w,PPPTimer2 ; Base it on a timer. _bank TCB mov TCPLocalPortl,w mov TCPLocalPorth,#$36 mov TCPRemotePorth,#POP3Porth ; Set the port to connect to. mov TCPRemotePortl,#POP3Portl jmp @_TCPActiveOpen ; Connect to the remote TCP. ENDIF ;=============================================================================== ; IP subroutines ;=============================================================================== org $400 ;------------------------------------------------------------------------------- ; Subroutine: IPStartPacket ; ; Start transmitting an IP packet. First a physical layer header is transmitted ; followed by the IP header. Information about the packet destination and length ; is read from the IPVars bank variables. ; ; Comments on the IP header checksum: ; ; The checksum is computed over the header fields only and is the 16 bit ; complement of the 16 bit ones complement sum. The checksum field is set to ; zero to compute the checksum. To simplify the checksum calculation all fields ; that are known a priori are calculated below: ; ; Version/IHL/TOS 4500 ; Total length ? ; ID ? ; Flags/Fragment offset 0000 ; TTL/Protocol ? ; Checksum 0000 ; Source Address IPAddress1<<8 | IPAddress2 ; Source Address IPAddress3<<8 | IPAddress4 ; Dest Address ? ; ; ip_cs1 = $4500 + (IPAddress1<<8 | IPAddress2) + (IPAddress3<<8 | IPAddress4) if ip_cs1 > $10000 ip_cs2 = ip_cs1 + 1 - $10000 else ip_cs2 = ip_cs1 endif ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _IPStartPacket call @PPPStartIPPacket ; Send a PPP IP packet header. _bank IPVars mov IPChecksumMSB,#(ip_cs2>>8)&$00ff ; Initialize the checksum. mov IPChecksumLSB,#ip_cs2&$00ff ; Increase the length by the header length. add IPLengthLSB,#(IPIHL<<2) ; Add the header length times 4. snc inc IPLengthMSB ; Compute the checksum clrb IPFlags.checksumBit mov w,IPLengthMSB call @IPChecksum mov w,IPLengthLSB call @IPChecksum mov w,#0 call @IPChecksum mov w,IPIDCounter call @IPChecksum mov w,#IPTTL call @IPChecksum mov w,IPProtocol call @IPChecksum mov w,IPDestAddress1 call @IPChecksum mov w,IPDestAddress2 call @IPChecksum mov w,IPDestAddress3 call @IPChecksum mov w,IPDestAddress4 call @IPChecksum not IPChecksumMSB not IPChecksumLSB mov w,#IPVIHL call @PhyTxByte mov w,#IPTOS call @PhyTxByte _bank IPVars mov w,IPLengthMSB ; Send the MSB of the length call @PhyTxByte _bank IPVars mov w,IPLengthLSB ; Send the LSB of the length call @PhyTxByte mov w,#$00 ; The identifier. call @PhyTxByte _bank IPVars mov w,IPIDCounter ; Load the counter before incrementing. inc IPIDCounter ; Increment the counter. call @PhyTxByte mov w,#IPFrag1 call @PhyTxByte mov w,#IPFrag2 call @PhyTxByte mov w,#IPTTL call @PhyTxByte _bank IPVars mov w,IPProtocol call @PhyTxByte _bank IPVars mov w,IPChecksumMSB call @PhyTxByte _bank IPVars mov w,IPChecksumLSB call @PhyTxByte mov w,#IPAddress1 call @PhyTxByte mov w,#IPAddress2 call @PhyTxByte mov w,#IPAddress3 call @PhyTxByte mov w,#IPAddress4 call @PhyTxByte _bank IPVars mov w,IPDestAddress1 call @PhyTxByte _bank IPVars mov w,IPDestAddress2 call @PhyTxByte _bank IPVars mov w,IPDestAddress3 call @PhyTxByte _bank IPVars mov w,IPDestAddress4 call @PhyTxByte ; There are no options. retp ;------------------------------------------------------------------------------- ; Subroutine: IPChecksum ; ; Accumulate the IP checksum. ; ; W on entry: The value of the byte to accumulate. ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _IPChecksum _bank IPVars sb IPFlags.checksumBit ; Are we processing an MSB? jmp :MSB ; Yes 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 IPFlags,#(1<<checksumBit) ; Flip the checksum bit. retp ;------------------------------------------------------------------------------- ; Subroutine: IPRxHeader ; ; Receive an IP packet header. If the header is not valid then the Z flag is set. ; ; W on entry: - ; W on exit : Z is set to 1 if the packet is invalid, 0 otherwise. ; Variables : - ;------------------------------------------------------------------------------- _IPRxHeader _bank IPVars call @PhyRxByte ; Receive a byte. xor w,#((IPVersion<<4)|IPIHL) ; We only accept packets with a HL of 5. sz jmp :Invalid call @PhyRxByte ; Ignore the type of service call @PhyRxByte mov IPRxLengthMSB,w ; Save the packet length. call @PhyRxByte mov IPRxLengthLSB,w ; Save the packet length. call @PhyRxByte ; Ignore the ID. call @PhyRxByte call @PhyRxByte and w,#$20 ; Are any fragment bits set? sz jmp :Invalid ; Yes, discard the packet. call @PhyRxByte test w ; Is the fragment offset set? sz jmp :Invalid ; Yes, discard the packet. call @PhyRxByte ; Ignore the TTL. call @PhyRxByte ; Receive the protocol. _bank IPVars mov SCRATCH0,w csne SCRATCH0,#IPProtocolICMP setb IPFlags.ICMPPacket csne SCRATCH0,#IPProtocolTCP setb IPFlags.TCPPacket csne SCRATCH0,#IPProtocolUDP setb IPFlags.UDPPacket call @PhyRxByte ; Live dangerously, Ignore the header checksum. call @PhyRxByte IFDEF TCP ; If a TCP connection is open then drop packets from any other IP address. _bank TCPVars cse TCPState,#TCPStateEstablished jmp :notTCP :inTCP _bank IPVars call @PhyRxByte xor w,IPSrcAddress1 ; Verify that the source address is the same as the existing. sz jmp :Invalid call @PhyRxByte xor w,IPSrcAddress2 sz jmp :Invalid call @PhyRxByte xor w,IPSrcAddress3 sz jmp :Invalid call @PhyRxByte xor w,IPSrcAddress4 sz jmp :Invalid jmp :ipAddressOK ENDIF :notTCP _bank IPVars call @PhyRxByte mov IPSrcAddress1,w ; Save the source address. call @PhyRxByte mov IPSrcAddress2,w ; Save the source address. call @PhyRxByte mov IPSrcAddress3,w ; Save the source address. call @PhyRxByte mov IPSrcAddress4,w ; Save the source address. :ipAddressOK call @PhyRxByte xor w,#IPAddress1 ; Verify that the packet is for us. sz jmp :Invalid call @PhyRxByte xor w,#IPAddress2 ; Verify that the packet is for us. sz jmp :Invalid call @PhyRxByte xor w,#IPAddress3 ; Verify that the packet is for us. sz jmp :Invalid call @PhyRxByte xor w,#IPAddress4 ; Verify that the packet is for us. sz jmp :Invalid sub IPRxLengthLSB,#(IPIHL<<2) ; Subtract the IP header length. sc dec IPRxLengthMSB ; That's the whole header. _bank IPVars setb IPFlags.anyPacket clrb z retp ; Return successful. :Invalid setb z retp ; Return unsuccessful. ;------------------------------------------------------------------------------- ; Subroutine: IPReceivePacket ; ; Receive the next IP packet. This routine DOES NOT block if there is no packet ; to receive. Bits are set in IPFlags acording to the packet type received. ; The following actions are taken: ; ; 1. ICMP echo packet - send response, set echo flag (just for info purposes). ; 2. UDP - receive UDP header, set UDP flag. ; 3. Any other packet type - set Unknown flag. ; ; If the received packet is a UDP packet then the application can call IPRxData ; to receive the packet contents. ; ; After the packet has been processed by the application it should call IPCleanUp ; to ensure that any of the incoming data not used by the application is read from ; the physical layer. ; ; !!!! Actually this routine might block if an LCP packet is being received and ; it is not followed by an IP packet. !!!! ; ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _IPReceivePacket _bank IPVars clr IPFlags ; Initialise the flags. call @PhyRxTest ; Test if there is a byte in the physical layer. sz ; Is a byte waiting? retp ; No, return. call @PPPRxData ; Read the PPP header. sz ; Was IP data received. retp ; No, just return. call @IPRxHeader ; Receive the IP header. _bank IPVars snb IPFlags.ICMPPacket ; Is it an ICMP packet? jmp :ICMP ; No, return. sz ; Is the packet valid? retp ; Yes. Return. jmp _IPRxClosePacket ; No, Gobble it up. Yum, yum. :ICMP ; Check if it is an echo request. call @PhyRxByte xor w,#ICMPEchoRequest sz ; Is it an echo request? jmp _IPRxClosePacket ; No, gobble the packet. ; Send the ICMP echo reply packet. call @PhyRxByte ; Ignore the Code field. _bank IPVars mov IPLengthMSB,IPRxLengthMSB mov IPLengthLSB,IPRxLengthLSB ; Set the length to the received length. mov IPProtocol,#IPProtocolICMP ; Set the protocol type to ICMP. mov IPDestAddress1,IPSrcAddress1 ; Copy the address. mov IPDestAddress2,IPSrcAddress2 ; !!!! This will only work if mov IPDestAddress3,IPSrcAddress3 ; IPRxVars and IPVars are in the mov IPDestAddress4,IPSrcAddress4 ; same bank !!!! call @IPStartPacket ; Send the IP packet header. mov w,#ICMPEchoReply ; Send the ICMP type. call @IPTxData mov w,#0 ; Send the code. call @IPTxData call @PhyRxByte ; Load the checksum. mov IPChecksumMSB,w call @PhyRxByte mov IPChecksumLSB,w add IPChecksumMSB,#8 snc inc IPChecksumLSB mov w,IPChecksumMSB call @PhyTxByte mov w,IPChecksumLSB call @PhyTxByte call @PhyRxByte call @PhyTxByte ; Send identifier. call @PhyRxByte call @PhyTxByte call @PhyRxByte call @PhyTxByte ; Send sequence number. call @PhyRxByte call @PhyTxByte ; Send the data from the echo request. sub IPLengthLSB,#(IPIHL<<2) + ICMPEchoHL :data call @PhyRxByte call @PhyTxByte decsz IPLengthLSB jmp :data call @PPPClosePacket ; Close the echo reply packet. _bank IPVars setb IPFlags.echoPacket retp _IPRxClosePacket ; Ignore a packet by receiving everything up to the closing flag. _bank IPVars ; Reset the flags. clr IPFlags DEBUGP $09,0 :loop call @PhyRxByte DEBUGW $03,1 xor w,#PPPFlag sz jmp :loop ; Since we just consumed a flag, move to the next PPP state to avoid missing ; the next packet. _bank PPPVars mov PPPRxState,#PPPStateAddress call @PPPRxFCSInit ; Initialize the FCS. retp IFDEF UDP ;------------------------------------------------------------------------------- ; Subroutine: UDPStartPacket ; ; Transmit an IP header followed by a UDP header. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _UDPStartPacket _bank IPVars add IPLengthLSB,#UDPHLength ; Increment the length by the UDP header size. snc inc IPLengthMSB mov IPProtocol,#IPProtocolUDP ; Set the protocol type to UDP. mov Scratch0,IPLengthMSB mov w,IPLengthLSB _bank UDPVars mov UDPLengthLSB,w ; Save the length for later. mov UDPLengthMSB,Scratch0 call @IPStartPacket _bank UDPVars mov w,UDPSrcPorth ; Load the source port. call @PhyTxByte mov w,UDPSrcPortl call @PhyTxByte mov w,UDPDestPorth ; Load the destination port. call @PhyTxByte mov w,UDPDestPortl call @PhyTxByte mov w,UDPLengthMSB ; Reload the saved lengh. call @PhyTxByte mov w,UDPLengthLSB ; Reload the saved lengh. call @PhyTxByte mov w,#0 ; Send zero for the checksum call @PhyTxByte mov w,#0 call @PhyTxByte retp ;------------------------------------------------------------------------------- ; Subroutine: UDPRxHeader ; ; Receive a UDP header. To make it easy to reply to a packet the source ; port of the received packet is copied into UDPDestPort and the destination ; port is copied into UDPSrcPort. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _UDPRxHeader call @PhyRxByte ; Get the source port. _bank UDPVars mov UDPDestPorth,w call @PhyRxByte mov UDPDestPortl,w call @PhyRxByte ; Get the destination port. mov UDPSrcPorth,w call @PhyRxByte mov UDPSrcPortl,w call @PhyRxByte ; Ignore the high byte of the length. call @PhyRxByte ; Get the low byte of the length. mov UDPRxLength,w sub UDPRxLength,#UDPHLength ; Subtract the length of the header. call @PhyRxByte ; Ignore the checksum. call @PhyRxByte retp ENDIF ;=============================================================================== ; TCP subroutines ;=============================================================================== ;------------------------------------------------------------------------------- ; TCP helper macros. ; ; Macros to assist with manipulation and comparison of 32 bit values. ; Made more complex by the fact that values might be in different banks. ; ; Specify the LSB of each argument. ; Must be in the bank of A on entry. ; Bank might be changed on exit. ;------------------------------------------------------------------------------- ; A == B ; A <= B ; A + constant ; A = B ; Compare32 A,B (returns with z bit set if A==B) TCPCompare32 MACRO 2 ; SXKEY_CHANGE : Change the '==' in the next line to '=' IF \1>>4 == \2>>4 ; Are they in the same bank? mov w,\1 xor w,\2 sz jmp :cdone mov w,\1-1 xor w,\2-1 sz jmp :cdone mov w,\1-2 xor w,\2-2 sz jmp :cdone mov w,\1-3 xor w,\2-3 ELSE mov w,\1 ; _bank \2 bank \2 IFDEF SX48_52 IFDEF SX48_52_ES IF \2 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE IF \2 & %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 ENDIF ENDIF xor w,\2 sz jmp :cdone ; _bank \1 bank \1 IFDEF SX48_52 IFDEF SX48_52_ES IF \1 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE 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 ENDIF ENDIF mov w,\1-1 ; _bank \2 bank \2 IFDEF SX48_52 IFDEF SX48_52_ES IF \2 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE IF \2 & %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 ENDIF ENDIF xor w,\2-1 sz jmp :cdone ; _bank \1 bank \1 IFDEF SX48_52 IFDEF SX48_52_ES IF \1 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE 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 ENDIF ENDIF mov w,\1-2 ; _bank \2 bank \2 IFDEF SX48_52 IFDEF SX48_52_ES IF \2 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE IF \2 & %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 ENDIF ENDIF xor w,\2-2 sz jmp :cdone ; _bank \1 bank \1 IFDEF SX48_52 IFDEF SX48_52_ES IF \1 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE 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 ENDIF ENDIF mov w,\1-3 ; _bank \2 bank \2 IFDEF SX48_52 IFDEF SX48_52_ES IF \2 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. ENDIF ELSE IF \2 & %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 ENDIF ENDIF xor w,\2-3 ENDIF :cdone ENDM IFDEF TCP ;------------------------------------------------------------------------------- ; Subroutine: TCPPassiveOpen ; ; Do a passive open. I.e. listen for connections on a given port. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPPassiveOpen _bank TCPVars mov TCPState,#TCPStateListen clr TCPOutstanding retp ;------------------------------------------------------------------------------- ; Subroutine: TCPActiveOpen ; ; Do a active open. I.e. initiate a connect to a remote TCP. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPActiveOpen _bank TCB mov TCPFlags,#(1<<TCPFlagSYN) ; Make a SYN packet. call @TCPSendSyn _bank TCPVars clr TCPOutstanding mov TCPState,#TCPStateSynSent retp ;------------------------------------------------------------------------------- ; Subroutine: TCPClose ; ; Force the current connection to close. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPClose _bank TCPVars mov TCPState,#TCPStateFinWait1 clr TCPOutstanding _bank TCB mov TCPFlags,#(1<<TCPFlagFIN)|(1<<TCPFlagACK) ; A FIN packet. call @TCPSendEmptyHeader ; Send the header. call @PPPClosePacket ; Close the packet. retp ;------------------------------------------------------------------------------- ; Subroutine: TCPRxHeader ; ; Receive a TCP header. ; ; W on entry: - ; W on exit : Z is set to 1 if the packet is invalid, 0 otherwise. ; Variables : - ;------------------------------------------------------------------------------- org $600 _TCPRxHeader ; Check port and refuse packet if not for current connection. DEBUGP $02,top ; TCP receive. _bank TCPVars mov w,TCPState DEBUGW $14,top ; TCP state. _bank TCPVars cjne TCPState,#TCPStateListen,:CheckSrc :readSrc ; Read the source port. _bank TCB call @PhyRxByte ; Get the source port. mov TCPRemotePorth,w call @PhyRxByte mov TCPRemotePortl,w jmp :checkDest :checkSrc ; Check the source port is for the current connection. _bank TCB call @PhyRxByte xor w,TCPRemotePorth sz ; Is the high byte the same? jmp :ignorePacket ; No, ignore the packet. call @PhyRxByte ; Get the low byte. xor w,TCPRemotePortl sz ; Is the low byte the same? jmp :ignorePacket ; No, ignore the packet. :checkDest call @PhyRxByte ; Check the destination port matches our port. xor w,TCPLocalPorth sz ; Is the high byte the same? jmp :ignorePacket ; No, ignore the packet. call @PhyRxByte ; Get the low byte. xor w,TCPLocalPortl sz ; Is the low byte the same? jmp :ignorePacket ; No, ignore the packet. _bank TCPVars call @PhyRxByte ; Receive the sequence number mov TCPTMP_SEQ4,w ; Save the sequence number in the TMP variable. call @PhyRxByte mov TCPTMP_SEQ3,w call @PhyRxByte mov TCPTMP_SEQ2,w call @PhyRxByte mov TCPTMP_SEQ1,w call @PhyRxByte ; Receive the acknowledgement mov TCPTMP_ACK4,w call @PhyRxByte mov TCPTMP_ACK3,w call @PhyRxByte mov TCPTMP_ACK2,w call @PhyRxByte mov TCPTMP_ACK1,w call @PhyRxByte ; Receive the data offset. Used to skip the options. and w,#TCPOffsetMask ; Mask out the offset. _bank TCB mov TCPOffset,w clc rr TCPOffset ; Shift right to get the number of bytes. rr TCPOffset mov w,TCPOffset _bank IPVars ; Decrease the total packet length sub IPRxLengthLSB,w sc dec IPRxLengthMSB _bank TCB sub TCPOffset,#(TCPHeaderLength<<2)-4 ; Subtract the standard header length (less the checksum and URG pointer.) call @PhyRxByte ; Receive the flags. DEBUGW $15,indent _bank TCPVars mov TCPRxFlags,w ; Take a copy of the flags. _bank TCB mov TCPFlags,w call @PhyRxByte mov TCP_WND_MSB,w ; Receive the window. call @PhyRxByte mov TCP_WND_LSB,w ; Skip over the options. :optionLoop call @PhyRxByte decsz TCPOffset jmp :optionLoop clz retp :ignorePacket ; Ignore the rest of the packet. DEBUGP $16,1 call @IPRxClosePacket ; ## Need to send a reset in response to this packet. ; ## Unfortunately sending a packet would overwrite the TCB. ; ## Need to create a second TCB? setb z retp ;------------------------------------------------------------------------------- ; Subroutine: TCPSendEmptyHeader,TCPSendHeader ; ; Send a TCP Header. Send empty header puts the checksum in the header so that ; a packet doesn't need to contain any data for a stuffed packet. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPSendEmptyHeader _bank IPVars clr IPLengthMSB clr IPLengthLSB clr TCPChecksumLSB clr TCPChecksumMSB clrb IPFlags.checksumBit ; Indicate next checksum bit is an MSB. _TCPSendHeader _bank TCB mov TCPOffset,#(TCPHeaderLength<<4) mov TCP_WND_MSB,#(TCPWindow&$ff00)>>8 mov TCP_WND_LSB,#(TCPWindow&$00ff) _bank IPVars clrb IPFlags.checksumBit ; Indicate next checksum bit is an MSB. add IPLengthLSB,#(TCPHeaderLength<<2) ; Add in the length of a TCP header. snc inc IPLengthMSB mov IPProtocol,#IPProtocolTCP ; Set the protocol type to TCP. call @TCPInitChecksum call @IPStartPacket ; Send the IP header. mov Scratch0,#TCB ; Send the TCB fields. :loop mov FSR,Scratch0 mov w,INDF ; Load the value. call @TCPTxByte ; Transmit inc Scratch0 cse Scratch0,#TCBEnd ; Is the loop finished. jmp :loop _bank IPVars mov w,TCPChecksumMSB not w call @PhyTxByte ; Transmit the MSB of the checksum. mov w,TCPChecksumLSB not w call @PhyTxByte ; Transmit the LSB of the checksum. mov w,#0 call @PhyTxByte ; Transmit the urgent pointer. mov w,#0 call @PhyTxByte retp ;------------------------------------------------------------------------------- ; Subroutine: TCPClosePacket ; ; Send the TCP checksum byte stuff and close the PPP packet. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPClosePacket = _PPPClosePacket ;------------------------------------------------------------------------------- ; Subroutine: TCPProcessPacket ; ; Process a received TCP packet. This function implements the TCP state machine. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPProcessPacket call @TCPRxHeader ; Receive the header. snz ; Is the packet OK? retp ; No, return. _bank TCPVars cje TCPState,#TCPStateClosed,:CLOSED ; Check the special states. cje TCPState,#TCPStateListen,:LISTEN cje TCPState,#TCPStateSynSent,:SYNSENT cje TCPState,#TCPStateFinWait1,:FINWAIT1 cje TCPState,#TCPStateFinWait2,:FINWAIT2 cje TCPState,#TCPStateLastAck,:LASTACK ; Otherwise... setb StatusPort.LED1 TCPCompare32 TCPTMP_SEQ1,TCPRCV_NXT1 ; Check that RCV.NXT == SEG.SEQ sz ; Are they equal? jmp @_TCPSendAck ; No, Send an ACK and drop packet. _bank TCB ; check the flags. snb TCPFlags.TCPFlagRST ; Is the reset flag set? jmp :gotoClosed ; Yes. Drop packet and close the connection. snb TCPFlags.TCPFlagSYN ; Is the SYN bit set? jmp @_TCPSendReset ; Yes. Drop the packet and send a reset. sb TCPFlags.TCPFlagACK ; Is the ACK bit set? jmp @_IPRxClosePacket ; No. Drop the packet. ; We only accept ACKs of complete packets. Assume the ACK is for our last packet. _bank TCPVars mov TCPState,#TCPStateEstablished ; Switch to the established state. ; ## If we were in Syn-receieved then need to send an Ack. test TCPOutstanding snz jmp :noneOutstanding call @AppAck ; Tell the application it was ACKed. _bank TCPVars clr TCPOutstanding ; There should be no data outstanding now. :noneOutstanding ; Check that the length is less than the window. ; We always advertise a window < 256 _bank IPVars test IPRxLengthMSB snz jmp :winOK ; DEBUGP $1D,top jmp @_TCPSendAck ; Window > 256 :winOK ; Does the packet contain data? (Determine this from the length.) _bank IPVars mov IPRxLengthMSB,IPRxLengthLSB ; Save the length. test IPRxLengthLSB snz jmp :noData mov w,IPRxLengthLSB call @AppBytesAvailable ; DEBUGP $1A,top ; Report payload start. :processData call @PhyRxByte ; Receive a byte. ; DEBUGW $19,indent ; Report that byte received. call @AppRxByte ; Process the byte. _bank IPVars ; Check whether to loop again. decsz IPRxLengthLSB jmp :processData inc IPRxLengthLSB :dataDone ; DEBUGP $1B,top ; Report payload end. :noData ; ## Check the PPP FCS ; call @PPPCheckFCS ; sz ; Is the FCS OK? ; jmp :badFCS ; No. Send a debug message. call @TCPAckUpdate ; Send an ACK packet. _bank IPVars test IPRxLengthLSB ; Was Data received? snz jmp :checkFIN ; No. It was an Ack packet. Just return. call @AppPacketOK ; Indicate the packet was OK to the application. _bank TCB mov w,TCPFlags DEBUGW $15,top ; TCP flags. _bank TCPVars snb TCPRxFlags.TCPFlagFIN ; Is the FIN bit set? jmp :doClose ; Yes. Close the connection. jmp @_TCPSendAck :checkFIN _bank TCPVars sb TCPRxFlags.TCPFlagFIN ; Is the FIN bit set? retp ; No, just return. :doClose DEBUGP $28,top mov w,#1 ; Ack the fin. call @TCPAddRCV_NXT _bank TCPVars mov TCPState,#TCPStateLastAck ; Change state. jmp @_TCPSendFin :badFCS call @AppPacketBad DEBUGP $18,top jmp @_IPRxClosePacket ; Don't acknowledge the packet. :gotoClosed DEBUGP $1C,top _bank TCPVars mov TCPState,#TCPStateClosed ; Go to the closed state. jmp @_IPRxClosePacket ; Ignore the incoming packet. :FINWAIT1 _bank TCPVars mov TCPState,#TCPStateFinWait2 ; Continue closing. retp :FINWAIT2 _bank TCPVars mov TCPState,#TCPStateClosed ; Go to the closed state. mov w,#1 call @TCPAddRCV_NXT ; ACK the FIN. jmp @_TCPSendAck :LASTACK ; Ignore the packet ; ## Should check the packet is actually an ACK. mov TCPState,#TCPStateClosed ; Go to the closed state. retp :CLOSED jmp @_TCPSendReset ; We shouldn't receive packets while closed. :LISTEN call @IPRxClosePacket ; Ignore the rest of the incoming packet. _bank TCB snb TCPFlags.TCPFlagRST ; Check for an RST retp ; Ignore a packet with RST. snb TCPFlags.TCPFlagACK ; Check for an ACK jmp @_TCPSendReset ; Bad ACK, send a RST. _bank TCPVars ; Make RCV.NXT = SEG.SEQ+1 mov w,TCPTMP_SEQ4 _bank TCB mov TCPRCV_NXT4,w _bank TCPVars mov w,TCPTMP_SEQ3 _bank TCB mov TCPRCV_NXT3,w _bank TCPVars mov w,TCPTMP_SEQ2 _bank TCB mov TCPRCV_NXT2,w _bank TCPVars mov w,TCPTMP_SEQ1 _bank TCB mov TCPRCV_NXT1,w _bank TCPVars mov w,TCPTMP_SEQ3 ; Initialise the initial segment sequence #. call @TCPSendSynAck _bank TCPVars mov TCPState,#TCPStateSynReceived ; Change state. retp :SYNSENT ; Is the packet an Ack? _bank TCB sb TCPFlags.TCPFlagACK ; Is the ACK bit set? jmp :noAck snb TCPFlags.TCPFlagRST ; Is the reset bit set? jmp :reset sb TCPFlags.TCPFlagSYN ; Is the SYN bit set? jmp :noAck ; No, Ignore the rest of the incoming packet. ; ## Check that SND.UNA <= SEG.ACK <= SND.NXT DEBUGP $20,top _bank TCPVars mov TCPState,#TCPStateEstablished ; The connection is now estabished. _bank IPVars clr IPRxLengthLSB ; Set the length to one. mov IPRxLengthMSB,#1 call @TCPAckUpdate jmp @_TCPSendAck :reset DEBUGP $1E,top mov TCPState,#TCPStateClosed ; Close the TCP. jmp @_IPRxClosePacket ; Ignore the rest of the incoming packet. :noAck ; The peer wants us to raise the precedence. We can't. ; We are not happy about not being Acked. Send a Reset. DEBUGP $1F,top jmp @_TCPSendReset _TCPAckUpdate _bank TCPVars ; Set SND.UNA = SEG.ACK. mov w,TCPTMP_ACK4 _bank TCB mov TCPSND_UNA4,w _bank TCPVars mov w,TCPTMP_ACK3 _bank TCB mov TCPSND_UNA3,w _bank TCPVars mov w,TCPTMP_ACK2 _bank TCB mov TCPSND_UNA2,w _bank TCPVars mov w,TCPTMP_ACK1 _bank TCB mov TCPSND_UNA1,w _bank TCPVars ; Set RCV.NXT = SEG.SEQ mov w,TCPTMP_SEQ4 _bank TCB mov TCPRCV_NXT4,w _bank TCPVars mov w,TCPTMP_SEQ3 _bank TCB mov TCPRCV_NXT3,w _bank TCPVars mov w,TCPTMP_SEQ2 _bank TCB mov TCPRCV_NXT2,w _bank TCPVars mov w,TCPTMP_SEQ1 _bank TCB mov TCPRCV_NXT1,w _bank IPVars mov w,IPRxLengthMSB jmp @_TCPAddRCV_NXT ; Add the length of the received packet to the ACK. ;------------------------------------------------------------------------------- ; Subroutine: 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. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- org $800 _TCPTransmit _bank TCPVars cjae TCPState,#TCPStateSynSent,:ok clrb StatusPort.LED1 retp :ok test TCPOutstanding sz ; Are there any bytes unacknowledged? jmp :timeout ; Yes. Check for timeout. cjae TCPState,#TCPStateFinWait1,:fintimeout ; Check for FIN timeout. :retransmit call @AppBytesToSend ; No. Ask the application if it wants to transmit. test w snz ; Does the application wish to transmit? retp ; No. We're done here. Return. _bank TCPVars mov TCPOutstanding,w ; Save the number of bytes to transmit. _bank IPVars ; Compute the checksum over the data. mov IPLengthLSB,w clr TCPChecksumMSB ; Initialize the checksum. clr TCPChecksumLSB clrb IPFlags.checksumBit ; Indicate the next bit is an MSB. DEBUGW $26,top :csloop call @AppTxByte DEBUGW $2A,indent call @TCPChecksum ; Accumulate the checksum _bank IPVars decsz IPLengthLSB jmp :csloop call @AppNak ; Reset the application. ; Start a TCP packet. _bank TCPVars mov w,TCPOutstanding _bank IPVars mov IPLengthLSB,w clr IPLengthMSB _bank TCB mov TCPFlags,#(1<<TCPFlagPSH)|(1<<TCPFlagACK) call @TCPSendHeader ; Send the header. _bank TCPVars mov w,TCPOutstanding _bank IPVars mov IPLengthLSB,w :dataloop ; Send the packet data. call @AppTxByte call @PhyTxByte ; Send the byte. _bank IPVars decsz IPLengthLSB jmp :dataloop call @TCPClosePacket ; End the packet. _bank PPPTimer ; Initialise the restart timer. clr PPPTimer1 clr PPPTimer2 clr PPPTimer3 retp :fintimeout _bank PPPTimer csae PPPTimer3,#TCPRestartExpire ; Has the restart timer expired? jmp :retransmit clr PPPTimer1 ; Yes. Initialise the restart timer. clr PPPTimer2 clr PPPTimer3 jmp @_TCPSendFin :timeout _bank PPPTimer csae PPPTimer3,#TCPRestartExpire ; Has the restart timer expired? retp ; No clr PPPTimer1 ; Yes. Initialise the restart timer. clr PPPTimer2 clr PPPTimer3 DEBUGP $24,top call @AppNak ; Tell the application. jmp :retransmit ; Transmit the packet again. ;------------------------------------------------------------------------------- ; Subroutine: TCPSendSyn ; ; Send an ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=SYN> ; ; W on entry: A random value to initialise the ISS. ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPSendSynAck _bank TCB mov TCPFlags,#(1<<TCPFlagSYN)|(1<<TCPFlagACK) jmp ISS _TCPSendSyn DEBUGP $22,top _bank TCB mov TCPFlags,#(1<<TCPFlagSYN) ISS mov TCPSND_UNA1,w ; packet to increase randomness. mov TCPSND_UNA2,rtcc mov TCPSND_UNA3,rtcc mov TCPSND_UNA4,rtcc mov w,#1 ; Add 1 to RCV.NXT call @TCPAddRCV_NXT call @TCPSendEmptyHeader ; Send the header. call @PPPClosePacket ; Close the packet. mov w,#1 ; Add 1 to SND.NXT jmp @_TCPAddSND_UNA ;------------------------------------------------------------------------------- ; Subroutine: TCPSendAck ; ; Send an ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPSendAck DEBUGP $23,top ; Copy the acknowledgement number. _bank TCB mov TCPFlags,#(1<<TCPFlagACK) ; An ACK packet. call @TCPSendEmptyHeader ; Send the header. call @PPPClosePacket ; Close the packet. jmp @_IPRxClosePacket ; Ignore the rest of the incoming packet. ;------------------------------------------------------------------------------- ; Subroutine: TCPSendFin ; ; Send a FIN packet ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPSendFin DEBUGP $29,top _bank TCB mov TCPFlags,#(1<<TCPFlagFIN)|(1<<TCPFlagACK) ; A FIN packet. call @TCPSendEmptyHeader ; Send the header. call @PPPClosePacket ; Close the packet. jmp @_IPRxClosePacket ; Ignore the rest of the incoming packet. ;------------------------------------------------------------------------------- ; Subroutine: TCPSendReset ; ; Send a reset packet with <SEQ=SEG.ACK><CTL=RST> ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPSendReset DEBUGP $21,top ; Copy the acknowledgement number. _bank TCB mov TCPFlags,#(1<<TCPFlagRST) ; A reset packet. _bank TCPVars mov w,TCPTMP_ACK4 _bank TCB mov TCPSND_UNA4,w _bank TCPVars mov w,TCPTMP_ACK3 _bank TCB mov TCPSND_UNA3,w _bank TCPVars mov w,TCPTMP_ACK2 _bank TCB mov TCPSND_UNA2,w _bank TCPVars mov w,TCPTMP_ACK1 _bank TCB mov TCPSND_UNA1,w call @TCPSendEmptyHeader ; Send the header. call @PPPClosePacket ; Close the packet. jmp @_IPRxClosePacket ; Ignore the rest of the incoming packet. ;------------------------------------------------------------------------------- ; Subroutine: TCPInitChecksum ; ; Initialize the TCP checksum with the pseudo header fields. ; ; W on entry: - ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- ENDIF IFDEF TCP _TCPInitChecksum _bank IPVars mov w,#IPAddress1 call @TCPChecksum mov w,#IPAddress2 call @TCPChecksum mov w,#IPAddress3 call @TCPChecksum mov w,#IPAddress4 call @TCPChecksum mov w,#0 call @TCPChecksum mov w,#IPProtocolTCP call @TCPChecksum mov w,IPDestAddress1 call @TCPChecksum mov w,IPDestAddress2 call @TCPChecksum mov w,IPDestAddress3 call @TCPChecksum mov w,IPDestAddress4 call @TCPChecksum mov w,IPLengthMSB call @TCPChecksum mov w,IPLengthLSB call @TCPChecksum retp ;------------------------------------------------------------------------------- ; Subroutine: TCPChecksum ; ; Accumulate the TCP checksum. ; ; W on entry: The value of the byte to accumulate. ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPChecksum _bank IPVars sb IPFlags.checksumBit ; Are we processing an MSB? jmp :MSB ; Yes 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 IPFlags,#(1<<checksumBit) ; Flip the checksum bit. retp ;------------------------------------------------------------------------------- ; Subroutine: TCPTxByte ; ; Transmit a TCP byte accumulating the checksum each time. ; ; W on entry: The value of the byte to send. ; W on exit : - ; Variables : - ;------------------------------------------------------------------------------- _TCPTxByte mov Scratch1,w ; Save the byte. call @TCPChecksum mov w,Scratch1 ; Retrieve the saved byte. call @PhyTxByte retp ;=============================================================================== ; 32-bit math subroutines ;=============================================================================== ;------------------------------------------------------------------------------- ; Subroutine: TCPAddRCV_NXT, TCPAddSND_NXT ; ; Add a constant to RCV.NXT or SND.NXT ; ; W on entry: The number to add. ; W on exit : - ; Variables : - ; Bank on exit : - ;------------------------------------------------------------------------------- _TCPAddRCV_NXT _bank TCB add TCPRCV_NXT1,w sc retp incsz TCPRCV_NXT2 retp incsz TCPRCV_NXT3 retp incsz TCPRCV_NXT4 retp _TCPAddSND_UNA _bank TCB add TCPSND_UNA1,w sc retp incsz TCPSND_UNA2 retp incsz TCPSND_UNA3 retp incsz TCPSND_UNA4 retp ENDIF IFDEF POP3DEMO _get_tens clr Scratch2 ;result :2d_again sub Scratch1,#10 sc ; nc=below 100 jmp :2d_next_digit inc Scratch2 jmp :2d_again :2d_next_digit add Scratch1,#10 ; restore from underflow add Scratch2,#'0' retp _get_hundreds clr Scratch2 ;result :3d_again sub Scratch1,#100 sc ; nc=below 100 jmp :3d_next_digit inc Scratch2 jmp :3d_again :3d_next_digit add Scratch1,#100 ; restore from underflow add Scratch2,#'0' retp _times_10 clc rl scratch1 mov scratch2,scratch1 ; store *2 rl scratch1 rl scratch1 add scratch1,scratch2 retp ENDIF org $A00 ;=============================================================================== ; TCP HTTP Demo Functions ;=============================================================================== IFDEF HTTPDEMO ;------------------------------------------------------------------------------- ; EEPROM Routines ; ; These must be in the first half of a page. ;------------------------------------------------------------------------------- E2Start ;SCL=high, SDA=in mov w, #E2SDAOutDDR ;Prepare to make SDA an output clrb E2SDAPin ;When output SDA will be low mov !E2Port, w ;Make SDA an output => low jmp E2DelaySCLHigh ;Delay & return E2Stop ;SCL=high, SDA=in/out clrb E2SCLPin ;Make SCL go low => can change data nop ;3 cycles needed before change DDR mov w, #E2SDAOutDDR ;Prepare to make SDA an output clrb E2SDAPin ;Prepare to output SDA low mov !E2Port, w ;Make SDA an output => low call E2DelaySCLLow ;Delay setb E2SCLPin ;Make SCL go high call E2DelaySCLHigh ;Delay mov w, #E2SDAInDDR ;Prepare to make SDA go high => stop bit mov !E2Port, w ;Release SDA => stop bit call E2DelaySCLHigh ;Delay clz ;Indicate stream closed retp E2WriteToRead ;SCL=high, SDA=in clrb E2SCLPin ;SCL goes low to allow data change call E2DelaySCLLow ;Delay setb E2SCLPin ;Return SCL high call E2DelaySCLHigh ;Delay jmp E2Start E2Write ;SCL=high, SDA=in mov E2DataBits, w ;Store data to be sent mov E2BitCount, #8 ;Send 8 bits :Bit clrb E2SCLPin ;SCL goes low to allow data change rl E2DataBits ;C = bit to send (MSB first) mov w, #E2SDAOutDDR ;Guess bit is a 0 snc ;Should bit be a 1 ? mov w, #E2SDAInDDR ;Yes => Change to 1 clrb E2SDAPin ;SDA low in case of an ouput mov !E2Port, w ;Apply SDA DDR call E2DelaySCLLow ;Delay setb E2SCLPin ;Return SCL high to allow E2 to read data call E2DelaySCLHigh ;Delay decsz E2BitCount ;More bits to send ? jmp :Bit ;Yes => send next bit mov w, #E2SDAInDDR ;Prepare to make SDA go high mov !E2Port, w ;Release SDA pin for ack clrb E2SCLPin ;SCL goes low for ack call E2DelaySCLLow ;Delay setb E2SCLPin ;Return SCL high to read ack call E2DelaySCLHigh ;Delay stz ;Assume a 0 snb E2SDAPin ;Is the data a 1 ? clz ;Yes => change to a 1 retp E2ReadAck ;SCL should be high mov E2BitCount, #8 ;Get 8 bits :Bit clrb E2SCLPin ;SCL goes low to allow data change nop nop mov w, #E2SDAInDDR ;Prepare to make SDA go high mov !E2Port, w ;Release SDA pin to allow data read call E2DelaySCLLow ;Delay setb E2SCLPin ;Return SCL high to allow E2 to read data clc ;Assume a 0 snb E2SDAPin ;Is the data a 1 ? stc ;Yes => change to a 1 rl E2DataBits ;Store new bit (MSB first) call E2DelaySCLHigh ;Delay decsz E2BitCount ;More bits to come ? jmp :Bit ;Yes => get next bit clrb E2SCLPin ;SCL goes low to allow data change for ack nop ;3 cycles needed before change DDR mov w, #E2SDAOutDDR ;Prepare to make SDA go low for ack clrb E2SDAPin ;SDA low when output mov !E2Port, w ;Force SDA pin low call E2DelaySCLLow ;Delay setb E2SCLPin ;Return SCL high. Note SDA still low call E2DelaySCLHigh ;Delay mov w, E2DataBits ;Data to be returned stz ;Indicate ack retp E2ReadNotAck ;SCL should be high mov E2BitCount, #8 ;Get 8 bits of data :Bit clrb E2SCLPin ;SCL goes low to allow data change nop nop mov w, #E2SDAInDDR ;Prepare to make SDA go high mov !E2Port, w ;Release SDA pin to allow data read call E2DelaySCLLow ;Delay setb E2SCLPin ;Return SCL high to allow E2 to read data clc ;Assume a 0 snb E2SDAPin ;Is the data a 1 ? stc ;Yes => change to a 1 rl E2DataBits ;Store new bit (MSB first) call E2DelaySCLHigh ;Delay decsz E2BitCount ;More bits to come ? jmp :Bit ;Yes => get next bit clrb E2SCLPin ;SCL goes low to allow data change for ack call E2DelaySCLLow ;Leave SDA high for not ack, Delay setb E2SCLPin ;Return SCL high. call E2DelaySCLHigh ;Delay mov w, E2DataBits ;Data to be returned clz ;Indicate not ack retp E2DelaySCLLow ;1300ns minimum = 65 instructions mov E2Delay, #15 ;Loop 15 times (4 cycles per loop + 8 for overhead) :Loop decsz E2Delay ;Delay complete ? jmp :Loop ;No => loop again retp E2DelaySCLHigh ;600ns minimum = 30 instructions mov E2Delay, #6 ;Loop 6 times (4 cycles per loop + 8 for overhead) :Loop decsz E2Delay ;Delay complete ? jmp :Loop ;No => loop again retp ; ============================================================================== ; Mode must be DDR E2WriteStart ;Returns Z=true for ready _bank E2Bank mov w,#$1f mov m,w call E2Start ;Send start bit mov w, #E2DeviceWR call E2Write ;Output device code sz ;Ack ? jmp E2Stop ;No => not ready, send stop bit + return z=false mov w, E2AddrH call E2Write ;Output AddrH mov w, E2AddrL call E2Write ;Output AddrL sz ;Ack ? jmp E2Stop ;No => not ready, send stop bit + return z=false retp E2WriteData ;W = data, returns Z=true for ack _bank E2Bank jmp E2Write E2WriteComplete ;Complete write process _bank E2Bank jmp E2Stop E2ReadStart ;Returns Z=true for ack _bank E2Bank mov w,#$1f mov m,w call E2Start ;Send start bit mov w, #E2DeviceWR call E2Write ;Output device code sz ;Ack ? jmp E2Stop ;No => not ready, send stop bit + return z=false mov w, E2AddrH call E2Write ;Output AddrH mov w, E2AddrL call E2Write ;Output AddrL call E2WriteToRead ;Send start bit mov w, #E2DeviceRD call E2Write ;Output sz ;Ack ? jmp E2Stop ;No => not ready, send stop bit + return z=false retp E2ReadData ;Returns W=data _bank E2Bank jmp E2ReadAck ;Read byte E2ReadComplete ;Returns W=data DEBUGP $62,top _bank E2Bank call E2ReadNotAck ;Read byte call E2Stop ;Send stop bit mov w, E2DataBits ;Data to be returned retp ; ============================================================================== E2OpenFile ;w = file reference, returns z=true for open _bank E2Bank mov E2AddrL, w ;AddrL = reference clr E2AddrH ;AddrH = 0 clc ;Prepare to multiply by 2 rl E2AddrL ;Multiply by 2 rl E2AddrH ;Multiply by 2 call E2ReadStart ;Prepare to read sz ;Ack ? retp ;No => return error (z=false) call E2ReadData ;Read MSB of address mov E2AddrH, w ;AddrH = MSB call E2ReadComplete ;Read LSB of address mov E2AddrL, w ;AddrL = LSB call E2ReadStart ;Prepare to read sz ;Ack ? retp ;No => return error (z=false) call E2ReadData ;Read 1st byte mov E2FileSizeH, w ;1st byte = FilesizeH call E2ReadData ;Read 2nd byte mov E2FileSizeL, w ;2nd byte = FilesizeL call E2ReadData ;Read 3rd byte mov E2FileChecksumH, w ;3rd byte = ChecksumH call E2ReadData ;Read 4th byte mov E2FileChecksumL, w ;4th byte = ChecksumL retp ;Return ready to read 1st real data byte E2CloseFile = E2ReadComplete ;Should not be used unless closing file piror to reaching end E2ReadFile ;Returns Z=More (=false for last byte) mov w,#$1f mov m,w _bank E2Bank decsz E2FilesizeL ;More ? jmp E2ReadData ;Yes => read data test E2FilesizeH ;No => check high snz ;More ? jmp E2ReadComplete ;No => read last byte and close file dec E2FilesizeH ;Yes => decrement counter jmp E2ReadData ; read data ; ============================================================================== ;------------------------------------------------------------------------------- ; Subroutine: AppInit ; ; Called once at startup to allow the application to initialise itself. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppInit DEBUGP $50,top _bank TCB mov TCPLocalPortl,#HTTPPortl mov TCPLocalPorth,#HTTPPorth _bank HTTPVars clr HTTPParseState ; Initialise all variables. clr HTTPURIHash clr HTTPDone clr HTTPLengthMSB clr HTTPLengthLSB jmp @_TCPPassiveOpen ; Connect to the remote TCP. ;------------------------------------------------------------------------------- ; Subroutine: AppBytesToSend ; ; Called before transmitting a TCP packet to see if the application has any ; data it wishes to send. ; ; The way that data is segmented is a little unorthodox. The maximum transmit ; packet size is 256 bytes. The first packet contains length%256 bytes. Each ; subsequent packet contains 256 bytes until all data is sent. ; ; W on entry: - ; W on exit : The number of bytes the application wishes to transmit. ;------------------------------------------------------------------------------- _AppBytesToSend _bank HTTPVars clr E2FileSizeH test HTTPLengthMSB snz jmp :msbzero mov w,#$ff mov E2FileSizeL,w retp :msbzero mov E2FileSizeL,HTTPLengthLSB test E2FileSizeL sz retp sb HTTPDone.0 retp clr HTTPDone _bank TCPVars cse TCPState,#TCPStateEstablished retp jmp @_TCPClose ;------------------------------------------------------------------------------- ; Subroutine: AppPacketOK ; ; The last packet received passed the CRC check. At this point the packet data ; may be acted upon. The application should not transmit a reply packet, but ; should wait for AppBytesToSend to be called. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppPacketOK DEBUGP $57,top _bank HTTPVars csae HTTPParseState,#HTTPParse4 ; Check that the parse state is correct. retp ; Read the pointer from the FAT. mov w,HTTPParseState DEBUGW $65,indent _bank HTTPVars clr HTTPParseState ; mov w,#$2a ; Hardcoded hash of /index.html mov w,HTTPURIHash DEBUGW $64,indent mov Scratch0,w :retry call @E2OpenFile sz ; z indicates file found. jmp :404 mov HTTPLengthMSB,E2FileSizeH mov HTTPLengthLSB,E2FileSizeL add E2AddrL,#4 ; Skip over length and checksum next time. snc inc E2AddrH retp :404 ;Indicate 404 error. mov w,Scratch0 DEBUGW $60,top mov Scratch0,#HTTP404Hash ; Hash of the 404 page jmp :retry retp ;------------------------------------------------------------------------------- ; Subroutine: AppPacketBad ; ; The last packet received did not pass the CRC check. The recieve counters ; should be reset and any actions undone since the packet will be received again. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppPacketBad _bank HTTPVars clr HTTPParseState retp ;------------------------------------------------------------------------------- ; Subroutine: AppBytesAvailable ; ; Indicator to the application that a packet has been received and that AppRxByte ; is about to be called 'w' times. ; ; W on entry: The number of bytes received ; W on exit : - ;------------------------------------------------------------------------------- _AppBytesAvailable _bank HTTPVars clr HTTPDone retp ;------------------------------------------------------------------------------- ; Subroutine: AppTxByte ; ; This routine is called once for each byte the application has says it wishes ; to transmit. The application must be prepared to transmit the same sequence ; of bytes again if it receives the AppNak call. ; ; W on entry: - ; W on exit : The bext byte to transmit. ;------------------------------------------------------------------------------- _AppTxByte _bank HTTPVars call @E2ReadFile retp ;------------------------------------------------------------------------------- ; Subroutine: AppRxByte ; ; Called once for each byte received in a packet. The application should not ; take any irreversible action until either AppPacketOK or AppPacketBad are ; called to indicate the packet CRC was OK. If the CRC is not OK then the same ; sequence of bytes will be received again when the packet is retransmitted by ; the remote host. ; ; W on entry: The byte received ; W on exit : - ;------------------------------------------------------------------------------- _AppRxByte _bank HTTPVars mov Scratch0,w ; Save the received byte. mov w,HTTPParseState DEBUGW $65,indent _bank HTTPVars cje Scratch0,#' ',:nextState cje HTTPParseState,#HTTPParseMethod,:method cje HTTPParseState,#HTTPParseURI,:uri jmp :findEnd :method mov HTTPMethod,Scratch0 ; Save the method. retp :uri add HTTPURIHash,Scratch0 ; Add the byte to the hash retp :nextState csae HTTPParseState,#HTTPParseVersion inc HTTPParseState :reset cjb HTTPParseState,#HTTPParseVersion,:done mov HTTPParseState,#HTTPParse1 :done retp :findEnd cja Scratch0,#CharCR,:reset inc HTTPParseState retp ;------------------------------------------------------------------------------- ; Subroutine: AppNak ; ; The last packet transmitted was rejected by the remote host, or not ; acknowledged. It needs to be retransmitted. This is a signal to the application ; to reset its transmit counters. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppNak DEBUGP $53,top _bank TCPVars mov w,TCPOutstanding _bank HTTPVars mov E2FileSizeL,w call E2ReadStart ;Prepare to read snz ;Ack ? retp DEBUGP $61,top retp ;------------------------------------------------------------------------------- ; Subroutine: AppAck ; ; The last packet transmitted was acknowledged by the remote host. It will not ; need to be retransmitted. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppAck DEBUGP $54,top _bank TCPVars mov w,TCPOutstanding _bank HTTPVars add E2AddrL,w snc inc E2AddrH sub HTTPLengthLSB,w sc dec HTTPLengthMSB test HTTPLengthLSB ; Check if the whole document has been sent. sz jmp :continue test HTTPLengthMSB sz jmp :continue setb HTTPDone.0 ; Signal that we are done. retp :continue call E2ReadStart ; Prepare to read. retp ENDIF ;=============================================================================== ; TCP SMTP Demo Functions ;=============================================================================== IFDEF SMTPDEMO ;------------------------------------------------------------------------------- ; Subroutine: AppPacketOK ; ; The last packet received passed the CRC check. At this point the packet data ; may be acted upon. The application should not transmit a reply packet, but ; should wait for AppBytesToSend to be called. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _SMTPCannedPackets = $ _SMTPHELO dw 'HELO scenix.com',CharCR,CharLF + $f00 _SMTPMAIL dw 'MAIL FROM:<dummy@demo.sx>',CharCR,CharLF + $f00 _SMTPRCPT dw 'RCPT TO:<joe@demo.sx>',CharCR,CharLF + $f00 _SMTPDATA dw 'DATA',CharCR,CharLF + $f00 _SMTPMESG dw 'From: SX',CharCR,CharLF dw 'To: Joe',CharCR,CharLF dw 'Subject: Warning!',CharCR,CharLF,CharCR,CharLF dw 'Over-temperature condition in router.',CharCR,CharLF,'.',CharCR,CharLF + $f00 _SMTPQUIT dw 'QUIT',CharCR,CharLF + $f00 _SMTPNONE dw $f00 _AppPacketOK DEBUGP $57,top _bank SMTPVars mov w,SMTPState DEBUGW $31,top ; SMTP state. _bank SMTPVars ; If the high nibble of the command is not 2 or 3 then an error occured. Abort. mov Scratch0,SMTPCommand and Scratch0,#%00100000 sz jmp :continue ; An error occurred. Close the TCP connection. mov w,SMTPCommand DEBUGW $30,top jmp @_TCPClose :continue inc SMTPState ; Increment the state to indicate the next step is possible. csne SMTPState,#SMTPStateQuit test SMTPState ; Load the pointer for the next state. mov Scratch0,SMTPState clc rl Scratch0 ; Multiply the state by 2. mov w,Scratch0 jmp pc+w ; Jump based on the SMTP state. mov w,#_SMTPNONE jmp :done mov w,#_SMTPHELO jmp :done mov w,#_SMTPNONE jmp :done mov w,#_SMTPMAIL jmp :done mov w,#_SMTPNONE jmp :done mov w,#_SMTPRCPT jmp :done mov w,#_SMTPNONE jmp :done mov w,#_SMTPDATA jmp :done mov w,#_SMTPNONE jmp :done mov w,#_SMTPMESG jmp :done mov w,#_SMTPNONE jmp :done mov w,#_SMTPQUIT jmp :done :done mov SMTPTxPointer,w clr SMTPRxCount clr SMTPTxCount ; Reset the count. mov w,SMTPState DEBUGW $31,top ; SMTP state. retp ;------------------------------------------------------------------------------- ; Subroutine: AppInit ; ; Called once at startup to allow the application to initialise itself. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppInit DEBUGP $50,top _bank SMTPVars mov SMTPState,#SMTPStateClosed ; Start in the closed state. clr SMTPRxCount clr SMTPTxCount mov SMTPTxPointer,#_SMTPNONE clr SMTPEOL _bank IPVars mov IPDestAddress1,#SMTPAddress1 ; SMTP server IP address. mov IPDestAddress2,#SMTPAddress2 mov IPDestAddress3,#SMTPAddress3 mov IPDestAddress4,#SMTPAddress4 ; Randomly choose our own port. _bank PPPTimer mov w,PPPTimer2 ; Base it on a timer. _bank TCB mov TCPLocalPortl,w mov TCPLocalPorth,#$36 mov TCPRemotePorth,#SMTPPorth ; Set the port to connect to. mov TCPRemotePortl,#SMTPPortl jmp @_TCPActiveOpen ; Connect to the remote TCP. ;------------------------------------------------------------------------------- ; Subroutine: AppBytesToSend ; ; Called before transmitting a TCP packet to see if the application has any ; data it wishes to send. ; ; W on entry: - ; W on exit : The number of bytes the application wishes to transmit. ;------------------------------------------------------------------------------- _AppBytesToSend ; Compute the number of bytes we have to transmit. clr Scratch1 _bank SMTPVars csne SMTPState,#SMTPStateMesgAck test SMTPState cjne SMTPTxPointer,#_SMTPNONE,:count mov w,#0 retp :count mov Scratch0,SMTPTxPointer ; Save the start address. mov w,#0 mov m,w :loop mov m, #(_SMTPCannedPackets>>8) ; Load the mode register. mov w,Scratch0 ; Load the pointer iread ; Read the next byte. inc Scratch1 mov w,m ; Load the mode register. test w sz ; If it is not zero then exit. jmp :done ; We're done counting. inc Scratch0 ; Increment the pointer jmp :loop :done _bank SMTPVars mov w,SMTPState DEBUGW $31,top mov w,Scratch1 retp ;------------------------------------------------------------------------------- ; Subroutine: AppBytesAvailable ; ; Indicator to the application that a packet has been received and that AppRxByte ; is about to be called 'w' times. ; ; W on entry: The number of bytes received ; W on exit : - ;------------------------------------------------------------------------------- _AppBytesAvailable DEBUGW $52,top retp ;------------------------------------------------------------------------------- ; Subroutine: AppTxByte ; ; This routine is called once for each byte the application has says it wishes ; to transmit. The application must be prepared to transmit the same sequence ; of bytes again if it receives the AppNak call. ; ; W on entry: - ; W on exit : The bext byte to transmit. ;------------------------------------------------------------------------------- _AppTxByte DEBUGP $55,indent _bank SMTPVars mov w,#0 mov m,w mov m, #(_SMTPCannedPackets>>8) ; Load the mode register. mov w,SMTPTxPointer ; Load start address. add w,SMTPTxCount ; Offset by the current count. iread ; Read the next byte. inc SMTPTxCount retp ;------------------------------------------------------------------------------- ; Subroutine: AppRxByte ; ; Called once for each byte received in a packet. The application should not ; take any irreversible action until either AppPacketOK or AppPacketBad are ; called to indicate the packet CRC was OK. If the CRC is not OK then the same ; sequence of bytes will be received again when the packet is retransmitted by ; the remote host. ; ; W on entry: The byte received ; W on exit : - ;------------------------------------------------------------------------------- _AppRxByte DEBUGP $56,indent mov Scratch2,w ; Save the received byte. _bank SMTPVars mov w,SMTPRxCount DEBUGW $32,indent _bank SMTPVars mov w,SMTPCommand DEBUGW $33,indent ; Process the recieved byte. _bank SMTPVars cjae SMTPRxCount,#2,:processByte ; The first two bytes received indicate the status sub Scratch2,#'0' ; Turn the character into a number. test SMTPRxCount sz jmp :one mov w,<>Scratch2 ; Move the first part of the command into the high nibble. mov SMTPCommand,w ; Save the command. jmp :done :one or SMTPCommand,Scratch2 ; Move the second part of the command into the low nibble. jmp :done :processByte ; If the byte is a linefeed the command is over. cje Scratch2,#CharLF,:linefeed :done inc SMTPRxCount test SMTPRxCount ; Watch out for the count wrapping around. mov w,#2 snz mov SMTPRxCount,w retp :linefeed inc SMTPEOL ; Indicate a linefeed was detected. retp ;------------------------------------------------------------------------------- ; Subroutine: AppNak ; ; The last packet transmitted was rejected by the remote host, or not ; acknowledged. It needs to be retransmitted. This is a signal to the application ; to reset its transmit counters. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppNak DEBUGP $53,top _bank SMTPVars clr SMTPTxCount retp ;------------------------------------------------------------------------------- ; Subroutine: AppAck ; ; The last packet transmitted was acknowledged by the remote host. It will not ; need to be retransmitted. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppAck DEBUGP $54,top _bank SMTPVars inc SMTPState mov SMTPTxPointer,#_SMTPNONE retp ;------------------------------------------------------------------------------- ; Subroutine: AppPacketBad ; ; The last packet received did not pass the CRC check. The recieve counters ; should be reset and any actions undone since the packet will be received again. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppPacketBad DEBUGP $58,top _bank SMTPVars clr SMTPRxCount retp ENDIF ;=============================================================================== ; TCP POP3 Demo Functions ;=============================================================================== IFDEF POP3DEMO ;------------------------------------------------------------------------------- ; Subroutine: AppPacketOK ; ; The last packet received passed the CRC check. At this point the packet data ; may be acted upon. The application should not transmit a reply packet, but ; should wait for AppBytesToSend to be called. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppPacketOK DEBUGP $57,top _bank POP3Vars mov w,POP3State DEBUGW $31,top ; POP3 state. _bank POP3Vars ; if the response is not a '+' then error occurs (except when receiving message body) csne POP3State,#POP3StateMsg jmp :continue csne POP3Command1,#'+' jmp :continue ; mov Scratch0,POP3Command ; and Scratch0,#%00100000 ; sz ; jmp :continue ; An error occurred. Close the TCP connection. mov w,POP3Command1 DEBUGW $30,top jmp @_TCPClose ; state diagram ; Sx POP3 server ; closed (0)--> send Username (1) ; +ok <------- UserAck (2) ; send Password (3) ; +ok <------- PassAck (4) ; send Stat (5) ; +ok num_msg <- StatAck (6) ;loop num_msg times in the following states with i=1 to num_msg ; send Retr i (7) ; +ok <- RetrAck (8) ; multi-line msg (transmit to UART) <- (9) ; terminated by . <- ; send dele i (10) ; +ok <- deleAck (11) ; goto state 7 ; send quit (12) ; +ok <- quitack (13) ; finished (14) :continue ; inc POP3State ; Increment the state to indicate the next step is possible. ; csne POP3State,#POP3StateQuit ; test POP3State ; Load the pointer for the current state. mov Scratch0,POP3State clc rl Scratch0 ; Multiply the state by 2. mov w,Scratch0 jmp pc+w ; Jump based on the POP3 state. ; st 0->1 mov w,#_POP3USER ; current state= closed, received +ok jmp :inc_state ; inc the state, next state= send user name (1) ; st 1->2 mov w,#_POP3NONE ; Username acknowledged here, dummy state jmp :inc_state ; will never visit here, since the ACK of user will inc state in AppAck routine ;recevied +ok after userAck ; st 2->3 mov w,#_POP3PASS jmp :inc_state ; st 3->4 mov w,#_POP3NONE ; password acknowledged here, dummy state jmp :inc_state ; will never visit here, since the ACK of password will inc state in AppAck routine ; st 4->5 ;recevied +ok after PassAck mov w,#_POP3STAT ; send STAT request jmp :inc_state ; st 5->6 mov w,#_POP3NONE ; stat acknowledged here, dummy state jmp :inc_state ; will never visit here, since the ACK of password will inc state in AppAck routine ;recevied +ok N M here after StatAck, where N is number of messages, M is the number of bytes ; limit to 9 messages only, thus only '+ok n' will be stored ; st 6->7 or 6->12 mov w,#_POP3RETR jmp :stat_decide ; st 7->8 mov w,#_POP3NONE ; Retr acknowledged here, dummy state jmp :inc_state ; will never visit here, since the ACK of RETR will inc state in AppAck routine ; st 8->9, +ok, then multiple line message mov w,#_POP3NONE jmp :inc_state ; nothing to send, just change state and wait for . to end, switch to dele state ; st 9 = message state, received packet okay, if msg end flag is set then, change to dele state mov w,#_POP3NONE jmp :msg_decide ; st 10 = delete state mov w,#_POP3NONE jmp :done ; st 11 = delete ack, +ok, retrieve next message or quit if no more mov w,#_POP3RETR jmp :deleAck_decide ; st 12 quit mov w,#_POP3NONE jmp :done ; st 13 quit ack, +ok received mov w,#_POP3NONE jmp :done ;st 14 finished jmp :done :deleAck_decide _bank POP3MoreVars mov w,POP3RxMsgNo _bank POP3Vars mov w,POP3TxMsgNo-w snz jmp :quit_state inc POP3TxMsgNo ; prepare the digits for the next retr command mov Scratch1,POP3TxMsgNo _bank POP3MoreVars clr POP3TxMsgDigit1 clr POP3TxMsgDigit2 clr POP3TxMsgDigit3 cjb Scratch1,#10,:single_digit cjb Scratch1,#100,:double_digit ; 3 digits call @get_hundreds ;quotient in scratch2,remainder in scratch1 mov POP3TxMsgDigit1,Scratch2 call @get_tens mov POP3TxMsgDigit2,Scratch2 add Scratch1,#'0' mov POP3TxMsgDigit3,Scratch1 jmp :retrieve :double_digit call @get_tens mov POP3TxMsgDigit1,Scratch2 add Scratch1,#'0' mov POP3TxMsgDigit2,Scratch1 jmp :retrieve :single_digit add Scratch1,#'0' mov POP3TxMsgDigit1,Scratch1 :retrieve _bank POP3Vars ; go to retr state mov POP3State,#POP3StateRetr mov w,#_POP3RETR jmp :done :msg_decide sb POP3MsgEndFlag.1 jmp :packet_done ; not yet end, remain in message state for multiple line message ; message ended with a . ; change to dele state clr POP3MsgEndFlag ; prepare for next message clr POP3MsgSubSt mov w,#_POP3DELE jmp :inc_state :stat_decide ; if number of message is 0, then next state is quit ; else retr message 1 to n, where n is in POP3Command5 csne POP3Command5,#'0' jmp :quit_state ; 0 messages, next state=quit ; message no. <>0, next state = retr ; determine no. of messages to retrieve mov scratch1,w ; POP3Command5-w is in w now cje POP3Command6,#' ', :store_msg_no ; single digit? sub POP3Command6,#'0' ;multiply the previous digit by 10=digit*8+*2 call @times_10 ; scratch1 x 10 add scratch1,POP3Command6 cje POP3Command7,#' ', :store_msg_no ; double digit? sub POP3Command7,#'0' ;multiply the previous digit by 10=digit*8+*2 call @times_10 ; scratch1 x 10 add scratch1,POP3Command7 :store_msg_no _bank POP3MoreVars mov POP3RxMsgNo,scratch1 ; =total no. of messages to retrieve mov POP3TxMsgDigit1,#'1' clr POP3TxMsgDigit2 clr POP3TxMsgDigit3 _bank POP3Vars mov POP3TxMsgNo,#1 clr POP3MsgEndFlag mov w,#_POP3RETR ; limit will be check in AppTxByte jmp :inc_state ; next stat=retrieve :packet_done mov POP3TxPointer,w mov POP3MsgSubStLast,POP3MsgSubSt ; store last sub state jmp :done2 :quit_state mov POP3State,#POP3StateQuit ; zero messages, next state=quit mov w,#_POP3QUIT jmp :done :inc_state inc POP3State ; Increment the state to indicate the next step is possible. :done mov POP3TxPointer,w :done2 clr POP3RxCount clr POP3TxCount ; Reset the count. ; clr POP3MsgSubSt ;start of Msg sub state IFDEF POP3DEBUG mov w,POP3State mov DebugScratch1,w add DebugScratch1,#'0' mov w,DebugScratch1 call @DebugSendByte ENDIF retp ;------------------------------------------------------------------------------- ; Subroutine: AppRxByte ; ; Called once for each byte received in a packet. The application should not ; take any irreversible action until either AppPacketOK or AppPacketBad are ; called to indicate the packet CRC was OK. If the CRC is not OK then the same ; sequence of bytes will be received again when the packet is retransmitted by ; the remote host. ; ; W on entry: The byte received ; W on exit : - ;------------------------------------------------------------------------------- _AppRxByte ; POP3DW mov Scratch2,w ; Save the received byte. _bank POP3Vars mov w,POP3RxCount DEBUGW $32,indent _bank POP3Vars cje POP3State,#POP3StateMsg,:message_state ; if message state, scan for <LF>.<CR> combination cjae POP3RxCount,#7,:processByte ; The first byte received indicate the status +=ok -=error or +ok n (no. of messages) mov fsr,#POP3Command1 add fsr,POP3RxCount mov ind,Scratch2 jmp :done :processByte ; skip until end of response ; If the byte is a linefeed the command is over. cje Scratch2,#CharLF,:linefeed :done inc POP3RxCount test POP3RxCount ; Watch out for the count wrapping around. mov w,#5 snz mov POP3RxCount,w retp :linefeed inc POP3EOL ; Indicate a linefeed was detected. retp :message_state cjne POP3RxCount,#0,:skip_loading mov POP3MsgSubSt,POP3MsgSubStLast :skip_loading mov w,POP3MsgSubSt jmp pc+w jmp :Start jmp :CR_detected jmp :CR_LF_detected jmp :CR_LF_dot_detected jmp :CR_LF_dot_CR_detected jmp :m_start :Start cjne Scratch2,#CharCR,:m_done :m_next_state inc POP3MsgSubSt ; 1 CR detected jmp :m_done :CR_detected cjne Scratch2,#CharLF,:m_start jmp :m_next_state :CR_LF_detected cje Scratch2,#'.',:m_next_state_no_show cjne Scratch2,#CharCR,:m_start ;<CR><LF><CR> detected mov POP3MsgSubSt,#POP3MsgSubSt1CR jmp :m_done :CR_LF_dot_detected cje Scratch2,#'.',:byte_stuffed ; byte-stuff . , display it cjne Scratch2,#CharCR,:m_start jmp :m_next_state_no_show :CR_LF_dot_CR_detected cjne Scratch2,#CharLF,:m_start setb POP3MsgEndFlag.1 ; LF+.+CR jmp :m_ok :m_next_state_no_show inc POP3MsgSubSt jmp :m_ok :m_done ;send data to debug port from this line mov w,Scratch2 POP3W :m_ok inc POP3RxCount mov Scratch1,POP3MsgSubSt add Scratch1,#'!' mov w,Scratch1 POP3DW retp :byte_stuffed :m_start ; change to start state clr POP3MsgSubSt jmp :m_done ;------------------------------------------------------------------------------- ; Subroutine: AppBytesToSend ; ; Called before transmitting a TCP packet to see if the application has any ; data it wishes to send. ; ; W on entry: - ; W on exit : The number of bytes the application wishes to transmit. ;------------------------------------------------------------------------------- _AppBytesToSend ; Compute the number of bytes we have to transmit. clr Scratch1 _bank POP3Vars cjne POP3TxPointer,#_POP3NONE,:count mov w,#0 retp :count mov Scratch0,POP3TxPointer ; Save the start address. mov w,#0 ; ## Compensate for SX52 mode bug. mov m,w ; ## :loop mov m, #(_POP3CannedPackets>>8) ; Load the mode register. mov w,Scratch0 ; Load the pointer iread ; Read the next byte. inc Scratch1 test w snz jmp :check mov w,m ; Load the mode register. test w sz ; If it is not zero then exit. jmp :done ; We're done counting. inc Scratch0 ; Increment the pointer jmp :loop :done _bank POP3Vars mov w,POP3State DEBUGW $31,top mov w,Scratch1 retp :check ; d1 d2 d3 -> +2 ; d1 d2 0 -> +1 ; d1 0 0 -> no change _bank POP3MoreVars mov w,POP3TxMsgDigit2 test w snz jmp :no_change inc Scratch1 mov w,POP3TxMsgDigit3 test w snz jmp :no_change inc Scratch1 :no_change _bank POP3Vars ;compensate for 0d,0a inc Scratch1 inc Scratch1 jmp :done ;------------------------------------------------------------------------------- ; Subroutine: AppBytesAvailable ; ; Indicator to the application that a packet has been received and that AppRxByte ; is about to be called 'w' times. ; ; W on entry: The number of bytes received ; W on exit : - ;------------------------------------------------------------------------------- _AppBytesAvailable DEBUGW $52,top retp ;------------------------------------------------------------------------------- ; Subroutine: AppTxByte ; ; This routine is called once for each byte the application has says it wishes ; to transmit. The application must be prepared to transmit the same sequence ; of bytes again if it receives the AppNak call. ; ; W on entry: - ; W on exit : The bext byte to transmit. ;------------------------------------------------------------------------------- ; due to the fact that retr and dele has a variable, all message in those 2 states will be transmitted differently ; in the way as follows: ; if it is zero, replaced with the content of POP3TxMsgNo _AppTxByte _bank POP3Vars mov w,#0 ; ## Compensate for SX52 mode bug. mov m,w ; ## mov m, #(_POP3CannedPackets>>8) ; Load the mode register. mov w,POP3TxPointer ; Load start address. add w,POP3TxCount ; Offset by the current count. iread ; Read the next byte. test w sz ; =0 then, replace with POP3TxMsgNo jmp :done ;replace mov Scratch2,POP3TxCount _bank POP3MoreVars mov fsr,#POP3MoreVars mov w,Scratch2 add fsr,w mov w,ind test w sz jmp :to_done ;skip to <CR><LF> _bank POP3Vars mov POP3TxCount,#8 ; move pointer to <CR> mov w,#CharCR :to_done _bank POP3Vars :done POP3DW inc POP3TxCount retp ENDIF ;=============================================================================== ; Application code ;=============================================================================== org $d00 _ResetVector mov !option,#%11001000 ; Disable RTCC rollover. mov StatusPort,#0 ; Initialize status LEDs. ; mov !StatusPort,#0 ; Set RA in/out directions for status LEDs. mov !re,#0 ; Set RA in/out directions for status LEDs. call @SerialInit ; Initialize the UART. call @PPPInit ; Initialize the PPP layer. IFDEF HTTPDEMO mov re, #E2PortInit mov !re, #E2SDAInDDR _bank E2Bank clr E2AddrH clr E2AddrL ENDIF IFDEF ADCDEMO clr rb ;Ensure rb output pins startup low mov !rb, #%01000000 _bank ADCVars clr adcCount clr adcAcc clr adcwarning ENDIF IFDEF JAVADEMO _bank JavaVars mov lawnOn,#sprinklerOn mov pathTime,#sprinkler45 ENDIF ; Application code starts here: ; Start the PPP connection :Start IFDEF WIN32 call @ModemConnect ; Pretend we are a modem. ENDIF call @PPPOpen sb PPPFlags.linkUp ; Is the link up? jmp :Start ; No. Try opening it again. ; Initialise the TCP IFDEF TCP _bank TCPVars clr TCPOutstanding mov TCPState,#TCPStateClosed ENDIF IFDEF SMTPDEMO _bank SMTPVars mov SMTPState,#SMTPStateClosed mov SMTPTxPointer,#_SMTPNONE ENDIF IFDEF POP3DEMO _bank POP3Vars mov POP3State,#POP3StateClosed mov POP3TxPointer,#_POP3NONE ENDIF IFDEF HTTPDEMO call @AppInit ENDIF IFDEF POP3DEMO call @AppInit ENDIF :loop IFDEF HTTPDEMO _bank TCPVars csne TCPState,#TCPStateClosed call @AppInit ; Ensure we continue to listen. ENDIF IFDEF TCP call @TCPTransmit ENDIF call @IPReceivePacket _bank IPVars IFDEF TCP snb IPFlags.TCPPacket ; Is the packet TCP? jmp :TCPRx ; Yes, process it. ENDIF IFDEF SMTPDEMO IFNDEF ADCDEMO snb IPFlags.ICMPPacket ; Is the packet ICMP? jmp :SendMail ; If we get pinged, send an email message. ENDIF ENDIF IFDEF JAVADEMO snb IPFlags.UDPPacket ; Is the packet UDP? jmp :UDPRx ; Yes, process it. ENDIF IFDEF UDPDEMO snb IPFlags.UDPPacket ; Is the packet UDP? jmp :UDPRx ; Yes, process it. ENDIF IFDEF ADCDEMO _bank ADCVars csa adcValue, #256/7 * 4 clr ADCWarning csbe adcValue, #256/7 * 4 call @ADCSendWarning ENDIF _bank IPVars snb IPFlags.anyPacket ; Was a packet received? call @IPRxClosePacket ; Yes, make sure it is cleaned up. _bank PPPVars snb PPPFlags.linkUp ; Check the link is still up. jmp :loop jmp :Start ; If the link is reset then start again. call @PPPClose ; Close the link. :done jmp :done IFDEF TCP :TCPRx call @TCPProcessPacket ; Handle a TCP packet. ENDIF jmp :loop IFDEF SMTPDEMO :SendMail call @AppInit jmp :loop ENDIF IFDEF UDPDEMO :UDPRx call @UDPRxHeader ; Receive the UDP header. _bank UDPVars cse UDPSrcPorth,#(DemoPort&$ff00)>>8 ; Check the port number. jmp :gobble cse UDPSrcPortl,#DemoPort&$00ff jmp :gobble ; OK the packet is for the right port. call @IPRxData mov Scratch0,w cje Scratch0,#DemoMemDump,:memdump ; Is the command a memory dump? cje Scratch0,#DemoMemSet,:set ; Is the command a set memory? cje Scratch0,#DemoMemGet,:get ; Is the command a get memory? :gobble DEBUGP $80,0 call @IPRxClosePacket jmp :loop :memdump ; Dump all of the memory in a 192 byte structure. _bank IPVars clr IPLengthMSB mov IPLengthLSB,#192 mov IPDestAddress1,IPSrcAddress1 ; Copy the address of the sender mov IPDestAddress2,IPSrcAddress2 mov IPDestAddress3,IPSrcAddress3 mov IPDestAddress4,IPSrcAddress4 call @UDPStartPacket ; Start the reply packet. clr fsr :all_ram sb fsr.4 setb fsr.3 mov Scratch0,fsr ; Save the FSR mov w,ind call @IPTxData mov fsr,Scratch0 ; Restore the FSR ijnz fsr,:all_ram call @PPPClosePacket jmp :gobble :set ; Read the next byte to get an address and set that address to ; the following byte. call @IPRxData mov Scratch0,w ; Save the address. call @IPRxData mov Scratch1,w ; Save the data. mov FSR,Scratch0 ; Use indirect addressing. mov IND,Scratch1 ; Set the register. jmp :gobble :get ; The next byte contains an address. Reply with a packet containing ; the byte at that address. _bank IPVars clr IPLengthMSB mov IPLengthLSB,#1 mov IPDestAddress1,IPSrcAddress1 ; Copy the address of the sender mov IPDestAddress2,IPSrcAddress2 mov IPDestAddress3,IPSrcAddress3 mov IPDestAddress4,IPSrcAddress4 call @UDPStartPacket ; Start the reply packet. call @IPRxData ; Read the address. mov FSR,w ; Use indirect addressing. mov w,IND ; Load the byte. call @IPTxData ; Transmit it. call @PPPClosePacket ; Finish the packet. jmp :gobble ENDIF IFDEF JAVADEMO :UDPRx call @UDPRxHeader ; Receive the UDP header. _bank UDPVars cse UDPSrcPorth,#(sprinklerPort&$ff00)>>8 ; Check the port number. jmp :gobble cse UDPSrcPortl,#sprinklerPort&$00ff jmp :gobble call @PhyRxByte ; Receive the command. mov Scratch0,w cje Scratch0,#commandGet,:commandGet cje Scratch0,#commandSet,:commandSet ; Invalid command. :gobble call @IPRxClosePacket jmp :loop :commandGet ; Send a reply packet. _bank IPVars mov IPLengthLSB,#2+(sprinklerZones*2) clr IPLengthMSB mov IPDestAddress1,IPSrcAddress1 ; Copy the address of the sender mov IPDestAddress2,IPSrcAddress2 mov IPDestAddress3,IPSrcAddress3 mov IPDestAddress4,IPSrcAddress4 call @UDPStartPacket ; Start the reply packet. mov w,#commandGet call @PhyTxByte mov w,#2+(sprinklerZones*2) call @PhyTxByte mov Scratch0,#JavaVars :gloop mov fsr,scratch0 mov w,indf call @PhyTxByte inc Scratch0 cjbe Scratch0,#JavaTxEnd,:gloop call @PPPClosePacket jmp :loop :commandSet call @PhyRxByte ; Ignore the number of zones. mov Scratch0,#JavaVars :sloop call @PhyRxByte mov Scratch1,w mov fsr,scratch0 mov indf,Scratch1 inc Scratch0 cjbe Scratch0,#JavaTxEnd,:sloop jmp :loop ENDIF IFDEF ADCDEMO _ADCSendWarning snb ADCWarning.0 retp setb ADCWarning.0 jmp @_AppInit ENDIF ;=============================================================================== ; Physical layer routines ;=============================================================================== ;------------------------------------------------------------------------------- ; Subroutine: PhyRxByte ; ; Receive a byte from the physical layer. This routine blocks until a byte is ; available. To prevent blocking, call PhyRxTest to see if there are any bytes ; already available in the receive buffer. Any transparency added by the peer ; is removed here. ; ; W on entry: - ; W on exit : the received byte ; Variables : Scratch1 ; Bank on exit : serial ;------------------------------------------------------------------------------- _IPRxData _PhyRxByte mov w,fsr _bank serial mov save_bank,w call @GetByte ; Call the UART VP mov Scratch2,w cjne Scratch2,#PPPEscape,:done call @GetByte ; Get the escaped byte xor w,#PPPXor mov Scratch2,w :done call @PPPRxFCSData mov w,Scratch2 DEBUGW $03,1 _bank serial mov fsr,save_bank mov w,Scratch2 retp ;------------------------------------------------------------------------------- ; Subroutine: PhyTxByte ; Subroutine: PhyNoTransTxByte ; ; Send a byte to the physical layer. If the transmit buffer is full then this ; routine will block until space is available. ; ; PhyTxByte adds transparency while PhyNoTransTxByte doesn't. Every byte between ; the start and stop flag sequences should be processed for transparency. ; By default all characters less than $20 as well as the control escape ($7D) ; and the flag sequence ($7E) is replaced by the control escape followed by the ; original character xored with $20. ; ; W on entry: The byte to transmit ; W on exit : - ; Variables : - ; Bank on exit : serial ;------------------------------------------------------------------------------- _IPTxData _PhyTxByte mov Scratch1,w mov w,fsr _bank serial mov save_bank,w mov w,Scratch1 call @PPPTxFCSData _PhyTxByteNoFCS DEBUGW $0A,1 mov Scratch1,w ; Save w cje Scratch1, #PPPFlag, :trans ; Compare to the flag sequence cje Scratch1, #PPPEscape,:trans ; Compare to the control escape cjb Scratch1, #$20, :trans ; Check if < $20 mov w,Scratch1 ; Reload byte to transmit call @SendByte ; No transparency issues _bank serial mov fsr,save_bank retp :trans mov w, #PPPEscape ; Transmit the control escape call @SendByte mov w,Scratch1 ; Reload byte to transmit xor w,#PPPXor ; Xor it. _PhyNoTransTxByte call @SendByte ; Call the UART VP _bank serial mov fsr,save_bank retp ;------------------------------------------------------------------------------- ; Subroutine: PhyRxTest ; ; Test if there is at least one byte available in the physical layer receive ; buffer. ; ; W on entry: - ; W on exit : Z is set to 1 if byte available, 0 otherwise ; Variables : - ; Bank on exit : serial ;------------------------------------------------------------------------------- _PhyRxTest mov w,fsr _bank serial ; Switch to the UART bank. mov save_bank,w clz test rx_ring_cnt ; Test if the ring count is zero snz jmp :not_set mov fsr,save_bank setb z retp :not_set mov fsr,save_bank clrb z retp IFDEF POP3DEMO ;------------------------------------------------------------------------------- ; Subroutine: AppNak ; ; The last packet transmitted was rejected by the remote host, or not ; acknowledged. It needs to be retransmitted. This is a signal to the application ; to reset its transmit counters. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppNak _bank POP3Vars clr POP3TxCount mov w,#'n' POP3DW retp ;------------------------------------------------------------------------------- ; Subroutine: AppAck ; ; The last packet transmitted was acknowledged by the remote host. It will not ; need to be retransmitted. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppAck _bank POP3Vars inc POP3State mov POP3TxPointer,#_POP3NONE mov w,#'a' POP3DW retp ;------------------------------------------------------------------------------- ; Subroutine: AppPacketBad ; ; The last packet received did not pass the CRC check. The recieve counters ; should be reset and any actions undone since the packet will be received again. ; ; W on entry: - ; W on exit : - ;------------------------------------------------------------------------------- _AppPacketBad DEBUGP $58,top _bank POP3Vars clr POP3RxCount mov POP3MsgSubSt,POP3MsgSubStLast mov w,#'b' POP3DW retp ENDIF ;=============================================================================== ; UART virtual peripheral code to provide the physical layer ;=============================================================================== org $e00 SerialISR IFDEF ADCDEMO _bank ADCVars ;switch to ISR bank mov w, rb ;Read port b mov adcTemp, w and w, #%01111111 ;clear bit 7 sb adcTemp.6 ;Bit 6 low ? or w, #%10000000 ;Yes => set bit 7 mov rb, w ;update cap. discharge pins snb adcTemp.6 ;check if adc0 triggered? incsz adcAcc ;if so, increment accumulator inc adcAcc ; and prevent overflowing dec adcAcc ; by skipping second 'INC' inc adcCount ;adjust adc0 timing count jnz :End ; not done, jump ahead mov adcValue, adcAcc ;samples ready, update adc0 clr adcAcc ; reset adc0 accumulator setb adcComplete ;Indicate data mov w, #%11000000 and rb, w csb adcValue, #256/7 * 1 setb rb.0 csb adcValue, #256/7 * 2 setb rb.1 csb adcValue, #256/7 * 3 setb rb.2 csb adcValue, #256/7 * 4 setb rb.3 csb adcValue, #256/7 * 5 setb rb.4 csb adcValue, #256/7 * 6 setb rb.5 :End ENDIF _bank serial ;switch to serial register bank :transmit clrb tx_divide.baud_bit ;clear xmit timing count flag inc tx_divide ;only execute the transmit routine stz ;set zero flag for test sb tx_divide.baud_bit ;every 2^baud_bit interrupt jmp :receive ;not a transmit cycle test tx_count ;are we sending? sz jmp :txbit ;yes, send next bit mov w,tx_ring_cnt ;is tx ring empty? snz jmp :receive ;yes, go to :receive :txring mov w,tx_ring_op ;move one character from the ring to the mov fsr,w ; transmitter using indirect addressing mov w,indf _bank serial ;switch back to the uart bank not w ;ready bits (inverse logic) mov tx_high,w ; store data byte setb tx_low.7 ; set up start bit mov tx_count,#10 ;1 start + 8 data + 1 stop bit dec tx_ring_cnt ;decrement tx ring byte count snz clrb StatusPort.LEDTx ringadv tx_ring_op,tx_ring,tx_ring_size ;advance ring pointer :txbit clc ;ready stop bit rr tx_high ; and shift to next bit rr tx_low ; dec tx_count ;decrement bit counter movb tx_pin,/tx_low.6 ;output next bit :receive movb c,rx_pin ;get current rx bit test rx_count ;currently receiving byte? sz jmp :rxbit ;if so, jump ahead mov w,#9 ;in case start, ready 9 bits sc ;skip ahead if not start bit mov rx_count,w ;it is, so renew bit count mov rx_divide,#start_delay ;ready 1.5 bit periods :rxbit decsz rx_divide ;middle of next bit? jmp :rxdone setb rx_divide.baud_bit ;yes, ready 1 bit period dec rx_count ;last bit? sz ;if not rr rx_byte ; then save bit sz ;and skip to end jmp :rxdone mov w,rx_ring_cnt ; Is the receive buffer already full? xor w,#rx_ring_size ; Compare with the buffer size sz jmp :rx_ok ; Not full setb flags.rx_over ; Signal receive buffer overflow jmp :rxdone ; Return to the interrupt handler :rx_ok mov w,rx_byte ; Move the received byte to the ring bank. _bank uart_rx_ring mov uart_temp_isr,w _bank serial mov w,rx_ring_ip ; Store character in receive buffer mov fsr,w ; Set indirect address mov w,uart_temp_isr ; temp must be in same bank as rx ring. mov indf,w ; Store the received byte _bank serial ; Restore the bank setb StatusPort.LEDRx ; There is traffic. ringadv rx_ring_ip,rx_ring,rx_ring_size inc rx_ring_cnt ; Increment the ring buffer count csne rx_ring_cnt,#(rx_ring_size-2) ; Is the ring nearly full? setb cts_pin ; Yes. Drop CTS to stop the DTE. :rxdone IFDEF DEBUG call @DebugSerialISR ; Call the debug ISR. ENDIF ; Update the timer. _bank PPPTimer incsz PPPTimer1 jmp :timerDone incsz PPPTimer2 jmp :timerDone inc PPPTimer3 :timerDone mov w,#-int_period&255 ;interrupt every 'int_period' clocks :end_int retiw ;exit interrupt
file: /Techref/scenix/lib/io/osi3/tcpip/iSXv1_0_4.src, 153KB, , updated: 2005/8/19 17:13, local time: 2024/11/30 03:55,
18.226.104.30:LOG IN
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://techref.massmind.org/Techref/scenix/lib/io/osi3/tcpip/iSXv1_0_4.src"> scenix lib io osi3 tcpip iSXv1_0_4</A> |
Did you find what you needed? |
Welcome to massmind.org! |
Welcome to techref.massmind.org! |
.