From Swordfish Wiki

SwordfishUser: PIC18F97J60EthernetDriver

Ethernet Module (driver) for PIC18F97J60 family .

This module support ICMP, ARP, UDP, TCP protocols and have Mini Web Server example.

No TCP/IP stack and fragmentation available now.

Default Ip address is 192.168.1.253

Available functions

Public Sub Eth_SetParameters
{Set your IP, Mac, Mask ... etc... here}

Public Sub Eth_Init 
{Init MCU Ethernet}

Public Sub Eth_Reset
{Reset MCU Ethernet}

Public Sub Eth_DoPacket
{Process incoming packets}

Public Function Eth_UserTCP(byref dest_ip_addr_T(4) as byte, byref source_port_T, dest_port_T, len_T as word) as word
{This Sub Function is called by library. Put your TCP response for WEB server parameters here. See example}

Public Function Eth_UserUDP(byref dest_ip_addr_U(4) as byte, byref dest_port_U, source_port_U, len_U as word) as word
{This Sub Function is called by library. Put your UDP response here. See example. ( ECHO example )}

Public Sub Firewall(ICMP, TCP, UDP as boolean)
{Default all false, allow all type of packets}
{If ICMP = true, ignore ICMP request. Ex. Firewall(true, false, false)}
{If TCP = true, ignore TCP packets. Ex. Firewall(false, true, false)}
{If UDP = true, ignore UDP packets. Ex. Firewall(false, false, true)} 

Public Sub Eth_PutByte(value as byte)
{Put one byte in ETH memory}

Public Function Eth_GetByte as byte
{Get one byte from ETH memory}

Public Function CopyFlashToEthMem_CP(start_Flash_address as longword) as word
{Copy Const from flash to Eth Memory and return length of Const data}
{Const data must be defined as STRING. (and must be zero terminated)}
{Ex. len = CopyFlashToEthMem_CP(AddressOf(httpHeader))} 

Public Function CopyRamToEthMem_CP(s as String) as word
{Copy ram data to Eth Memory and return length of data}
{ram data must be defined as String. (and must be zero terminated)}
{Ex. len = CopyRamToEthMem_CP(data)}

Public Sub CopyEthMemToRam(start_eth_address, dest_ram_address, length_w as word)
{Ex. CopyEthMemToRam(AddrPacket+6,AddressOf(dest_mac_addr),6)}

Public Sub CopyRamToEthMem(start_ram_address, dest_eth_address, length_w as word)
{Ex. CopyRamToEthMem(AddressOf(eth_mac),TXSTART+22,6)}

Public Sub CopyFlashToEthMem(start_Flash_address as longword, dest_eth_address, length_w as word)
{Ex. CopyFlashToEthMem(AddressOf(httpHeader),TXSTART+54,30)}

Public Sub CopyEthMemToRam_Inv(start_eth_address, dest_ram_address, length_w as word)
{Ex. CopyEthMemToRam(AddrPacket+6,AddressOf(data_dWord),4)}

Public Sub CopyRamToEthMem_Inv(start_ram_address, dest_eth_address, length_w as word)
{Ex. CopyRamToEthMem(AddressOf(data_dWord),TXSTART+22,4)}

Public Sub CopyEthMemToEthMem(start_eth_address, dest_eth_address, length_w as word, where as byte)
{where = 0 copy from Eth RxBuf to Eth TxBuf}
{where = 1 copy from Eth TxBuf to Eth TxBuf}
{Ex. CopyEthMemToEthMem(AddrPacket+38,TXSTART+28,4,0)}

Public Sub WriteToEthMem(dest_eth_address as word, value as byte)
{Ex. WriteToEthMem(TXSTART+12,$08)}

Public Function ReadFromEthMem(start_eth_address as word) as byte
{Ex. data = ReadFromEthMem(AddrPacket+38)}

Public Function EthMemCompareWithRam(start_eth_address, start_ram_address, length_w as word) as boolean
{Ex. bol = EthMemCompareWithRam(AddrPacket+30,AddressOf(eth_ip_addr),4)}

Public Function EthMemCompareWithFlash(start_eth_address as word, start_Flash_address as longword, length_w as word) as boolean
{Ex. bol = EthMemCompareWithFlash(AddrPacket+54, AddressOf(httpHeader), 30)}

Public Function Eth_Cksum(start_eth_address, length_w as word) as word
{Ex. cksum_ip = Eth_Cksum(TXSTART+14,20)}

Public Sub Eth_WritePHYReg(register_address as byte, data as word)
{Write to PHY registers}

Public Sub Eth_SetLedConfig(NewConfig as word)
{Set Eth Led configuration. See datasheet for more detail}

Public Function Eth_ReadPacket as word
{Read packet and return TYPE OF SERVICE}

Public Sub Eth_Send(length_w as word)
{Send packet from Tx buffer}

You can download source code from : http://www.microelemente.ro/Swordfish/Mini_WEB_Server.zip

Example Code

{
****************************************************************
*  Name    : MiniWebServer.BAS                                 *
*  Author  : Florin Andrei Medrea                              *
*  Notice  : Copyright (c) 2007 YO2LIO                         *
*          : All Rights Reserved                               *
*  Date    : 10/11/2007                                        *
*  Version : 3.3                                               *
*  Notes   : MCU PIC18F97J60 family                            *
*          :                                                   *
****************************************************************
}
Device = 18F67J60
Clock = 41.666667
Config FOSC = HSPLL
Config FOSC2 = ON
Config WDTPS = 1024

#option WDT = true

Include "lib_18F97J60_V3_3"
Include "lib1_18F97J60_V3_3"  
Include "system"

Sub Init() 
  OSCTUNE = %01000000
  DelayMS(100)
  ClrWDT
  WDTCON = 1
  CMCON = $07
  ADCON1 = %00001111         // All digital
  ADCON2 = %10000111         // RC Clock
  INTCON2 = %01111111        // PORTB Pull Up
  TRISE = $0F
  PORTE = $F0
End Sub

Sub Eth_SetParameters()   // set your parameters here
  eth_ip_addr(0) = 192
  eth_ip_addr(1) = 168
  eth_ip_addr(2) = 1
  eth_ip_addr(3) = 253

  eth_mac(0) = $00
  eth_mac(1) = $04
  eth_mac(2) = $A3
  eth_mac(3) = $00
  eth_mac(4) = $80
  eth_mac(5) = $85

  eth_port = 10001
  dest_port = 10001

  Uptime = 0
End Sub

  Init
  Eth_Reset
  Eth_SetParameters
  Eth_Init
  Firewall(false,false,false)
  While true 
    Eth_DoPacket // process incoming packets
  Wend
End.

Module Code

{
****************************************************************
*  Name    : lib_18F97J60_V3_3.BAS                             *
*  Author  : Florin Andrei Medrea                              *
*  Notice  : Copyright (c) 2007 YO2LIO                         *
*          : All Rights Reserved                               *
*  Date    : 10/11/2007                                        *
*  Version : 3.3                                               *
*  Notes   :                                                   *
*          :                                                   *
****************************************************************
}
Module lib_18F97J60_V3_3

Include "lib1_18F97J60_V3_3"
Include "lib_user"
Include "system" 

Const
      ETH_ARP	           As Word = $0806,
      ETH_ARP_REQ	       As Byte = $01,
      ETH_ARP_RES	       As Byte = $02,
      ETH_IP	           As Word = $0800,
      ETH_IP_TCP	       As Byte = $06,
      ETH_IP_UDP	       As Byte = $11,
      ETH_IP_ICMP	       As Byte = $01,
      ETH_IP_ICMP_ECHO	 As Byte = $08,
      ETH_IP_ICMP_REPLY	 As Byte = $00,
      URG As Byte = 5,
      ACK As Byte = 4,
      PSH As Byte = 3,
      RST As Byte = 2,
      SYN As Byte = 1,
      FIN As Byte = 0,
      PHLCON As Word = $0014,
      PHCON1 As Word = $0000,
      PHSTAT1 As Word = $0001,
      PHCON2 As Word = $0010,
      PHSTAT2 As Word = $0011

Sub Eth_WritePHYReg(register_address As Byte, data As Word)
  MIREGADR = register_address       // Address to MIREGADR
  MIWRL =  data.Byte0               // Lo byte to MIWRL
  MIWRH = data.Byte1                // Hi byte to MIWRH
  // Wait until the PHY register has been written
  FSR2 = AddressOf(MISTAT)
  While INDF2.0 <> 0 Wend
End Sub

Sub Eth_SetLedConfig(NewConfig As Word)
  Eth_WritePHYReg(PHLCON, NewConfig)
End Sub

Public Sub Firewall(F_ICMP, F_TCP, F_UDP As Boolean)
  FICMP = F_ICMP
  FTCP = F_TCP
  FUDP = F_UDP
End Sub

Public Sub Eth_Init()
  NextPacket = RXSTART
  AddrPacket = RXSTART
  LATA.0 = 0
  LATA.1 = 0
  TRISA.0 = 0
  TRISA.1 = 0
  FSR2 = AddressOf(ECON2)
  INDF2.5 = 1
  FSR2 = AddressOf(ESTAT)
  While INDF2.0 <> 1 Wend
  DelayMS(1)
  //*** INIT **************************************//
  FSR2 = AddressOf(ETXSTL)
  POSTINC2 = $00 Nop
  INDF2 = $19
  // Start of RX buffer  NOTE as  ERXWRPT WILL TAKE ON SAME VALUES
  FSR2 = AddressOf(ERXSTL)
  POSTINC2 = $00 Nop
  INDF2 = $00
  // End of RX buffer
  FSR2 = AddressOf(ERXNDL)
  POSTINC2 = $FF Nop
  INDF2 = $18
  // Pointer of RX buffer
  FSR2 = AddressOf(ERXRDPTL)
  POSTINC2 = $00 Nop
  INDF2 = $00
  FSR2 = AddressOf(ERXFCON)
  INDF2 = %00100000     // Receiver Filter
  //*** MAC INIT *********************************//
  // Enable MAC to RX
  FSR2 = AddressOf(MACON1)
  INDF2 = %00001101        // TXPAUS, RXPAUS, MARXEN
  // Paded to 60 bytes and CRC and check length
  FSR2 = AddressOf(MACON3)
  POSTINC2 = %00110011 Nop // PADCFG0, TXCRCEN, FRMLNEN
  INDF2 = %00000000
  // Non Back to Back shit
  FSR2 = AddressOf(MAIPGL)
  POSTINC2 = $12 Nop
  INDF2 = $0C
  FSR2 = AddressOf(MABBIPG)
  INDF2 = $15
  FSR2 = AddressOf(MAMXFLL)
  POSTINC2 = $EE Nop     // max_packet bytes
  INDF2 = $05 Nop  
  MAADR1 = eth_mac(5)    // Set up your MAC address
  MAADR2 = eth_mac(4) 
  MAADR3 = eth_mac(3) 
  MAADR4 = eth_mac(2) 
  MAADR5 = eth_mac(1) 
  MAADR6 = eth_mac(0) 
  Eth_WritePHYReg(PHCON1, $0100)   // PHCON1 full duplex
  Eth_WritePHYReg(PHCON2, $0110)   // PHCON2 Disable half duplex loopback in PHY
  Eth_SetLedConfig($0472)
  ECON1.2 = 1        // Enable packet reception
  udp_counter = 1000 // start UDP counter with this value
  tcp_counter = 1000
  ACK_No = 0
  SEQ_No = $42844A80
  DelayMS(1000)
  ClrWDT
  Firewall(false, false, false)
End Sub 

Public Sub Eth_Reset()
  FSR2 = AddressOf(ECON2)
  INDF2.5 = 0
  FSR2 = AddressOf(ESTAT)
  INDF2.0 = 0
  TRISA = $FF
  DelayMS(1)
  ClrWDT
End Sub

Function Eth_ReadPacket() As Word
Dim next_address As Word
  AddrPacket = NextPacket
  ERDPTL =(AddrPacket).Byte0      // Set the lower write pointer
  ERDPTH = (AddrPacket).Byte1     // Set the upper write pointer  
  NextPacket.Byte0 = EDATA_reg
  NextPacket.Byte1 = EDATA_reg      
  next_address = NextPacket - 1
  ERXRDPTL = (next_address).Byte0 // Set the lower write pointer
  ERXRDPTH = (next_address).Byte1 // Set the upper write pointer
  FSR2 = AddressOf(ECON2)
  INDF2.6 = 1                     // Decrement the number of packets
  next_address = Check_FIFO(AddrPacket + 18) 
  ERDPTL = (next_address).Byte0   // Set the lower write pointer
  ERDPTH = (next_address).Byte1   // Set the upper write pointer         
  Result.Byte1 = EDATA_reg 
  Result.Byte0 = EDATA_reg 
  AddrPacket = Check_FIFO(AddrPacket + 6) 
End Function

Sub Eth_Send(length_w  As  Word)
Dim address  As  Word
  FSR2 = AddressOf(ETXSTL)
  POSTINC2 = $00 Nop   // Set the lower start of packet
  INDF2 = $19          // Set the upper start of packet
  FSR2 = AddressOf(EWRPTL)
  POSTINC2 = $00 Nop   // Set the lower write pointer
  INDF2 = $19          // Set the upper write pointer
  EDATA_reg = $0E      // Start Byte (MUST START WITH THIS)
  address = (TXSTART - 1) + length_w
  ETXNDL = (address).Byte0
  ETXNDH = (address).Byte1
  ECON1.3 = 1
  While ECON1.3 <> 0 Wend 
End Sub

Sub Eth_MacSwap()
  CopyEthMemToEthMem(AddrPacket+6,TXSTART,6,0)
  CopyRamToEthMem(AddressOf(eth_mac),TXSTART+6,6)
End Sub

Sub Eth_IpSwap()
  CopyRamToEthMem(AddressOf(eth_ip_addr),TXSTART+26,4)
  CopyEthMemToEthMem(AddrPacket+26,TXSTART+30,4,0)
End Sub

Sub Eth_MacSwap_User()
  CopyRamToEthMem(AddressOf(dest_mac),TXSTART,6)
  CopyRamToEthMem(AddressOf(eth_mac),TXSTART+6,6)
End Sub

Sub Eth_IpSwap_User()
  CopyRamToEthMem(AddressOf(eth_ip_addr),TXSTART+26,4)
  CopyRamToEthMem(AddressOf(dest_ip_addr),TXSTART+30,4)
End Sub

Sub Eth_ARPResponse()
  Eth_MacSwap
  WriteToEthMem(TXSTART+12,$08)
  EDATA_reg = $06 Nop
  EDATA_reg = $00
  EDATA_reg = $01
  EDATA_reg = $08 Nop
  EDATA_reg = $00
  EDATA_reg = $06
  EDATA_reg = $04 Nop
  EDATA_reg = $00
  EDATA_reg = $02
  CopyRamToEthMem(AddressOf(eth_mac),TXSTART+22,6)
  CopyEthMemToEthMem(AddrPacket+28,TXSTART+38,4,0)
  CopyEthMemToEthMem(AddrPacket+38,TXSTART+28,4,0)
  CopyRamToEthMem(AddressOf(eth_mac),TXSTART+32,6)
  Eth_Send(42)
End Sub

Sub Eth_PingResponse()
Dim cksum,PacketSize As Word
  PacketSize.Byte1 = ReadFromEthMem(AddrPacket+16)
  PacketSize.Byte0 = EDATA_reg
  PacketSize = PacketSize + 14
  Eth_MacSwap
  CopyEthMemToEthMem(AddrPacket+12,TXSTART+12,14,0)
  Eth_IpSwap
  WriteToEthMem(TXSTART+34,$00)
  EDATA_reg = $00 Nop
  EDATA_reg = $00 Nop
  EDATA_reg = $00
  CopyEthMemToEthMem(AddrPacket+38,TXSTART+38,PacketSize-38,0)
  cksum = Eth_Cksum(TXSTART+34,PacketSize-34)
  WriteToEthMem(TXSTART+36,(cksum).Byte1)
  EDATA_reg = (cksum).Byte0
  Eth_Send(PacketSize)
End Sub

Sub Eth_SendUDP(dest_portA, source_portA, PacketLenA As Word)
Dim TotalLen,cksum_ip,cksum_udp As Word,
    Align As Byte
  WriteToEthMem(TXSTART+12,$08)
  EDATA_reg = $00
  EDATA_reg = $45 Nop
  EDATA_reg = $00
  Inc(udp_counter)
  TotalLen = PacketLenA + 28
  EDATA_reg = (TotalLen).Byte1 Nop
  EDATA_reg = (TotalLen).Byte0 Nop
  EDATA_reg = (udp_counter).Byte1 Nop
  EDATA_reg = (udp_counter).Byte0 Nop
  EDATA_reg = $00 Nop
  EDATA_reg = $00
  EDATA_reg = $80
  EDATA_reg = $11 Nop
  EDATA_reg = $00 Nop
  EDATA_reg = $00
  cksum_ip = Eth_Cksum(TXSTART+14,20)
  WriteToEthMem(TXSTART+24,(cksum_ip).Byte1)
  EDATA_reg = (cksum_ip).Byte0
  WriteToEthMem(TXSTART+34,(source_portA).Byte1)
  EDATA_reg = (source_portA).Byte0 
  EDATA_reg = (dest_portA).Byte1 
  EDATA_reg = (dest_portA).Byte0
  TotalLen = PacketLenA + 8
  EDATA_reg = (TotalLen).Byte1 
  EDATA_reg = (TotalLen).Byte0 
  EDATA_reg = $00 Nop
  EDATA_reg = $00
  Align = TotalLen And $01
  WriteToEthMem(TXSTART+PacketLenA+42,$00)
  EDATA_reg = $00
  CopyEthMemToEthMem(TXSTART+26,TXSTART+PacketLenA+Align+42,8,1)
  WriteToEthMem(TXSTART+PacketLenA+Align+42+8,$00)
  EDATA_reg = $11
  EDATA_reg = (TotalLen).Byte1 
  EDATA_reg = (TotalLen).Byte0
  cksum_udp = Eth_Cksum(TXSTART+34,TotalLen + Align + 12)
  If cksum_udp = 0 Then cksum_udp = $FFFF End If
  WriteToEthMem(TXSTART+40,(cksum_udp).Byte1)
  EDATA_reg = (cksum_udp).Byte0
  TotalLen = PacketLenA + 42
  Eth_Send(TotalLen)
End Sub

Sub SendUDP(dest_port_S, source_port_S, len_data As Word, ByRef data_udp() As Byte)
Dim i As Word
  Eth_MacSwap_User
  Eth_IpSwap_User
  Put_UDPPointer
  i = 0
  While i < len_data    
    Eth_PutByte(data_udp(i))
    Inc(i)
  Wend
  Eth_SendUDP(dest_port_S, source_port_S, i)
End Sub

Sub Eth_ProcessUDP()
Dim PacketLen,source_port1,dest_port1,len,cksum_ip,cksum_udp As Word,
    Align As Byte
  cksum_ip.Byte1 = ReadFromEthMem(AddrPacket+24)
  cksum_ip.Byte0 = EDATA_reg   
  cksum_udp.Byte1 = ReadFromEthMem(AddrPacket+40)
  cksum_udp.Byte0 = EDATA_reg 
  len.Byte1 = ReadFromEthMem(AddrPacket+16)
  len.Byte0 = EDATA_reg   
  CopyEthMemToEthMem(AddrPacket,TXSTART,len+14,0)
  WriteToEthMem(TXSTART+24,$00)
  EDATA_reg = $00
  If cksum_ip <> Eth_Cksum(TXSTART+14,20) Then Exit End If
  If len <= 28 Then Exit End If
  Align = len And $01
  WriteToEthMem(TXSTART+40,$00)
  EDATA_reg = $00
  WriteToEthMem(TXSTART+len+14,$00)
  EDATA_reg = $00
  CopyEthMemToEthMem(TXSTART+26,TXSTART+len+Align+14,8,1)
  WriteToEthMem(TXSTART+len+Align+14+8,$00)
  EDATA_reg = $11
  len = len - 20
  EDATA_reg = (len).Byte1 
  EDATA_reg = (len).Byte0
  If cksum_udp <> Eth_Cksum(TXSTART+34,len + Align + 12) Then Exit End If
  len = len - 8
  source_port1.Byte1 = ReadFromEthMem(AddrPacket+36)
  source_port1.Byte0 = EDATA_reg 
  dest_port1.Byte1 = ReadFromEthMem(AddrPacket+34)
  dest_port1.Byte0 = EDATA_reg     
  CopyEthMemToRam(AddrPacket + 26,AddressOf(dest_ip_addr1),4)
  WriteToEthMem(TXSTART+41,$00) // put tx pointer
  Align = ReadFromEthMem(AddrPacket + 41) // put rx pointer to start of UDP data.
  PacketLen = Eth_UserUDP(dest_ip_addr1, dest_port1, source_port1, len)
  If PacketLen = 0 Then Exit End If
  Eth_MacSwap
  Eth_IpSwap
  Eth_SendUDP(dest_port1, source_port1, PacketLen)
End Sub

Sub Eth_SendTCP(source_portT, dest_portT, PacketLenT As Word, ByRef SEQ_NoT,ACK_NoT As LongWord, TCP_FlagT As Byte)
Dim TotalLen,cksum_ip,cksum_tcp As Word,
    Align As Byte
  WriteToEthMem(TXSTART+12,$08)
  EDATA_reg = $00
  EDATA_reg = $45 Nop
  EDATA_reg = $00
  TotalLen =40 + PacketLenT
  EDATA_reg = (TotalLen).Byte1
  EDATA_reg = (TotalLen).Byte0 
  EDATA_reg = (tcp_counter).Byte1 
  EDATA_reg = (tcp_counter).Byte0
  EDATA_reg = $40 Nop
  EDATA_reg = $00
  EDATA_reg = $80
  EDATA_reg = $06 Nop
  EDATA_reg = $00 Nop
  EDATA_reg = $00
  cksum_ip = Eth_Cksum(TXSTART+14,20)
  WriteToEthMem(TXSTART+24,(cksum_ip).Byte1)
  EDATA_reg = (cksum_ip).Byte0
  WriteToEthMem(TXSTART+34,(source_portT).Byte1)
  EDATA_reg = (source_portT).Byte0 
  EDATA_reg = (dest_portT).Byte1 
  EDATA_reg = (dest_portT).Byte0
  CopyRamToEthMem_Inv(AddressOf(SEQ_NoT),TXSTART+38,4)
  CopyRamToEthMem_Inv(AddressOf(ACK_NoT),TXSTART+42,4)
  If TCP_FlagT = $12 Then WriteToEthMem(TXSTART+46,$70)
  Else WriteToEthMem(TXSTART+46,$50) End If
  WriteToEthMem(TXSTART+47,TCP_FlagT)
  EDATA_reg = (Window).Byte1 
  EDATA_reg = (Window).Byte0 
  WriteToEthMem(TXSTART+50,$00)
  EDATA_reg = $00
  WriteToEthMem(TXSTART+14+TotalLen,$00)
  EDATA_reg = $00
  Align = TotalLen And $01
  CopyEthMemToEthMem(TXSTART+26,TXSTART+14+TotalLen+Align,8,1)
  WriteToEthMem(TXSTART+22+TotalLen+Align,$00)
  EDATA_reg = $06 Nop
  TotalLen =TotalLen - 20
  EDATA_reg = (TotalLen).Byte1 
  EDATA_reg = (TotalLen).Byte0
  cksum_tcp = Eth_Cksum(TXSTART+34,12+TotalLen+Align)
  WriteToEthMem(TXSTART+50,(cksum_tcp).Byte1)
  EDATA_reg = (cksum_tcp).Byte0
  Eth_Send(34+TotalLen)
End Sub

Sub Eth_ProcessTCP()
Dim PacketLen,PacketLenUser,source_port2,dest_port2,len,cksum_ip,cksum_tcp As Word,
    Align As Byte
  cksum_ip.Byte1 = ReadFromEthMem(AddrPacket+24)
  cksum_ip.Byte0 = EDATA_reg   
  cksum_tcp.Byte1 = ReadFromEthMem(AddrPacket+50)
  cksum_tcp.Byte0 = EDATA_reg 
  len.Byte1 = ReadFromEthMem(AddrPacket+16)
  len.Byte0 = EDATA_reg 
  PacketLen = len - 40
  CopyEthMemToEthMem(AddrPacket,TXSTART,len+14,0)
  WriteToEthMem(TXSTART+24,$00)
  EDATA_reg = $00
  If cksum_ip <> Eth_Cksum(TXSTART+14,20) Then Exit End If
  Align = len And $01
  WriteToEthMem(TXSTART+50,$00)
  EDATA_reg = $00
  WriteToEthMem(TXSTART+len+14,$00)
  EDATA_reg = $00
  CopyEthMemToEthMem(TXSTART+26,TXSTART+len+Align+14,8,1)
  WriteToEthMem(TXSTART+len+Align+22,$00)
  EDATA_reg = $06
  len = len - 20
  EDATA_reg = (len).Byte1 
  EDATA_reg = (len).Byte0
  If cksum_tcp <> Eth_Cksum(TXSTART+34,len + Align + 12) Then Exit End If
  source_port2.Byte1 = ReadFromEthMem(AddrPacket+36)
  source_port2.Byte0 = EDATA_reg   
  dest_port2.Byte1 = ReadFromEthMem(AddrPacket+34)
  dest_port2.Byte0 = EDATA_reg 
  tcp_counter.Byte1 = ReadFromEthMem(AddrPacket+18)
  tcp_counter.Byte0 = EDATA_reg   
  TCP_FlagR = ReadFromEthMem(AddrPacket + 47)
  CopyEthMemToRam_Inv(AddrPacket + 38,AddressOf(SEQ_NoR),4)
  CopyEthMemToRam_Inv(AddrPacket + 42,AddressOf(ACK_NoR),4)
  Window.Byte1 = ReadFromEthMem(AddrPacket+48)
  Window.Byte0 = EDATA_reg   
  If TCP_FlagR.Bits(SYN) = 1 Then   
    ACK_No = SEQ_NoR + 1
    TCP_Flag = $12
    Eth_MacSwap
    Eth_IpSwap
    Eth_SendTCP(source_port2, dest_port2, 8, SEQ_No, ACK_No, TCP_Flag)
    Exit
  End If
  If TCP_FlagR.Bits(FIN) = 1 Then
    SEQ_No = ACK_NoR
    ACK_No = SEQ_NoR + 1
    TCP_Flag = $10
    Eth_MacSwap
    Eth_IpSwap
    Eth_SendTCP(source_port2, dest_port2, 0, SEQ_No, ACK_No, TCP_Flag)
    Exit
  End If
  If TCP_FlagR.Bits(PSH) = 1 Then    
    SEQ_No = ACK_NoR
    ACK_No = SEQ_NoR + PacketLen
    TCP_Flag = $10
    Eth_MacSwap
    Eth_IpSwap
    Eth_SendTCP(source_port2, dest_port2, 0, SEQ_No, ACK_No, TCP_Flag)
    CopyEthMemToRam(AddrPacket + 26,AddressOf(dest_ip_addr2),4)
    WriteToEthMem(TXSTART+53,$00) // put tx pointer
    Align = ReadFromEthMem(AddrPacket + 53) // put rx pointer to start of TCP data.
    PacketLenUser = Eth_UserTCP(dest_ip_addr2, source_port2, dest_port2, PacketLen)
    TCP_Flag = $10
    Eth_MacSwap
    Eth_IpSwap
    Eth_SendTCP(source_port2, dest_port2, PacketLenUser, SEQ_No, ACK_No, TCP_Flag)
    SEQ_No = SEQ_No + PacketLenUser
    TCP_Flag = $11
    Eth_MacSwap
    Eth_IpSwap
    Eth_SendTCP(source_port2, dest_port2, 0, SEQ_No, ACK_No, TCP_Flag)
    Exit
  End If
End Sub

Public Sub Eth_DoPacket()
  ClrWDT
  If EIR.6 = 1 Then
      Select Eth_ReadPacket
        Case ETH_ARP     
          If EthMemCompareWithRam(AddrPacket+38,AddressOf(eth_ip_addr),4) = True Then       
            Select ReadFromEthMem(AddrPacket+21) 
              Case ETH_ARP_REQ  Eth_ARPResponse
              Case ETH_ARP_RES  Nop
            End Select
          End If
        Case ETH_IP        
          If EthMemCompareWithRam(AddrPacket+30,AddressOf(eth_ip_addr),4) = true Then
            Select ReadFromEthMem(AddrPacket+23)
              Case ETH_IP_ICMP 
                Select ReadFromEthMem(AddrPacket+34)
                  Case ETH_IP_ICMP_ECHO  If FICMP = false Then Eth_PingResponse End If
                  Case ETH_IP_ICMP_REPLY  Nop
                End Select
              Case ETH_IP_UDP  If FUDP = false Then Eth_ProcessUDP End If
              Case ETH_IP_TCP  If FTCP = false Then Eth_ProcessTCP End If
            End Select
          End If
      End Select
  End If
End Sub
{
****************************************************************
*  Name    : lib1_18F97J60_V3_3.BAS                            *
*  Author  : Florin Andrei Medrea                              *
*  Notice  : Copyright (c) 2007 YO2LIO                         *
*          : All Rights Reserved                               *
*  Date    : 10/11/2007                                        *
*  Version : 3.3                                               *
*  Notes   :                                                   *
*          :                                                   *
****************************************************************
}
Module lib1_18F97J60_V3_3

Include "system"

Public Const
      RXSTART   As Word = $0000,
      RXEND     As Word = $18FF,
      TXSTART   As Word = $1901

Public Dim 
    eth_ip_addr(4), dest_ip_addr(4),TCP_Flag,TCP_FlagR As Byte,
    eth_mac(6), dest_mac(6), dest_ip_addr1(4), dest_ip_addr2(4) As Byte,
    eth_port,dest_port, udp_counter, AddrPacket, NextPacket As Word,  
    SEQ_No, ACK_No, SEQ_NoR, ACK_NoR As LongWord,
    Window,tcp_counter As Word,
    FICMP,FTCP,FUDP As Boolean, 
    Uptime As Word

Public Dim EDATA_reg As Byte Absolute $0F61

Public Function Check_FIFO(address_s As Word) As Word
  If address_s > RXEND Then result = address_s - (RXEND + RXSTART + 1)
  Else result = address_s End If
End Function

Public Sub CopyEthMemToRam(start_eth_address, dest_ram_address, length_w As Word)
Dim i As Word
  i = Check_FIFO(start_eth_address)
  ERDPTL = i.byte0
  ERDPTH = i.byte1
  FSR2 = dest_ram_address
  i = 0
  While i < length_w
    POSTINC2 = EDATA_reg
    Inc(i)
  Wend
End Sub

Public Sub CopyRamToEthMem(start_ram_address, dest_eth_address, length_w As Word)
Dim i As Word
  EWRPTL = dest_eth_address.Byte0
  EWRPTH = dest_eth_address.Byte1
  FSR2 = start_ram_address
  i = 0
  While i < length_w
    EDATA_reg = POSTINC2
    Inc(i)
  Wend
End Sub

Public Sub CopyEthMemToRam_Inv(start_eth_address, dest_ram_address, length_w As Word)
Dim i As Word
  i = Check_FIFO(start_eth_address)
  ERDPTL = i.byte0
  ERDPTH = i.byte1
  FSR2 = dest_ram_address + length_w - 1
  i = 0
  While i < length_w
    POSTDEC2 = EDATA_reg
    Inc(i)
  Wend
End Sub

Public Sub CopyRamToEthMem_Inv(start_ram_address, dest_eth_address, length_w As Word)
Dim i As Word
  EWRPTL = dest_eth_address.Byte0
  EWRPTH = dest_eth_address.Byte1
  FSR2 = start_ram_address + length_w - 1
  i = 0
  While i < length_w
    EDATA_reg = POSTDEC2
    Inc(i)
  Wend
End Sub

Public Sub CopyFlashToEthMem(start_Flash_address As LongWord, dest_eth_address, length_w As Word)
Dim i As Word
  i = 0
  EWRPTL = dest_eth_address.Byte0
  EWRPTH = dest_eth_address.Byte1
  TBLPTRL = start_Flash_address.Byte0
  TBLPTRH = start_Flash_address.Byte1
  TBLPTRU = start_Flash_address.Byte2
  While i < length_w
    ASM
      TBLRD*+
    End ASM
    EDATA_reg = TABLAT
    Inc(i)
  Wend
End Sub

Public Function CopyRamToEthMem_CP(ByRef s As String) As Word
  result = 0
  FSR2 = AddressOf(s)
  While INDF2 <> 0
    EDATA_reg = POSTINC2
    Inc(result)
  Wend
End Function

Public Function CopyFlashToEthMem_CP(start_Flash_address As LongWord) As Word
  result = 0
  TBLPTRL = start_Flash_address.Byte0
  TBLPTRH = start_Flash_address.Byte1
  TBLPTRU = start_Flash_address.Byte2
  ASM
    TBLRD*+
  End ASM
  While TABLAT <> 0
    EDATA_reg = TABLAT
    Inc(result)
    ASM
      TBLRD*+
    End ASM
  Wend
End Function  

Public Sub CopyEthMemToEthMem(start_eth_address, dest_eth_address, length_w As Word, where As Byte)
Dim i As Word
  If where = 0 Then
    i = Check_FIFO(start_eth_address)
    EDMASTL = i.Byte0
    EDMASTH = i.Byte1
    i = Check_FIFO(start_eth_address + length_w - 1)
    EDMANDL = i.Byte0
    EDMANDH = i.Byte1
  Else
    i = start_eth_address
    EDMASTL = i.Byte0
    EDMASTH = i.Byte1
    i = start_eth_address + length_w - 1
    EDMANDL = i.Byte0
    EDMANDH = i.Byte1
  End If
  EDMADSTL = dest_eth_address.Byte0
  EDMADSTH = dest_eth_address.Byte1
  ECON1.4 = 0
  ECON1.5 = 1
  While ECON1.5 <> 0 Wend 
End Sub

Public Sub WriteToEthMem(dest_eth_address As Word, value As Byte)
  EWRPTL = dest_eth_address.Byte0
  EWRPTH = dest_eth_address.Byte1
  EDATA_reg = value
End Sub

Public Function ReadFromEthMem(start_eth_address As Word) As Byte
Dim i As Word
  i = Check_FIFO(start_eth_address)
  ERDPTL = i.byte0
  ERDPTH = i.byte1
  result = EDATA_reg
End Function

Public Sub Eth_PutByte(value As Byte)
  EDATA_reg = value
End Sub

Public Function Eth_GetByte() As Byte
  result = EDATA_reg
End Function

Public Function EthMemCompareWithRam(start_eth_address, start_ram_address, length_w As Word) As Boolean
Dim i As Word
  result = false
  i = Check_FIFO(start_eth_address)
  ERDPTL = i.byte0
  ERDPTH = i.byte1
  FSR2 = start_ram_address
  i = 0
  While i < length_w
    If POSTINC2 <> EDATA_reg Then Exit End If
    Inc(i)
  Wend
  result = true
End Function

Public Function EthMemCompareWithFlash(start_eth_address As Word, start_Flash_address As LongInt, length_w As Word) As Boolean
Dim i As Word
  result = false
  i = Check_FIFO(start_eth_address)
  ERDPTL = i.byte0
  ERDPTH = i.byte1
  TBLPTRL = start_Flash_address.Byte0
  TBLPTRH = start_Flash_address.Byte1
  TBLPTRU = start_Flash_address.Byte2
  i = 0
  While i < length_w
    ASM
      TBLRD*+
    End ASM
    If EDATA_reg <> TABLAT Then Exit End If
    Inc(i)
    Wend
  result = true
End Function

Public Function Eth_Cksum(start_eth_address, length_w As Word) As Word
Dim i As Word
  EDMASTL = start_eth_address.Byte0 
  EDMASTH = start_eth_address.Byte1
  i = start_eth_address + length_w - 1 
  EDMANDL = i.Byte0
  EDMANDH = i.Byte1
  ECON1.4 = 1
  ECON1.5 = 1
  While ECON1.5 <> 0 Wend
  result.Byte0 = EDMACSL
  result.Byte1 = EDMACSH  
End Function

Public Sub Put_UDPPointer()
  WriteToEthMem(TXSTART+41,$00)
End Sub

Public Function MemCmp(addr1, addr2, len As Word) As Integer
Dim i, S_FSR1, S_FSR2 As Word
  S_FSR1 = FSR1
  S_FSR2 = FSR2
  FSR1 = addr1
  FSR2 =addr2
  i = 0
  While i < len 
    result = POSTINC1 - POSTINC2
    If result <> 0 Then Break End If
    Inc(i)
  Wend
  FSR1 = S_FSR1
  FSR2 = S_FSR2
End Function
{
****************************************************************
*  Name    : lib_user.BAS                                      *
*  Author  : Florin Andrei Medrea                              *
*  Notice  : Copyright (c) 2007 YO2LIO                         *
*          : All Rights Reserved                               *
*  Date    : 10/11/2007                                        *
*  Version : 3.3                                               *
*  Notes   : User library                                      *
*          :                                                   *
****************************************************************}
Module lib_user

Include "lib1_18F97J60_V3_3"
Include "index_page"
Include "Convert.bas"

Dim UpH,UpM As Word

Public Function Eth_UserTCP(ByRef dest_ip_addr_T() As Byte, ByRef source_port_T, dest_port_T, len_T As Word) As Word
Dim len_tcp_resp As Word,       ' my reply len_tcp_response
    i As Word,
    getRequest As String(16),
    txt15 As String(16),                     
    mask As Byte
  len_tcp_resp = 0
  result = 0
  If (source_port_T <> 80) Then
    result = 0
    Exit
  End If
  If (len_T = 0) Then
    result = 0
    Exit
  End If
  i = 0 
  While i < 15   ' get 15 first bytes only of the request
    getRequest(i) = Eth_GetByte
    Inc(i)
  Wend
  getRequest(i) = 0
  i = 0
  txt15 = httpMethod  
  While i < 5  ' only GET method is supported here
    If txt15(i) <> getRequest(i) Then
      result = 0
      Exit
    End If
    Inc(i)
  Wend
  Inc(Uptime)  
  txt15 = "java1"
  If MemCmp(@getRequest(5), @txt15, 5) = 0 Then
    len_tcp_resp = CopyFlashToEthMem_CP(@httpHeader)                            ' HTTP header
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@httpMimeTypeScript)     ' with Script MIME type
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@java1)
    result = len_tcp_resp
    Exit
  End If
  txt15 = "fonts"
  If MemCmp(@getRequest(5), @txt15, 5) = 0 Then
    len_tcp_resp = CopyFlashToEthMem_CP(@httpHeader)                            ' HTTP header
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@httpMimeTypeCSS)        ' with HTML MIME type
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@fonts)
    result = len_tcp_resp
    Exit
  End If
  txt15 = "value"
  If MemCmp(@getRequest(5), @txt15, 5) = 0 Then

    len_tcp_resp = CopyFlashToEthMem_CP(@httpHeader)                            ' HTTP header
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@httpMimeTypeScript)     ' with text MIME type

    txt15 = "var PORTE = " 
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
    txt15 = DecToStr(PORTE)
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
    txt15 = " ;" 
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)

    UpH = Uptime / 100
    UpM = Uptime - (UpH * 100)

    txt15 = "var uph = "
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
    txt15 = DecToStr(UpH)
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
    txt15 = " ;" 
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)

    txt15 = "var upm = "
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
    txt15 = DecToStr(UpM)
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
    txt15 = " ;" 
    len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)

    result = len_tcp_resp
    Exit
  End If
  If getRequest(5) = "t" Then
    Mask = 0
    If ((getRequest(6) >= "0") And (getRequest(6) <= "7")) Then
      Mask = Byte(getRequest(6)) - 48         ' convert ASCII to byte
      Mask = 1 << Mask                    ' create bit mask
      PORTE = PORTE Xor Mask              ' toggle PORTD with xor operator
      len_tcp_resp = CopyFlashToEthMem_CP(@httpHeader)                          ' HTTP header
      len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@httpMimeTypeScript)   ' with text MIME type

      txt15 = "var PORTE ="
      len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
      txt15 = DecToStr(PORTE)
      len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
      txt15 = "; setTimeout("+#34
      len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)
      txt15 = "timer()"+#34+",100);"
      len_tcp_resp = len_tcp_resp + CopyRamToEthMem_CP(txt15)      
      result = len_tcp_resp
      Exit
    End If
  End If
  txt15 = "reset"
  If MemCmp(@getRequest(5), @txt15, 5) = 0 Then
    ASM
      reset
    End ASM
  End If
  If len_tcp_resp = 0 Then
    len_tcp_resp = CopyFlashToEthMem_CP(@httpHeader)                             ' HTTP header
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@httpMimeTypeHTML)        ' with HTML MIME type
    len_tcp_resp = len_tcp_resp + CopyFlashToEthMem_CP(@page1)                   ' HTML page first part
    result = len_tcp_resp
    Exit
  End If
End Function

Public Function Eth_UserUDP(ByRef dest_ip_addr_U() As Byte, ByRef dest_port_U, source_port_U, len_U As Word) As Word
//Dim i As Word
  result = 0
  If source_port_U <> eth_port Then Exit End If
  CopyEthMemToEthMem(AddrPacket+42, TXSTART+42, len_U, 0)
{  i = 0
  While i < len_U 
    Eth_PutByte(Eth_GetByte)  // echo (max. 1472 bytes)
    Inc(i)
  Wend   }
  result = len_U
End Function
{
****************************************************************
*  Name    : index_page.BAS                                    *
*  Author  : Florin Andrei Medrea                              *
*  Notice  : Copyright (c) 2007 YO2LIO                         *
*          : All Rights Reserved                               *
*  Date    : 10/11/2007                                        *
*  Version : 3.3                                               *
*  Notes   : User library                                      *
*          :                                                   *
****************************************************************}
Module index_page

Public Const httpHeader = "HTTP/1.1 200 OK" + #10 + "Content-type: "   ' HTTP header
Public Const  httpMimeTypeHTML = "text/html" + #10 + #10             ' HTML MIME type
Public Const  httpMimeTypeScript = "text/plain" + #10 + #10         ' TEXT MIME type
Public Const  httpMimeTypeCSS = "text/css" + #10 + #10               ' CSS MIME type
Public Const  httpMethod = "GET /"

Public Const page1 =
      "<html><head>"+
      "<title>Web Example</title>"+
      "<link rel="+#34+"stylesheet"+#34+" type="+#34+
      "text/css"+#34+" media="+#34+"screen"+#34+" href="+#34+
      "fonts.css"+#34+" /></head><body>"+
      "<dif id="+#34+"w"+#34+"><div id="+#34+"c"+#34+
      "><div id="+#34+"t"+#34+">Mini Web Server Example</div>"+
      "<form name="+#34+"f"+#34+"><h2>Commands</h2>"+
      "<fieldset>"+
      "<legend>Digital IN/OUT</legend>"+
      "<div class="+#34+"s"+#34+"><div class="+#34+"l"+#34+
      ">Inputs : PORTE(0-3)</div>"+
      "In 1:<span id="+#34+"t0"+#34+"></span>&nbsp;&nbsp;&nbsp;"+
      "In 2:<span id="+#34+"t1"+#34+"></span>&nbsp;&nbsp;&nbsp;"+
      "In 3:<span id="+#34+"t2"+#34+"></span>&nbsp;&nbsp;&nbsp;"+ 
      "In 4:<span id="+#34+"t3"+#34+"></span>"+
      "</div>"+
      "<div class="+#34+"s"+#34+"><div class="+#34+"l"+#34+
      ">Outputs : PORTE(4-7)</div>"+
      "Out 5:<input type=checkbox name=t4 onClick="+#34+"javascript:b(4);"+#34+">&nbsp;&nbsp;"+
      "Out 6:<input type=checkbox name=t5 onClick="+#34+"javascript:b(5);"+#34+">&nbsp;&nbsp;"+
      "Out 7:<input type=checkbox name=t6 onClick="+#34+"javascript:b(6);"+#34+">&nbsp;&nbsp;"+
      "Out 8:<input type=checkbox name=t7 onClick="+#34+"javascript:b(7);"+#34+">&nbsp;&nbsp;"+
      "</div></fieldset><br>"+
      "<h2>Information</h2>"+
      "<fieldset>"+
      "<legend>CONTROLERS</legend>"+
      "<div class="+#34+"s"+#34+"><div class="+#34+"l"+#34+
      ">PIC18F67J60</div>Home made Ip-Watcher board</div>"+
      "<div class="+#34+"s"+#34+"><div class="+#34+"l"+#34+
      ">OS Version:</div>V 3.3</div>"+
      "<div class="+#34+"s"+#34+"><div class="+#34+"l"+#34+
      ">Uptime:</div><span id="+#34+"uph"+#34+"></span>&nbsp;h&nbsp;<span id="+#34+
      "upm"+#34+"></span>&nbsp;m"+
      "<div class="+#34+"s"+#34+"><div class="+#34+"l"+#34+
      ">Author Program :</div>Florin Andrei Medrea - YO2LIO -</div>"+
      "</fieldset></form></div></div>"+
      "<script type="+#34+"text/javascript"+#34+" src="+#34+"value.js"+#34+"></script>"+
      "<script type="+#34+"text/javascript"+#34+" src="+#34+"java1.js"+#34+"></script>"+
      "</body></html>"                    

Public Const java1 = 
      "function timer() {"+
      "var jsel = document.createElement("+#34+"SCRIPT"+#34+");"+
      "jsel.type = "+#34+"text/javascript"+#34+
      ";jsel.src = "+#34+"value.js"+#34+
      ";document.body.appendChild (jsel);"+
      "div = document.getElementById("+#34+"uph"+#34+");"+
      "div.innerHTML = uph;"+
      "div = document.getElementById("+#34+"upm"+#34+");"+
      "div.innerHTML = upm;"+
      "var PORT = PORTE;"+
      "var bit = "+#34+"OFF"+#34+
      ";for(i=0;i<4;i++){"+
      "if(PORT&(1<<i)) {bit = "+#34+"ON"+#34+";}"+
      "else {bit = "+#34+"OFF"+#34+";}"+
      "div = document.getElementById("+#34+"t"+#34+"+i);"+
      "div.innerHTML = bit;};"+
      "PORT = PORTE;"+
      "bit = false;	"+
      "for(i=4;i<8;i++){"+
      "if(PORT&(1<<i)) {bit = true;}"+
      "else {bit = false;}"+
      "box = eval("+#34+"document.f.t"+#34+" + i); "+
      "box.checked = bit;};	}"+
      "function b(bit) {"+
      "var jsel = document.createElement("+#34+"SCRIPT"+#34+");"+
      "jsel.type = "+#34+"text/javascript"+#34+
      ";jsel.src = "+#34+"t"+#34+"+bit;"+
      "document.body.appendChild (jsel);"+
      "var bit = false;	"+
      "for(i=4;i<8;i++){"+
      "if(PORTE&(1<<i)) {bit = true;}"+
      "else {bit = false;}"+
      "box = eval("+#34+"document.f.t"+#34+" + i); "+
      "box.checked = bit;};}"+
      "timer();"+
      "setInterval("+#34+"timer()"+#34+",5000);"       

Public Const fonts = 
      "* {font-family:Tahoma, Arial, Helvetica, sans-serif;"+
      "font-size:1em;} body {text-align:center;"+
      "background:#111;margin:0;font-size:0.8em;}"+
      "#w {position:relative;text-align:left;}"+
      "#c {padding: 5px;background:#FFF;width:700px;"+
      "margin:0 auto;text-align:left;}"+
      "#t {font-weight:bold;font-size: 2em;"+
      "margin-bottom: 0.5em;}h2, h3 {"+
      "margin:0 0 .906em 0;background:#099;"+
      "padding:.181em .725em;color:#000;"+
      "letter-spacing:.09em;}.s {position:relative;"+
      "margin-bottom:.453em;line-height:1.993em;"+
      "width:100%;}.s .l {float:left;width:18.931em;}"+      
      "fieldset {border:solid .09em #ccc;padding:.725em;"+
      "margin:0;}fieldset legend {"+
      "font-weight:bold;margin-left:-.362em;"+
      "padding:0 .272em;background:#fff;color:#099;}"   
Retrieved from https://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.PIC18F97J60EthernetDriver
Page last modified on October 26, 2007, at 10:24 PM