; *****************************************************************************************
; 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 asemble correctly with SASM (Rev 1.44.6). Also verified with Parallax
; 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&255
jmp :done
mov w,#_SMTPHELO&255
jmp :done
mov w,#_SMTPNONE&255
jmp :done
mov w,#_SMTPMAIL&255
jmp :done
mov w,#_SMTPNONE&255
jmp :done
mov w,#_SMTPRCPT&255
jmp :done
mov w,#_SMTPNONE&255
jmp :done
mov w,#_SMTPDATA&255
jmp :done
mov w,#_SMTPNONE&255
jmp :done
mov w,#_SMTPMESG&255
jmp :done
mov w,#_SMTPNONE&255
jmp :done
mov w,#_SMTPQUIT&255
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&255
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&255,: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&255
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&255
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/eSXv1_0_4.src, 153KB, , updated: 2005/8/19 17:13, local time: 2024/11/19 10:29,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/Techref/scenix/lib/io/osi3/tcpip/eSXv1_0_4.src"> scenix lib io osi3 tcpip eSXv1_0_4</A> |
Did you find what you needed?
|