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