So, what is this WebTiger all about? A RTL8019as ethernet controller, AVR Atmega128 microcontroller, compatible to the ethernut 1.3F and it is the little brother of the WebCat from www.achatz.nl

Here a picture.....

 

and it fits on the STK500 from Atmel

 

So you will get this combination

Here you can find a step-by-step tutorial about the Bascom-AVR code to drive the WebTiger.


 

History of Ethernet

    Ethernet was initially developed in Xerox's Palo Alto Research Centre (PARC) by Metcalfe and Bloggs. They designed the system on a 3Mbps serial cable while trying to connect a printer to a computer. The cable became known as the ether.

    In 1980 Intel, Xerox and DEC combined to create the ethernet specification. It standardised Ethernet at 10Mbps and gave it the title Ethernet II. That specification was then used as the basis for IEEE 802.3 - essentially the same as ethernet with a few minor changes but backward compatible.

    Since that time Ethernet has grown and matured from a single cable communication technique to bustling networks of inter-connected hubs, switches, nodes and bridges, becoming the dominant LAN technology in the world. 100Mbps networks are now common, 1Gbps starting to enter the market, and 10Gbps not far past the horizon.

 

 

Step 1. The first steps, setting up the hardware.....


We first have to set up the hardware. Put the WebTiger on the STK500. The resetbutton should point to the side where the two serial connectors are.

To see what is happening on the serial port of the WebTiger, you need to connect two wires from SER0 to RS232 Spare. Like this:

 

And to program the Atmega128 from within Bascom-AVR you need to put a flatcable between the ISP6PIN and the WebTiger ISP like this:

Install AVR Studio 4.0.
This because we need it to get the right programming tools for the STK500.

AVR Studio 4.0

And as a last step set up the STK500 programmer in Bascom-AVR

If you connect the serial cable between your PC and the STK500. Start the programmer and you should get the next screen

The information on the right of the screen I got by pressing the READ buttons on the screen.

For testing and programming we just have to swap a serial cable from one DB9 connector to another DB9 connector on the STK500.


Be aware: If you want to program parallel (high voltage), you have to remove the reset-jumper on the WebTiger. On the resetline 12 Volts will occur and will destoy the 74HC04 and MAX809. If you don't use the two flatcables from PROG CTRL  and PROG DATA you are not parallel programming.

 


Now we are ready to GO!!!!

 


We can start programming in Bascom-AVR. I have tested with version 1.11.81. With a lower version it can be done but you need to replace the SPLIT-command.

You can cut and paste all green parts of the next text and place it into the Bascom-AVR IDE. Going through the code, I will ask you to compile the code and let you know what you should see on the LEDs of the STK500 or on the connected hyperterminal.

First, start with a small description what you are planning to do with date and/or version-number.

'------------------------------------------------------------------------------------
' WebTiger / Ethernut 1.3F - Atmega128 and RTL8019AS - ARP - UDP - PING - HTML
'------------------------------------------------------------------------------------
'
' january 2006 - Ben Zijlstra - Netherlands - https://benshobbycorner.nl/bzijlstra

Make a small note what you did with the fusebits of the microcontroller
For this program the following fusebits should be set. If you buy your WebTiger, all fusebits will have been set in the right order.

' Lockbits:
' Lockbit 65 : 11
' Lockbit 43 : 11
' Lockbit 21 : 11

' Fusebits:
' Fusebit C : 1
' Fusebit B : 1
' Fusebit KLA987 : 101111

' Fusebits High
' Fusebits I : 1
' Fusebits H : 1
' Fusebits O : 0
' Fusebits P : 1
' Fusebits G : 0
' Fusebits FE : 00
' Fusebits D : 1


At the Bascom-AVR options put Hardware stack, Soft stack and Framesize all at 128 Bytes.
Furthermore XRAM waitstate and External Access should be enabled.


Study the schematics and put here an inventory, helps to keep things simple

$regfile "m128def.dat"
$crystal = 14745600
$baud = 57600

$waitstate 'XRAM wait state
$xa 'External Access enabled
$default Xram

const debug = 1

#if debug = 1
print "Hello world"
#endif

end

$crystal is used to give the Mhz or MegaCycles you are using. In this case a crystal of 14.745.600 is used.
$regfile is used to let Bascom-AVR use the right registers for the right microcontroller. In this case we are using a Atmega128.
$baud is used to give the right transmission speed for the serial port we are going to use on the board.
$waitstate and $xa. At the Bascom-AVR Compiler options we also put Xram waitstate on and also the External Access enabled. But in the new Bascom-AVR you can also put them on in the program. 
$default XRAM With this command we start using the external SRAM on the WebTiger.

The const debug is used for testpurposes. During programming you can test certain parts by using a #if #endif combination. 

Compile this program. Program the WebTiger with it, unplug the programcable and put it on the RS232-connector. Start Hyperterminal, 57600 baud, 8 bit, no parity, no handshake, and after resetting the board you should see a message.

On Hyperterminal I see Hello world. You now know that you have done a good hardware setup. 

Step 2: creating subroutines to read and write the RTL8019as

 

'memory map:

'The ATMEGA128 works in normal-mode (fuse-settings)

'Data Memory
'$0000-$001F 32 registers
'$0020-$005F 64 I/O reg.
'$0060-$00FF 160 Ext I/O reg.
'$0100-$10FF Internal SRAM
'$1100-$7FFF External SRAM
'$8300-$831F RTL8019as

So the startaddress external SRAM is $1100
and available is 28416 Bytes

Also at the top of the program it is easy to place the the memory-map of the board. The Atmega128 has internal RAM from $0100-$10FF. 4096 bytes. The WebTiger has 32 K SRAM external memory, but it overlaps these first bytes. So only 28416 byte of the external SRAM is left to use.

The RTL8019as is memory mapped and occupies 32 bytes at address $8300-$831F. If you do a check you will see that it occurs several times in the memory space above $8000. All traffic from and to the RTL8019as will be done with only these 32 bytes. I will show you later on.

$hwstack = 128
$swstack = 128
$framesize = 128

Config Int5 = Falling

At the Bascom-AVR options we already made some space available for hardware, software and framesize. It is however easy to force these settings also in the program. If you switch from one program to another, you always know you will have the good settings for stack.

Besides the 32 Byte memory-mapped space $8300-$831F, the RLT8019as uses on the WebTiger an interrupt. An interrupt is given when a packet arrives. And Bascom can act on several kinds of Interrupts, falling, rising and changing. Here we act on a falling interrupt.

Declare Sub Write_rtl8019as(byval Regaddr As Byte , Byval Regdata As Byte)
Declare Sub Read_rtl8019as(byval Regaddr As Byte)

Have written software of memory-mapped RTL8019as but also for RTL8019as that was connected through Generic I/O. By just replacing two Read and Write routines you can switch between these two modes. For Generic I/O you can have a look at the code for the AVR Ethernet I/O board from www.drftech.com. and www.edtp.com. At the top of the program we declare subroutines we are going to use.

We use two subroutines to read and write the RTL8019as registers and use two variables to pass the data on. In Bascom-AVR we have to dimension all used variables.

Dim Regaddr As Byte
Dim Regdata As Byte

Dim Byte_read As Byte
Dim Whulp0 As Word

And here are the subroutines. Place them after the END of your program.

' Routine to Write to NIC Control register
'
Sub Write_rtl8019as(regaddr , Regdata)
Whulp0 = Regaddr + &H8300
Out Whulp0 , Regdata
End Sub


' Routine to read from NIC Control register
'
Sub Read_rtl8019as(regaddr)
Whulp0 = Regaddr + &H8300
Byte_read = Inp(whulp0)
End Sub

Perhaps you remembered the RTL8019as base-address was $8300, and you can see we are using it in our read- and write-routines. We dimensioned Whulp0 as a Word variable, because it should hold the address of the RTL8019as + register-address inside the RTL8019as.

Now we are ready to read and to write to the Ethernet controller.


Step 3: Initializing the RTL8019as

Now we have two routines to read and write to the RTL8019as Ethernet controller we can start to initialize it. We are doing this by adding a subroutine called Init_rtl8019as

Go to the top of the program and add a declare of this new subroutine, Sub Init_rtl8019as

Declare Sub Init_rtl8019as

Place under the read and write routines the new routine.

' Routine to initialize the RTL8019AS ethernetchip
'
Sub Init_rtl8019as
#if Debug = 1
Print "Sub Init_RTL8019as"
#endif
Call Read_rtl8019as(nic_rstport)
Call Write_rtl8019as(nic_rstport , Byte_read)
Waitms 10
'check for good soft reset
Call Write_rtl8019as(nic_imr , 0) 'interrupt mask register
Call Write_rtl8019as(nic_isr , &HFF)
Call Read_rtl8019as(nic_isr)
'page 0 RST
If Byte_read.7 = 0 Then
Print Msg_initfail
End If

'switch to page 3
Bhulp0 = Nic_cr_rd2 Or Nic_cr_stp
Bhulp0 = Bhulp0 Or Nic_cr_ps0
Bhulp0 = Bhulp0 Or Nic_cr_ps1
Call Write_rtl8019as(nic_cr , Bhulp0)

Bhulp0 = Nic_9346cr_eem0 Or Nic_9346cr_eem1
Call Write_rtl8019as(nic_9346cr , Bhulp0)

Call Write_rtl8019as(nic_config3 , 0)

Call Write_rtl8019as(nic_config2 , Nic_config2_bselb)

Call Write_rtl8019as(nic_9346cr , 0)
Waitms 255

Bhulp0 = Nic_cr_rd2 Or Nic_cr_stp
Call Write_rtl8019as(nic_cr , Bhulp0)
Waitms 2
Call Write_rtl8019as(nic_dcr , Dcrval) '58
Call Write_rtl8019as(nic_rbcr0 , &H00)
Call Write_rtl8019as(nic_rbcr1 , &H00)

'0000_0100 packets with broadcast destination address are accepted
Call Write_rtl8019as(nic_rcr , &H04) 'receive configuration register

Call Write_rtl8019as(nic_tpsr , Txstart) '&H40

'0000_0010 internal loopback
Call Write_rtl8019as(nic_tcr , &H02) 'transmit configuration register

Call Write_rtl8019as(nic_pstart , Rxstart) '&H46
Call Write_rtl8019as(nic_bnry , Rxstart) '&H46
Call Write_rtl8019as(nic_pstop , Rxstop)
Call Write_rtl8019as(nic_isr , &HFF)

Call Write_rtl8019as(nic_cr , &H61)
Waitms 2
Call Write_rtl8019as(nic_curr , Rxstart) '&H46
For Hulp1 = 1 To 6
Call Write_rtl8019as(hulp1 , Mymac(hulp1))
Next Hulp1

Call Write_rtl8019as(nic_cr , &H21)
Call Write_rtl8019as(nic_dcr , Dcrval) '58
Call Write_rtl8019as(nic_cr , &H22)
Call Write_rtl8019as(nic_isr , &HFF)

Call Write_rtl8019as(nic_imr , Imrval) 'interrupt mask register / 0001_0001

Call Write_rtl8019as(nic_tcr , Tcrval) 'transmit configuration register / 0000_0000

Bhulp0 = Nic_cr_sta Or Nic_cr_rd2
Call Write_rtl8019as(nic_cr , Bhulp0)

End Sub

Compile this and see a screen full of errors. That's where I got my gray hair from.

We are referring to a lot of variables that are neither in the M128def.dat or in our own Dim'ed variables. So we have to add some DIM's or in this case variables with a static value, so we use Constants.

I will try to explain picture above. In the memory-map 32 bytes are occupied by the RTL8019as. On the first address a command-register resides and with some bits in this command-register you can select pages. Four pages to be exact. On address &H10 there is also on all pages a RDMA-port and this is where the complete ethernetpackets are read and write. &H18 is the resetport. Also on all pages. By changing PS1 and PS0 in the Command-register I can select several pages. Not all 32 bytes in these four pages are Read/Write. Some you can read but while writing they have another function.

In the next part we will dimension all registers of the RTL8019as. Instead of using a DIM a CONST. The difference between DIM and CONST is that DIM-variables are stored in RAM and can be changed, CONST are stored in flash and have a fixed value. Is it a register then we will use a &Hxx  (hex-notation), is it a bit in a register then we will use a &Bxxxx_xxxx (binary notation).

'NIC_CR page 0/1/2/3 (ps1=x, ps0=x) x = depending on page

'
Const Nic_cr = &H00
'
Const Nic_cr_stp = &B0000_0001 'stop command - Power up default
Const Nic_cr_sta = &B0000_0010 'start command
Const Nic_cr_txp = &B0000_0100 'transmit a packet
Const Nic_cr_rd0 = &B0000_1000 'remote read
Const Nic_cr_rd1 = &B0001_0000 'remote write
Const Nic_cr_rd2 = &B0010_0000 'abort/complete remote DMA
Const Nic_cr_ps0 = &B0100_0000 'register page 0
Const Nic_cr_ps1 = &B1000_0000 'register page 1

Const Nic_rdmaport = &H10 'all pages
Const Nic_rstport = &H18 'all pages


'NIC_PSTART page 0 (ps1=0, ps0=0)
'
Const Nic_pstart = &H01
'

'NIC_PSTOP page 0 (ps1=0, ps0=0)
'
Const Nic_pstop = &H02
'

'NIC_BNRY page 0 (ps1=0, ps0=0)
'
Const Nic_bnry = &H03
'

'NIC_TPSR page 0 (ps1=0, ps0=0)
'
Const Nic_tpsr = &H04
'

'NIC_TBCR0 page 0 (ps1=0, ps0=0)
'
Const Nic_tbcr0 = &H05
'

'NIC_TBCR1 page 0 (ps1=0, ps0=0)
'
Const Nic_tbcr1 = &H06
'

'NIC_ISR page 0 (ps1=0, ps0=0)
'
Const Nic_isr = &H07
'
Const Nic_rdc = &B0100_0000


'NIC_RSAR0 page 0 (ps1=0, ps0=0)
'
Const Nic_rsar0 = &H08
'

'NIC_RSAR1 page 0 (ps1=0, ps0=0)
'
Const Nic_rsar1 = &H09
'

'NIC_rbcr0 page 0 (ps1=0, ps0=0)
'
Const Nic_rbcr0 = &H0A
'

'NIC_rbcr0 page 0 (ps1=0, ps0=0)
'
Const Nic_rbcr1 = &H0B
'

'NIC_rcr page 0 (ps1=0, ps0=0) receive configuration register
'
Const Nic_rcr = &H0C
'

'NIC_tcr page 0 (ps1=0, ps0=0) transmit configuration register
'
Const Nic_tcr = &H0D
'

'NIC_DCR page 0 (ps1=0, ps0=0)
'
Const Nic_dcr = &H0E
'

'NIC_IMR page 0 (ps1=0, ps0=0) interrupt mask register
'
Const Nic_imr = &H0F
'


'NIC_CURR page 1 (ps1=0, ps0=1)
'
Const Nic_curr = &H07
'

'NIC_9346CR page 3 (ps1=1, ps0=1)
'
Const Nic_9346cr = &H01
'
Const Nic_9346cr_eem1 = &B1000_0000
Const Nic_9346cr_eem0 = &B0100_0000
Const Nic_config2_bselb = &B0010_0000


'NIC_CONFIG2 page 3 (ps1=1, ps0=1)
'
Const Nic_config2 = &H05


'NIC_CONFIG3 page 3 (ps1=1, ps0=1)
'
Const Nic_config3 = &H06

Const Msg_initfail = "Init failed"
Const Dcrval = &H58
Const Txstart = &H40
Const Rxstart = &H46
Const Rxstop = &H60
Const Imrval = &H11
Const Tcrval = &H00


Dim Bhulp0 As Byte
Dim Hulp1 As Byte

And while we are at it, add our first array. The Mymac(6). It holds the MAC-address of this Ethernet board.

Dim Mymac(6) as byte

The MAC-address of the board is also static, but in Bascom-AVR arrays may not be used with constants, so we do it a bit different. Assigning values to this array. To be honest, for our initialization at the moment we don't need a MAC-address, but later on we can't do without it.

Mymac(1) = &H00
Mymac(2) = &H10
Mymac(3) = &H20
Mymac(4) = &H30
Mymac(5) = &H40
Mymac(6) = &H50

That's all. Now we have initialized the RTL8019as. An explanation of  all constants used will follow after the next section. But is the RTL8019as alive? 

In the main part of the program before the end put this line

Call init_rtl8019as

Compile the program and you will get this

In Bascom-AVR, I can hit Control-W to see the result of the compilation.
On the WebTiger I see a quick burning of ACT LED and after initializing I see the LINK LED burning. Have a networkcable in the RJ45-connector. If I remove that the LINK led turns off.


2182 bytes used till now. The rest of the options we will see later on.

Step 4: It's alive......

If you have a HUB or switch, connect a straight cable from PC to HUB or switch, and a straight cable from HUB or switch to Ethernet board. If you don't have a HUB or switch, you can use a crosscable between PC and ethernet board. If you want to know how a crosscable looks like, check www.sbprojects.net and go to the knowledge base.

Another way of doing things ;-)

We have to add an interrupt routine to our program. The RTL8019as is giving an interrupt when a Ethernet packet has arrived. 

 

' Sit and wait for an Interrupt from the RTL8019as

Enable Interrupts
Enable Int5
On Int5 Rtl8019as_interrupt
'start the NIC

Bhulp0 = Nic_cr_rd2 Or Nic_cr_sta
Call Write_rtl8019as(nic_cr , Bhulp0)

Do
Loop
End

Here interrupts are enabled and Int5 to be specific. When an Interrupt occurs there will be a jump to subroutine Rtl8019as_interrupt. The program will, when not interrupted by the RTL8019as, stay in the DO LOOP part of the program.

'Routine to handle an interrupt from the RTL8019as

Rtl8019as_interrupt:
    Print "Interrupt from RTL8019as"
    'reset the interrupt bits
    Call Write_rtl8019as(isr , &HFF)
    'start the NIC again
   Bhulp0 = Nic_cr_rd2 Or Nic_cr_sta
    Call Write_rtl8019as(cr, Bhulp0)
Return

It is an interrupt and the above code is the ISR, Interrupt Service Routine. We have to end that routine with a Return.

So, now, from a PC start a ping with a -t option. This means a continuous ping on your network.

On a PC, Start, Run, CMD, and then ping 192.168.0.109 -t. And this is what you should see on your Hyperterminal screen. On every ping you will receive an interrupt from the RTL801as.

 

And what do I see on the WebTiger? The ACT LED is blinking on all pings that passes the network. If you have more traffic on your network you will see that, the ACT LED is blinking more.

The code till this point can be found here: tutorial_webtiger_1.txt

Don't forget to put the right options in Bascom-AVR (Atmega128, External Access, Waitstate, HW/SW stac, 32 K external RAM etc.).

Step 5: What have we done so far?

The code size is about 2506 bytes until now. We have written data into the registers of the Realtek RTL8019as. Registers that are used during initialization, packet transmission and reception. The RTL8019as has a Ring Buffer. It's a classic circular, head and tail buffer with four pointers:
  • Pstart
  • Pstop
  • Curr
  • Bnry

 

  • Pstart (Page Start) is the beginning address of the Ring Buffer. Pstop (Page Stop) the address of the end. Curr (Current Page Pointer) points to the next available buffer area for the next incoming frame. BNRY (Boundary Pointer) points to the next frame to be unloaded from the Ring Buffer.

    Here some more detail. There are several register pages in the RTL8019as.
    &H followed by a number will tell you at what position of the page
    After that is it Read, Write or Read and Write
    After that on which pages it can be found.

    If you want more detail about the registers check the datasheet of the RTL8019as which can be found at https://realtek.info/pdf/8019as.pdf

  • Pstart
    Page Start Register (&h01; Type = W in Page0, Type = R in Page2)
    The page Start Register sets the start page address of the receive buffer ring.
  • Pstop
    Page Stop Register (&h02; Type = W in Page 0, Type = R in Page 2)
    The Page Stop register sets the stop page address of the receive buffer ring. In 8 bit mode the Pstop register should not exceed to &h60, in 16 bit mode the Pstop register should not exceed to &h80
  • Bnry
    Boundary Register (&h03; Type = R/W in Page 0)
    This register is used to prevent overwirte of the receive buffer ring. It is typically used as a pointer indicating the last receive buffer page the host has read.


  • TPSR
    Transmit Page Start Register (&h04; Type = W in Page 0)
    This register sets the start page address of the packet to be transmitted.
  • RBCDR0, 1
    Remote Byte Count Registers (&h0A and &h0B; Type = W in Page 0)
    These two registers set the data byte counts of remote DMA
  • PAR0-5
    Physical Address Registers (&h01 - &h06; Type = R/W in Page 1)
    These rgisters contain the Ethernet node address and are used to compare the destination address of incoming packets for acceptation or rejection. In our program we use the array MyMac() for it.
  • Curr:
    Current Page Register (&h07; Type = R/W in Page 1)
    This register points to the page address of the first receive buffer page to be used for a packet reception.
  • IMR
    Interrupt Mask Register (&h0F; Type = W in Page 0, Type = R in Page2)
    All bits correspond to the bits in the ISR register. Power Up = all 0's. Setting individual bits will enable the corresponding interrupts.
  • DCR
    Data Configuration Register (&h0E; Type = W in Page 0, Type = R in Page 2)
  • TCR
    Transmit Configuration Register (&h0D; Type = W in Page 0, Type = R in Page 2)
  • RCR
    Receive Configuration Register (&h0C; Type = W in Page 0, Type = R in Page 2)
  • CR
    Command Register (&h00; Type = R/W)
    This register is used to select register pages, enable or disable DMA operation and issue commands.
  • ISR
    Interrupt Status Register (&h07; Type = R/W in Page 0)
    This register reflects the NIC status. The host reads it to determine the cause of an interrupt. Individual bits are cleared by writing a "1" into the corresponding bit. It must be cleared after power up.
  • And here are the bits
    7 = RST
    This bit is set when NIC enters reset state and is cleared when a start command is issued to the CR. It is also set when receibe buffer overflowd and is cleared when one or more packets have been read from the buffer.
    6 = RDC
    Set when remote DMA operation has been completed.
    5 = CNT
    Set when MSB of one of more of the network tally counters has been set
    4 = OVW
    This bit is set when the receibe buffer has been exhausted
    3 = TXE
    Transmit error bit is set when a packet transmission is aborted due to excessive collisions
    2 = RXE
    This bit is set when a packet received with one or more of the following errors:
    - CRC error
    - Frame alignment error
    - Missed packet
    1 = PTX
    This bit indicates packet transmitted with no errors.
    0 = PRX
    This bit indicates packet received with no errors.



    First we will make a small ulility to see all pages of the RTL8019as. But after that we are going to use the Interrupt Status Register bits to get a packet and to check for a overrun. 

 

Step 6: A small utility: Showregs

We have to add another subroutine to show the contents of the RTL8019as registers.

' Routine show the contents of the RTL8019as registers
'
Sub Showregs
Local Cntr1 As Byte
Local Cntr2 As Byte
Call Write_rtl8019as(nic_cr , &H21)
Print
Print "Realtek 8019AS Register dump"
Print
Print "REG Page0 Page1 Page2 Page3"
Print
For Cntr1 = 0 To 15
Cntr2 = Cntr1
Print Hex(cntr2);
Print " ";
Call Write_rtl8019as(nic_cr , &H21)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read);
Print " ";
Call Write_rtl8019as(nic_cr , &H61)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read);
Print " ";
Call Write_rtl8019as(nic_cr , &HA1)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read);
Print " ";
Call Write_rtl8019as(nic_cr , &HE1)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read)
Next Cntr1
End Sub

With the write_rtl8019as-instruction with &h21, &h61, &hA1 and &hE1, we instruct the ethernetcontroller to go to the different pages of registers. At each page we collect 16 values.
Notice the use of LOCAL, if you are sure you wouldn't need variables at other places in your program, you can use locals. The space they take in the internal RAM of the microcontroller is given back when leaving the subroutine.

At the top of the program, at the place we have declared the other subroutines, place

Declare Sub Showregs

And in our main piece of program we have to add a call to showregs, and a stop to halt the program.

Call Init_rtl8019as
Call Showregs
Stop

So that piece will look like the above part.

If you compile it and send it to the Atmega128,
you will have the next screen on your hyperterminal

Picture below: Notice at the red parts, on page 1 the MAC-address we gave. MyMac().
HEX 00, 10, 20, 30, 40 and 50.

The 50 70 at page 0 is the ID for a Realtek RTL8019as

If you got your code all mixed up, here you can download the source to the point we have arrived now.

tutorial_webtiger_2.txt

Step 7: Get a packet from the RTL8019as

Now we got the interrupt from the RTL8019as when a packet arrives, we have to grab the packet for further analyzes

Replace the Rtl8019as_interrupt-routine with the follow code:

' Routine to handle an interrupt on the RTL8019AS
'
Rtl8019as_interrupt:
#if Debug = 1
Print "Interrupt from RTL8019as"
#endif
Disable Int5
'read the interrupt status register
Call Read_rtl8019as(nic_isr)
'if the receive buffer has been overrun
'page 0 - ISR OVW (OVR - receive buffer has been exhausted)
If Byte_read.4 = 1 Then
Call Overrun
End If
'if the receive buffer holds a good packet
'page 0 - ISR PRX (PRX - received the packet with no errors)
If Byte_read.0 = 1 Then
Call Getpacket
End If
'make sure the receive buffer ring is empty. If BNRY = CURR, the buffer is empty
Call Read_rtl8019as(nic_bnry)
Data_l = Byte_read

Bhulp0 = Nic_cr_ps0 Or Nic_cr_rd2
Bhulp0 = Bhulp0 Or Nic_cr_sta
'switch to page 1
Call Write_rtl8019as(nic_cr , Bhulp0)

Call Read_rtl8019as(nic_curr)
Data_h = Byte_read

Bhulp0 = Nic_cr_rd2 Or Nic_cr_sta
Call Write_rtl8019as(nic_cr , Bhulp0)

'buffer is not empty, get next packet
If Data_l <> Data_h Then
Call Getpacket
End If

'reset the interrupts bits
Call Write_rtl8019as(nic_isr , &HFF)

Bhulp0 = Nic_cr_rd2 Or Nic_cr_sta
Call Write_rtl8019as(nic_cr , Bhulp0)
Enable Int5
Return

And add two dummy-subroutines

Dim Data_l As Byte
Dim Data_h As Byte

Sub Overrun
#if Debug = 1
     Print "Sub Overrun"
#endif
End Sub

Sub Getpacket
#if Debug = 1
     Print "Sub Getpacket"
#endif
End Sub

And at the declare part of this program, add the line

Declare Sub Overrun
Declare Sub Getpacket

Remove the 'Call showregs' and 'Stop' commands.

During the interrupt-handling, we are disabling the interrupt on Int5. We don't want the information to be grabbed garbled. After that we check bit 0 if the Interrupt Service Register if a good packet is in the RTL8019as. If it is, we call a subroutine, at the moment doing nothing, and after resetting the Interrupt bits and starting the RTL8019as again we continue with the enabling of Int5. 

After compiling and sending to the WebTiger I see this on the Hyperterminal screen

Step 7: Get your motor running...

Now it is getting interesting... First of all, we are going to reserve an array called packet() to receive the contents of the RTL8019as ethernet packet. 

Dim Packet(1500) as Byte

At the moment we have got two dummy subroutines. Getpacket and Overrun. In the Getpacket subroutine I have to convert a low-byte and a high-byte to a Word. In Bascom-AVR that can easily be done using overlays.

Add this at the DIM-part of the program:

Dim Result16 As Word
Dim Result16h As Byte At Result16 + 1 Overlay
Dim Result16l As Byte At Result16 Overlay

What we do here is we dimension a WORD. After that we overlay this WORD with two BYTES, high-part and low-part. By filling the two Bytes we get a full WORD, and if we want to split a WORD into two Bytes we can fill just Result16 and have the result in Resul16h and Result16l. I love Bascom-AVR for these little tricks.

We have to add some more variables for the Getpacket subroutine

Dim I as Integer
Dim Rxlen as Word
Dim Hulp2 as Word

Dim Pageheader(4) as Byte
Dim T_enetpacketlenl As Byte At Pageheader + 2 Overlay
Dim T_enetpacketlenh As Byte At Pageheader + 3 Overlay

Const Rdc = &H40

Oke, here is the complete Getpacket-routine. Replace the dummy Getpacket subroutine with this one.

' Routine to execute send packet command to retrieve the packet

Sub Getpacket
Call Write_rtl8019as(nic_cr , &H1A)
For I = 0 To 4
    Call Read_rtl8019as(nic_rdmaport)
    Pageheader(i + 1) = Byte_read
Next I
'watch it. overlay variables
Result16h = T_enetpacketlenh
Result16l = T_enetpacketlenl
Rxlen = Result16
Hulp2 = Rxlen + 1
For I = 1 To Hulp2
    Call Read_rtl8019as(nic_rdmaport)
    'dump any bytes that will overrun the receive buffer
    If I < 1500 Then
        Packet(i + 1) = Byte_read
    End If
Next I
Hulp1 = Byte_read And Rdc
If Hulp1 <> 64 Then
    Call Read_rtl8019as(nic_isr)
End If
Call Write_rtl8019as(nic_isr , &HFF)
End Sub

Send packet command is written to the RTL8019as, Length of the packet is retrieved. Packet-array is filled with the packet from the RTL8019as.

And while we are busy here is the Overrun subroutine.
Place this at the DIM-part

Dim Resend as Byte
Dim Hulp3 as Word
Dim Hulp4 as Byte

And place this under the END statement

' Routine to handle an overrun
'
Sub Overrun
#if Debug = 1
    Print "Sub Overrun"
#endif
Call Read_rtl8019as(nic_cr)
Data_l = Byte_read
Call Write_rtl8019as(nic_cr , &H21)
Waitms 2
Call Write_rtl8019as(nic_rbcr0 , &H00)
Call Write_rtl8019as(nic_rbcr1 , &H00)
Hulp1 = Data_l And &H04
If Hulp1 <> 0 Then
        Resend = 0
    Else
        If Hulp1 = 0 Then
            Call Read_rtl8019as(nic_isr)
            Data_l = Byte_read
            Hulp1 = Data_l And &H02
            Hulp4 = Data_l And &H08
            Hulp3 = Hulp1 Or Hulp4
            If Hulp3 > 0 Then
            Resend = 0
        Else
            Resend = 1
        End If
    End If
End If
Call Write_rtl8019as(nic_tcr , &H02) 'transmit configuration register

Bhulp0 = Nic_cr_rd2 Or Nic_cr_sta
Call Write_rtl8019as(nic_cr , Bhulp0)

Call Write_rtl8019as(nic_bnry , Rxstart) '&H46
Bhulp0 = Nic_cr_ps0 Or Nic_cr_rd2
Bhulp0 = Bhulp0 Or Nic_cr_sta
Call Write_rtl8019as(nic_cr , Bhulp0)

Call Write_rtl8019as(nic_curr , Rxstart) '&H46

Bhulp0 = Nic_cr_rd2 Or Nic_cr_sta
Call Write_rtl8019as(nic_cr , Bhulp0)

'0001_0000 OVW
Call Write_rtl8019as(nic_isr , &H10)

Call Write_rtl8019as(nic_tcr , Tcrval) 'transmit configuration register / 0000_0000
End Sub

 

Step 8: Array Packet and its overlays...

And now we are overlaying this array packet with a lot of variables.

'Ethernet packet destination
Dim T_enetpacketdest0 As Byte At Packet Overlay
Dim T_enetpacketdest1 As Byte At Packet + &H01 Overlay
Dim T_enetpacketdest2 As Byte At Packet + &H02 Overlay
Dim T_enetpacketdest3 As Byte At Packet + &H03 Overlay
Dim T_enetpacketdest4 As Byte At Packet + &H04 Overlay
Dim T_enetpacketdest5 As Byte At Packet + &H05 Overlay
'Ethernet packet source
Dim T_enetpacketsrc0 As Byte At Packet + &H06 Overlay
Dim T_enetpacketsrc1 As Byte At Packet + &H07 Overlay
Dim T_enetpacketsrc2 As Byte At Packet + &H08 Overlay
Dim T_enetpacketsrc3 As Byte At Packet + &H09 Overlay
Dim T_enetpacketsrc4 As Byte At Packet + &H0A Overlay
Dim T_enetpacketsrc5 As Byte At Packet + &H0B Overlay
'Ethernet packet type
Dim T_enetpackettype As Word At Packet + &H0C Overlay
Dim T_arp_hwtype1 As Byte At Packet + &H0F Overlay
'Arp
Dim T_arp_prttype1 As Byte At Packet + &H11 Overlay
Dim T_arp_hwlen As Byte At Packet + &H12 Overlay
Dim T_arp_prlen As Byte At Packet + &H13 Overlay
Dim T_arp_op1 As Byte At Packet + &H15 Overlay
'arp source ip address
Dim T_arp_sipaddr0 As Byte At Packet + &H1C Overlay
Dim T_arp_sipaddr1 As Byte At Packet + &H1D Overlay
Dim T_arp_sipaddr2 As Byte At Packet + &H1E Overlay
Dim T_arp_sipaddr3 As Byte At Packet + &H1F Overlay
'arp target IP address
Dim T_arp_tipaddr As Long At Packet + &H26 Overlay
'IP header layout IP version and header length
Dim T_ip_vers_len As Byte At Packet + &H0E Overlay

Dim T_arp_hwtype0 As Byte At Packet + &H0E Overlay
'Arp
Dim T_arp_prttype0 As Byte At Packet + &H10 Overlay
Dim T_arp_op0 As Byte At Packet + &H14 Overlay
'arp source mac address
Dim T_arp_src_enetpacket0 As Byte At Packet + &H16 Overlay
Dim T_arp_src_enetpacket1 As Byte At Packet + &H17 Overlay
Dim T_arp_src_enetpacket2 As Byte At Packet + &H18 Overlay
Dim T_arp_src_enetpacket3 As Byte At Packet + &H19 Overlay
Dim T_arp_src_enetpacket4 As Byte At Packet + &H1A Overlay
Dim T_arp_src_enetpacket5 As Byte At Packet + &H1B Overlay
'arp source mac address
Dim T_arp_dest_enetpacket0 As Byte At Packet + &H20 Overlay
Dim T_arp_dest_enetpacket1 As Byte At Packet + &H21 Overlay
Dim T_arp_dest_enetpacket2 As Byte At Packet + &H22 Overlay
Dim T_arp_dest_enetpacket3 As Byte At Packet + &H23 Overlay
Dim T_arp_dest_enetpacket4 As Byte At Packet + &H24 Overlay
Dim T_arp_dest_enetpacket5 As Byte At Packet + &H25 Overlay
Dim T_tos As Byte At Packet + &H0F Overlay
'packet length
Dim T_ip_pktlen0 As Byte At Packet + &H10 Overlay
Dim T_ip_pktlen1 As Byte At Packet + &H11 Overlay

Dim T_id0 As Byte At Packet + &H12 Overlay
Dim T_id1 As Byte At Packet + &H13 Overlay
Dim T_flags As Byte At Packet + &H14 Overlay
Dim T_offset As Byte At Packet + &H15 Overlay
Dim T_ttl As Byte At Packet + &H16 Overlay

'protocol (ICMP=1, TCP=6, UDP=11)
Dim T_ip_proto As Byte At Packet + &H17 Overlay

'header checksum
Dim T_ip_hdr_cksum0 As Byte At Packet + &H18 Overlay
Dim T_ip_hdr_cksum1 As Byte At Packet + &H19 Overlay
Dim T_ip_hdr_cksum As Word At Packet + &H18 Overlay
'IP address of source
Dim T_ip_srcaddr0 As Byte At Packet + &H1A Overlay
Dim T_ip_srcaddr1 As Byte At Packet + &H1B Overlay
Dim T_ip_srcaddr2 As Byte At Packet + &H1C Overlay
Dim T_ip_srcaddr3 As Byte At Packet + &H1D Overlay

Dim T_ip_srcaddr As Long At Packet + &H1A Overlay

'IP address of destination
Dim T_ip_destaddr0 As Byte At Packet + &H1E Overlay
Dim T_ip_destaddr1 As Byte At Packet + &H1F Overlay
Dim T_ip_destaddr2 As Byte At Packet + &H20 Overlay
Dim T_ip_destaddr3 As Byte At Packet + &H21 Overlay

Dim T_ip_destaddr As Long At Packet + &H1E Overlay

Dim T_icmp_type As Byte At Packet + &H22 Overlay
Dim T_icmp_code As Byte At Packet + &H23 Overlay
Dim T_icmp_cksum0 As Byte At Packet + &H24 Overlay
Dim T_icmp_cksum1 As Byte At Packet + &H25 Overlay
Dim T_icmp_cksum As Word At Packet + &H24 Overlay

Dim Tcp_srcporth As Byte At Packet + &H22 Overlay
Dim Tcp_srcportl As Byte At Packet + &H23 Overlay

Dim Tcp_destporth As Byte At Packet + &H24 Overlay
Dim Tcp_destportl As Byte At Packet + &H25 Overlay

Dim Tcp_seqnum3 As Byte At Packet + &H26 Overlay
Dim Tcp_seqnum2 As Byte At Packet + &H27 Overlay
Dim Tcp_seqnum1 As Byte At Packet + &H28 Overlay
Dim Tcp_seqnum0 As Byte At Packet + &H29 Overlay

Dim Tcp_acknum3 As Byte At Packet + &H2A Overlay
Dim Tcp_acknum2 As Byte At Packet + &H2B Overlay
Dim Tcp_acknum1 As Byte At Packet + &H2C Overlay
Dim Tcp_acknum0 As Byte At Packet + &H2D Overlay

Dim Tcp_hdr As Byte At Packet + &H2E Overlay
Dim Tcp_flags As Byte At Packet + &H2F Overlay
Dim Tcp_cksumh As Byte At Packet + &H32 Overlay
Dim Tcp_cksuml As Byte At Packet + &H33 Overlay
Dim Tcp_cksum As Word At Packet + &H32 Overlay
'UDP header
Dim T_udp_srcport0 As Byte At Packet + &H22 Overlay
Dim T_udp_srcport1 As Byte At Packet + &H23 Overlay

Dim T_udp_srcport As Word At Packet + &H22 Overlay

Dim T_udp_destport0 As Byte At Packet + &H24 Overlay
Dim T_udp_destport1 As Byte At Packet + &H25 Overlay

Dim T_udp_destport As Word At Packet + &H24 Overlay

Dim T_udp_len0 As Byte At Packet + &H26 Overlay
Dim T_udp_len1 As Byte At Packet + &H27 Overlay
Dim T_udp_chksum0 As Byte At Packet + &H28 Overlay
Dim T_udp_chksum1 As Byte At Packet + &H29 Overlay
Dim T_udp_data As Byte At Packet + &H2A Overlay

Dim T_udp_data1 As Byte At Packet + &H2B Overlay
Dim T_udp_data2 As Byte At Packet + &H2C Overlay
Dim T_udp_data3 As Byte At Packet + &H2D Overlay
Dim T_udp_data4 As Byte At Packet + &H2E Overlay
Dim T_udp_data5 As Byte At Packet + &H2F Overlay
Dim T_udp_data6 As Byte At Packet + &H30 Overlay
Dim T_udp_data7 As Byte At Packet + &H31 Overlay
Dim T_udp_data8 As Byte At Packet + &H32 Overlay
Dim T_udp_data9 As Byte At Packet + &H33 Overlay
Dim T_udp_data10 As Byte At Packet + &H34 Overlay
Dim T_udp_data11 As Byte At Packet + &H35 Overlay
Dim T_udp_data12 As Byte At Packet + &H36 Overlay
Dim T_udp_data13 As Byte At Packet + &H37 Overlay
Dim T_udp_data14 As Byte At Packet + &H38 Overlay
Dim T_udp_data15 As Byte At Packet + &H39 Overlay
Dim T_udp_data16 As Byte At Packet + &H3A Overlay
Dim T_udp_data17 As Byte At Packet + &H3B Overlay
Dim T_udp_data18 As Byte At Packet + &H3C Overlay
Dim T_udp_data19 As Byte At Packet + &H3D Overlay
Dim T_udp_data20 As Byte At Packet + &H3E Overlay
Dim T_udp_data21 As Byte At Packet + &H3F Overlay
Dim T_udp_data22 As Byte At Packet + &H40 Overlay
Dim T_udp_data23 As Byte At Packet + &H41 Overlay
Dim T_udp_data24 As Byte At Packet + &H42 Overlay
Dim T_udp_data25 As Byte At Packet + &H43 Overlay
Dim T_udp_data26 As Byte At Packet + &H44 Overlay
Dim T_udp_data27 As Byte At Packet + &H45 Overlay
Dim T_udp_data28 As Byte At Packet + &H46 Overlay
Dim T_udp_data29 As Byte At Packet + &H47 Overlay
Dim T_udp_data30 As Byte At Packet + &H48 Overlay
Dim T_udp_data31 As Byte At Packet + &H49 Overlay
Dim T_udp_data32 As Byte At Packet + &H4A Overlay

We can, without much effort, read and write in the array packet. At some places we can choose to write two BYTES or a single WORD (see for example T_udp_chksum)

And here is another funny one. At the DIM-part insert the next two lines. Some reservations for our IP-number we are going to use.

Dim Myip(4) As Byte
Dim My_ip As Long At Myip Overlay

Myip(1)=192
Myip(2)=168
Myip(3)=0
Myip(4)=100

Have to add some more constants at the Const-part

'IP Protocol types
'icmp
Const Prot_icmp = &H01
'tcp
Const Prot_tcp = &H06
'udp
Const Prot_udp = &H11


When I compile this the total bytes are around 4114 bytes.
3% of the Atmega128's flash.

We are almost there to receive our first packet. After step 11 I will put the complete source as we have it till now. 

Step 11: ARP, ICMP, UDP or TCP?

In the Getpacket routine we have to make some decisions, the packet that is arriving is it ARP, ICMP (ping), UDP or TCP? We have to add a few lines and the complete subroutine will look like this:

' Routine to execute send packet command to retrieve the packet
'
Sub Getpacket
#if Debug = 1
     Print "Sub Getpacket"
#endif
Call Write_rtl8019as(nic_cr , &H1A)
For I = 0 To 4
     Call Read_rtl8019as(nic_rdmaport)
     Pageheader(i + 1) = Byte_read
Next I
'watch it. overlay variables
Result16h = T_enetpacketlenh
Result16l = T_enetpacketlenl
Rxlen = Result16
Hulp2 = Rxlen + 1
For I = 1 To Hulp2
     Call Read_rtl8019as(nic_rdmaport)
     'dump any bytes that will overrun the receive buffer
     If I < 1500 Then
          Packet(i + 1) = Byte_read
     End If
Next I
Hulp1 = Byte_read And Nic_rdc
If Hulp1 <> 64 Then
     Call Read_rtl8019as(nic_isr)
End If

Call Write_rtl8019as(nic_isr , &HFF)


' Routine to process an ARP reply/request
'
If T_enetpackettype = &H0608 Then
     If T_arp_hwtype1 = &H01 Then
          If T_arp_prttype1 = &H00 Then
               If T_arp_hwlen = &H06 Then
                    If T_arp_prlen = &H04 Then
                         If My_ip = T_arp_tipaddr Then
                              If T_arp_op1 = &H02 Then
                                   Call Arp_reply
                              End If
                              If T_arp_op1 = &H01 Then
                                   Call Arp
                              End If
                         End If
                    End If
               End If
          End If
     End If
End If


' Routine to go ahead with icmp or udp
'
If T_enetpackettype = &H0008 Then
     If T_ip_destaddr = My_ip Then
          Select Case T_ip_proto
               Case Prot_icmp : Call Icmp
               Case Prot_tcp : Call Tcp
               Case Prot_udp : Call Udp_receive
          End Select
     End If
End If

End Sub

To get this compiled we have to add a few dummy routines:

At the declare-part of the program

Declare Sub Arp
Declare Sub Arp_reply
Declare Sub Icmp
Declare Sub Udp
Declare sub Tcp

At the subroutine-part of the program

Sub Arp
End Sub

Sub Arp_reply
End Sub

Sub Icmp
End Sub

Sub Udp
End Sub

Sub Tcp
End Sub

We are nearly there......

Step 12: ARP
Address Resolution Protocol (arp)

The address resolution protocol (arp) is a protocol used by the Internet Protocol (IP), specifically IPv4, to map IP network addresses to the hardware addresses (MAC-address) used by a data link protocol. The protocol operates below the network layer as a part of the interface between the OSI network and OSI link layer. 

The term address resolution refers to the process of finding an address of a computer in a network. The address is "resolved" using a protocol in which a piece of information is sent by a client process executing on the local computer to a server process executing on a remote computer. The information received by the server allows the server to uniquely identify the network system for which the address was required and therefore to provide the required address. The address resolution procedure is completed when the client receives a response from the server containing the required address.

Dim T as byte

Declare sub Sub Write_dest_mac

And here is the ARP subroutine

' Routine to handle ARP-traffic
'
Sub Arp
#if Debug = 1
     Print "Sub Arp"
#endif
'Start the NIC
Call Write_rtl8019as(nic_cr , &H22)
'load beginning page for transmit buffer
Call Write_rtl8019as(nic_tpsr , Txstart) '&H40
'set start address for remote DMA operation
Call Write_rtl8019as(nic_rsar0 , &H00)
Call Write_rtl8019as(nic_rsar1 , &H40)
'clear the interrupts
Call Write_rtl8019as(nic_isr , &HFF)
'load data byte count for remote DMA
Call Write_rtl8019as(nic_rbcr0 , &H3C) '60 dec
Call Write_rtl8019as(nic_rbcr1 , &H00)
'do remote write operation
Call Write_rtl8019as(nic_cr , &H12)
'write destination MAC address
Call Write_dest_mac
'write source address
For I = 1 To 6
     Call Write_rtl8019as(nic_rdmaport , Mymac(i))
Next I
'arp target IP address
'arp_op1 = packet(&h16)
Packet(&H16) = &H02
For I = 1 To 10
     Hulp1 = &H0C + I
     T = Packet(hulp1)
     Call Write_rtl8019as(nic_rdmaport , T)
Next I
'write ethernet module mac address
For I = 1 To 6
     Call Write_rtl8019as(nic_rdmaport , Mymac(i))
Next I
'write myip
For I = 1 To 4
     Call Write_rtl8019as(nic_rdmaport , Myip(i))
Next I
'write remote mac address
Call Write_dest_mac
'write remote IP address
Call Write_rtl8019as(nic_rdmaport , T_arp_sipaddr0)
Call Write_rtl8019as(nic_rdmaport , T_arp_sipaddr1)
Call Write_rtl8019as(nic_rdmaport , T_arp_sipaddr2)
Call Write_rtl8019as(nic_rdmaport , T_arp_sipaddr3)
'write som pad characters to fill out the packet to the minimum length
For I = 0 To &H11
     Call Write_rtl8019as(nic_rdmaport , &H00)
Next I
'make sure the DMA operation has succesfully completed
Byte_read = 0
Do
     Hulp1 = Byte_read And Nic_rdc
     Call Read_rtl8019as(nic_isr)
Loop Until Hulp1 = 0
'load numbers of bytes to be transmitted
Call Write_rtl8019as(nic_tbcr0 , &H3C)
Call Write_rtl8019as(nic_tbcr1 , &H00)
'send the contents of the transmit buffer onto the network
Call Write_rtl8019as(nic_cr , &H24)
End Sub


' helper-routine
'
Sub Write_dest_mac
#if Debug = 1
Print "Sub Write_dest_mac"
#endif
Call Write_rtl8019as(nic_rdmaport , T_enetpacketsrc0)
Call Write_rtl8019as(nic_rdmaport , T_enetpacketsrc1)
Call Write_rtl8019as(nic_rdmaport , T_enetpacketsrc2)
Call Write_rtl8019as(nic_rdmaport , T_enetpacketsrc3)
Call Write_rtl8019as(nic_rdmaport , T_enetpacketsrc4)
Call Write_rtl8019as(nic_rdmaport , T_enetpacketsrc5)
End Sub


On a PC do a Start, Run, CMD

arp -a

to see the ARP-table. You will see the ARP-table with one or more Internet Addresses.

Do a PING to your WebTiger.

After that arp -a again
If your ARP-function is working you will see the Internet Address and Physical Address (MAC-address) of your WebTiger in the list.

From here on I continue with ICMP (ping), UDP and TCP. But now you have the base for your own applications. An 
  • Interrupt Service routine
  • Read/Write routines for the RTL8019as
  • an array called Packet with 1500 bytes read from the RTL8019as
  • a bunch of variables to every detail of the ethernetpacket header and
  • a response to an ARP-request.

Remarks

You can and should comment your program for clarity and your later sanity. You can use REM or ' followed by your comment. All statements after REM or ' are treated as comments so you cannot use statements on the same line after a REM statement.

Block comments can be used too:

'( start block comment
print "This will not be compiled
') end block comment

If you use these block comments to remark the complete
subroutine showregs, the total amount of flash that is used so far is:
6218 Bytes 

The sourcecode up to this point.

It is a small step......

Step 13: ICMP
We carry on with the next command PING.

PING

A utility to determine whether a specific IP address is accessible. It works by sending a packet to the specified address and waiting for a reply. PING is used primarily to troubleshoot Internet connections. There are many freeware and shareware Ping utilities available for personal computers.

It is often believed that "Ping" is an abbreviation for Packet Internet Groper, but Ping's author has stated that the names comes from the sound that a sonar makes.

There are a few new subroutines you have ta add:
ICMP, Setipaddrs, Icmp_checksum, Echopacket, Packetshape and IP_header_checksum.

Declare Sub Setipaddrs
Declare Sub Echopacket
Declare Sub Icmp_checksum
Declare Sub Packetshape
Declare Sub IP_header_checksum
Declare Sub General_part_checksum(byval Val1 As Byte , Byval Val2 As Word)

Dim Txlen as Word
Dim I_header_length As Word
Dim I_odd As Byte
Dim I_chksum32 As Long
Dim Hdr_chksum as Long

Dim Hulp6 As Word
Dim Hulp6h As Byte At Hulp6 + 1 Overlay
Dim Hulp6l As Byte At Hulp6 Overlay

Dim I_x As Word
Dim I_hulp1 As Word
Dim I_checksum16 As Word
Dim I_temp16 As Word

Dim I_value16 As Word
Dim I_value16h As Byte At I_value16 + 1 Overlay
Dim I_value16l As Byte At I_value16 Overlay

' PING-routine
'
Sub Icmp
#if Debug = 1
     Print "Sub Icmp"
#endif

'set echo reply
T_icmp_type = &H00
T_icmp_code = &H00
'setup the IP-header
Call Setipaddrs
Call Icmp_checksum
Call Echopacket
End Sub

' Routine to handle the source/destination address
'
Sub Setipaddrs
#if Debug = 1
     Print "Sub Setipaddrs"
#endif
T_ip_destaddr = T_ip_srcaddr
'make ethernet module IP address source address
T_ip_srcaddr = My_ip
Call Packetshape
Call Ip_header_checksum
End Sub

 

' Routine to echo a complete packet
'
Sub Echopacket
#if Debug = 1
      Print "Sub Echopacket"
#endif
Call Write_rtl8019as(nic_cr , &H22)
Call Write_rtl8019as(nic_tpsr , Txstart) '&H40
Call Write_rtl8019as(nic_rsar0 , &H00)
Call Write_rtl8019as(nic_rsar1 , &H40)
Call Write_rtl8019as(nic_isr , &HFF)
Hulp1 = T_enetpacketlenl - 4
Call Write_rtl8019as(nic_rbcr0 , Hulp1)
Call Write_rtl8019as(nic_rbcr1 , T_enetpacketlenh)
Call Write_rtl8019as(nic_cr , &H12)
Result16h = T_enetpacketlenh
Result16l = T_enetpacketlenl
Result16 = Result16 - 4
Txlen = Result16
'write the complete packet to the RTL8019AS from packet(1) to packet(txlen+1)
Hulp2 = Txlen + 1
For I = 1 To Hulp2
     Call Write_rtl8019as(nic_rdmaport , Packet(i))
Next I
Byte_read = 0
While Hulp1 <> 0
     Hulp1 = Byte_read And Nic_rdc
     Call Read_rtl8019as(nic_isr)
Wend
Hulp1 = T_enetpacketlenl - 4
Call Write_rtl8019as(nic_tbcr0 , Hulp1)
Call Write_rtl8019as(nic_tbcr1 , T_enetpacketlenh)
Call Write_rtl8019as(nic_cr , &H24)
End Sub

 

Sub Packetshape

'move hardware source address to destination address

T_enetpacketdest0 = T_enetpacketsrc0
T_enetpacketdest1 = T_enetpacketsrc1
T_enetpacketdest2 = T_enetpacketsrc2
T_enetpacketdest3 = T_enetpacketsrc3
T_enetpacketdest4 = T_enetpacketsrc4
T_enetpacketdest5 = T_enetpacketsrc5

' Make ethernet module mac address the source address
T_enetpacketsrc0 = Mymac(1)
T_enetpacketsrc1 = Mymac(2)
T_enetpacketsrc2 = Mymac(3)
T_enetpacketsrc3 = Mymac(4)
T_enetpacketsrc4 = Mymac(5)
T_enetpacketsrc5 = Mymac(6)
End Sub

' Routine to calculate a ICMP-checksum
'
Sub Icmp_checksum

'clear the ICMP checksum
T_icmp_cksum = &H00

'calculate the ICMP checksum
I_header_length = T_ip_pktlen1 - 20
'I_header_length = I_header_length - 20
I_odd = I_header_length Mod 2

'14 for MAC-part
'20 for IP-header
'start on 35
'ip_pktlen = 00 3c (60)
'icmp-packetlengte = ip_pktlen - ip_header
I_chksum32 = 0
'Total packetlength - ip_header - 1
Hulp6h = T_ip_pktlen0 
Hulp6l = T_ip_pktlen1
Hulp6 = Hulp6 + 13
For I_x = 35 To Hulp6 Step 2
     I_value16h = Packet(i_x)
     I_hulp1 = I_x + 1
     I_value16l = Packet(i_hulp1)
     I_value16 = Not I_value16
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     I_value16h = Packet(i_x)
     I_value16l = 0
     I_value16 = Not I_value16
     I_chksum32 = I_chksum32 + I_value16
End If
I_checksum16 = I_chksum32
I_temp16 = Highw(i_chksum32)
I_checksum16 = I_checksum16 + I_temp16
T_icmp_cksum0 = High(i_checksum16)
T_icmp_cksum1 = Low(i_checksum16)
End Sub

' Routine to calculate a IP-header checksum
'
Sub Ip_header_checksum
#if Debug = 1
     Print "Sub IP_header_checksum"
#endif
Local Ip_x As Byte
Local Ip_hulp1 As Byte
Local Ip_chksum32 As Long
Local Ip_checksum16 As Word
Local Ip_temp16 As Word
Local Ip_header_length As Byte
'calculate the IP header checksum
T_ip_hdr_cksum = &H00
' Hdr_chksum = 0
I_chksum32 = 0
Ip_header_length = T_ip_vers_len And &H0F
Ip_header_length = 4 * Ip_header_length

I_chksum32 = 0
I_odd = 0

Val1 = 15
Val2 = &H0E + Ip_header_length
Call General_part_checksum(val1 , Val2)
T_ip_hdr_cksum0 = Val3
T_ip_hdr_cksum1 = Val4
End Sub

' Overall routine for checksum
'
Sub General_part_checksum(byval Val1 As Byte , Byval Val2 As Word)
#if Debug = 1
Print "Sub General_part_checksum"
#endif
For I_x = Val1 To Val2 Step 2
I_value16h = Packet(i_x)
Hulp3 = I_x + 1
I_value16l = Packet(hulp3)
I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
Incr Val2
I_value16h = Packet(val2)
I_value16l = 0
I_chksum32 = I_chksum32 + I_value16
End If
I_checksum16 = Highw(i_chksum32)
I_checksum16 = I_checksum16 + I_chksum32 ' only 16 lower bits of i_chksum32 is taken...
I_checksum16 = Not I_checksum16
Val3 = High(i_checksum16)
Val4 = Low(i_checksum16)
End Sub

Ping at work. The first PING takes somewhat longer, it has to do with the ARP-routine. First the Mac-address of the IP-number must be fetched.

In Bascom-AVR, at Program, Show result (or with the hotkey Control-W) we can see that the flash used is 8274 Bytes, 6% of the flash of the Atmega128.

When I check the Hyperterm connection, it is still getting the Interrupt from RTL8019as message, this slows down things. So when I make the variable Debug 0, no messages will be send to the serial port and the Ping response will be faster. 7 and 4 ms. 7764 flash used, only 5%.

Here the source of the Bascom-AVR code till this point

Step 14: ICMP and IP Checksum calculation explained
First I show you some captures of Sniffer screens. I use Iris 4.0.

Total frame size is 74 byte. The MAC-address we are using 00-10-20-30-40-50 is seen as a Welch Allyn, Data Collection device.

 

 

Destination MAC-address and source address, followed by DoD IP.

Destination MAC-address

Source MAC-address

 

IP version 4 header. 20 byte long.

45 = IP version 4 (that's the 4 in this byte) and a length of 20 bytes (4 times 5)

Length 60 bytes

Protocol 1: ICMP

Checksum IP-header

ICMP-header

Type 0: echo reply

ICMP checksum

The payload of a ping, with no special parameters in Windows, 32 byte.

And what if you would do a PING -l 200 (a ping with a payload of 200 bytes)

Payload of 200 bytes

I will show the calculation of the IP-header-checksum and of the ICMP-checksum here:

This is only an example to be run through the Bascom-AVR Simulator.
Don't insert this in your main code!!

To explain how the IP-header-checksum is calculated we take the ip-header-checksum subroutine and simulate the calculation of this checksum. 

Make a new program in Bascom-AVR and cut and paste the program that follows in it.
Compile it, start the Bascom-AVR simulator, make the terminal window a bit bigger and run the program. You can follow the calculation of the ip-header-checksum.

'
'IP_header_checksum Example in the Simulator
'
' Routine to calculate a IP-header checksum
'

' The frame is 74 bytes long
Dim Packet(74) As Byte At &HC0

' temp variables, only used in this example
Dim X As Byte
Dim Y As Byte

' a 4 byte long
Dim Hdr_chksum As Long

' from the original program
Dim Hulp1 As Byte

' from the original program
' a WORD at &HB3, and two seperate BYTES overlaying it at &HB3 and &HB4
Dim Ip_value16 As Word At &HB3
Dim Ip_value16h As Byte At &HB4 Overlay
Dim Ip_value16l As Byte At &HB3 Overlay

'from the original program (in the frame it has the value &H45)
Dim T_ip_vers_len As Byte At &HCE Overlay

'header checksum
'this will be stored in packet &HD8-&HC0 (&H18) and &HD9-&HC0 (&H19)
Dim T_ip_hdr_cksum0 As Byte At &HD8 Overlay
Dim T_ip_hdr_cksum1 As Byte At &HD9 Overlay
Dim T_ip_hdr_cksum As Word At &HD8 Overlay

Declare Sub Ip_header_checksum

Restore Frame

'Read the frame of 74 bytes in the array Packet()
For X = 1 To 74
     Read Y
     Packet(x) = Y
     Print "Packet(" ; X ; ") = " ; Hex(y)
Next X

' Oke, lets go, to the original Ip_header_checksum subroutine
Call Ip_header_checksum

Print "That's it folks....."


End

Frame:
Data &H00 , &H00 , &HC5 , &H0E , &HEC , &H5A , &H00 , &H10 , &H20 , &H30 , &H40 , &H50 , &H08 , &H00 , &H45 , &H00
Data &H00 , &H3C , &H29 , &H2C , &H00 , &H00 , &H80 , &H01 , &H8F , &H40 , &HC0 , &HA8 , &H00 , &H64 , &HC0 , &HA8
Data &H00 , &HA0 , &H00 , &H00 , &H2F , &H5C , &H02 , &H00 , &H24 , &H00 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66
Data &H67 , &H68 , &H69 , &H6A , &H6B , &H6C , &H6D , &H6E , &H6F , &H70 , &H71 , &H72 , &H73 , &H74 , &H75 , &H76
Data &H77 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66 , &H67 , &H68 , &H69


Sub Ip_header_checksum
Local Ip_x As Byte
Local Ip_hulp1 As Byte
Local Ip_chksum32 As Long
Local Ip_checksum16 As Word
Local Ip_temp16 As Word
Local Ip_header_length As Byte
'calculate the IP header checksum
T_ip_hdr_cksum = &H00
Hdr_chksum = 0
Ip_header_length = T_ip_vers_len And &H0F
Ip_header_length = 4 * Ip_header_length

'
Print "T_ip_vers_len " ; Hex(t_ip_vers_len)
'
Print "Ip_header_length (hex) " ; Hex(ip_header_length)
'

Hulp1 = &H0E + Ip_header_length
Ip_chksum32 = 0
For Ip_x = 15 To Hulp1 Step 2
     Ip_value16h = Packet(ip_x)
     Print "Ip_value16 high " ; Hex(ip_value16h) ; " ";
     Ip_hulp1 = Ip_x + 1
     Ip_value16l = Packet(ip_hulp1)
     Print "Ip_value16 low " ; Hex(ip_value16l)

     Print "Ip_value16 " ; Hex(ip_value16) ; " ones complement will become ";
     Ip_value16 = Not Ip_value16
     Print Hex(ip_value16)
     Print "and is added to Ip_checksum32"
     Ip_chksum32 = Ip_chksum32 + Ip_value16
Next Ip_x
Print "At the end of this calculation Ip_chksum32 is " ; Hex(ip_chksum32)
Print
Print "Ip_chksum32 is 32 bit long"
Print
print "And we take only the least significant 16 bit of it"
Print
Ip_checksum16 = Ip_chksum32
Print "Ip_checksum16 = " ; Hex(ip_checksum16)
Print
Print "Now we take to most significant 16 bit of it"
Print
Ip_temp16 = Highw(ip_chksum32)
Print "Highw(ip_cksum32) = " ; Hex(ip_temp16 )
Print
Print "We add them together ";
Ip_checksum16 = Ip_checksum16 + Ip_temp16
Print Hex(ip_checksum16)
Print
Print "And then we split them in a Highbyte and a Lowbyte"
T_ip_hdr_cksum0 = High(ip_checksum16)
T_ip_hdr_cksum1 = Low(ip_checksum16)
Print
Print "High byte of Ip-header-checksum " ; Hex(t_ip_hdr_cksum0)
Print "Low byte of Ip-header-checksum " ; Hex(t_ip_hdr_cksum1)
Print
End Sub

Run this program through the simulator and see if all things are right. Remember, this is the ip_header

And the checksum of this header can be found here:

And here is a snapshot from the result in the Simulator

.

ICMP-checksum is next.... Remember, this is the ICMP-part

And this is the checksum

This is the checksum

This is only an example to be run through the Bascom-AVR Simulator.
Don't insert this in your main code!!

'
' ICMP_checksum Example in the Simulator
'
' Routine to calculate an ICMP-checksum
'

' The frame is 74 bytes long
Dim Packet(74) As Byte At &HC0

' temp variables, only used in this example
Dim X As Byte
Dim Y As Byte

Dim I_header_length As Word
Dim I_odd As Byte
Dim I_chksum32 As Long
Dim I_x As Word
Dim I_hulp1 As Word
Dim I_checksum16 As Word
Dim I_temp16 As Word

Dim Hulp6 As Word At &HAF
Dim Hulp6h As Byte At &HB0 Overlay
Dim Hulp6l As Byte At &HAF Overlay

Dim I_value16 As Word At &HB1
Dim I_value16h As Byte At &HB2 Overlay
Dim I_value16l As Byte At &HB1 Overlay

Dim T_ip_pktlen0 As Byte At &HD0 Overlay
Dim T_ip_pktlen1 As Byte At &HD1 Overlay

Dim T_icmp_cksum0 As Byte At &HE4 Overlay
Dim T_icmp_cksum1 As Byte At &HE5 Overlay
Dim T_icmp_cksum As Word At &HE4 Overlay

Declare Sub Icmp_checksum

Restore Frame

'Read the frame of 74 bytes in the array Packet()
For X = 1 To 74
     Read Y
     Packet(x) = Y
     Print "Packet(" ; X ; ") = " ; Hex(y)
Next X

Call Icmp_checksum

End

Frame:
Data &H00 , &H00 , &HC5 , &H0E , &HEC , &H5A , &H00 , &H10 , &H20 , &H30 , &H40 , &H50 , &H08 , &H00 , &H45 , &H00
Data &H00 , &H3C , &H29 , &H2C , &H00 , &H00 , &H80 , &H01 , &H8F , &H40 , &HC0 , &HA8 , &H00 , &H64 , &HC0 , &HA8
Data &H00 , &HA0 , &H00 , &H00 , &H2F , &H5C , &H02 , &H00 , &H24 , &H00 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66
Data &H67 , &H68 , &H69 , &H6A , &H6B , &H6C , &H6D , &H6E , &H6F , &H70 , &H71 , &H72 , &H73 , &H74 , &H75 , &H76
Data &H77 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66 , &H67 , &H68 , &H69


' Routine to calculate a ICMP-checksum
'
Sub Icmp_checksum
'clear the ICMP checksum
Print "You have to clear the ICMP-checksum first"
T_icmp_cksum = &H00
'calculate the ICMP checksum
Print
Print "IP-packetlength minus 20"
I_header_length = T_ip_pktlen1 - 20
'I_header_length = I_header_length - 20
Print
Print "The ICMP-payload combination can be a odd number."
Print "We have to pay attention to that while calculating the checksum."
I_odd = I_header_length Mod 2
'14 for MAC-part
'20 for IP-header
Print
Print "We start at Frame-position 35 to calculate the checksum"
'start on 35
'icmp-packetlengte = ip_pktlen - ip_header
I_chksum32 = 0
'Total packetlength - ip_header - 1
Hulp6h = T_ip_pktlen0
Hulp6l = T_ip_pktlen1
Hulp6 = Hulp6 + 13
Print
Print "Total packetlength is " ; Hulp6
Print
Print "This for/next loop goes from the first position of the ICMP-part to the end"
For I_x = 35 To Hulp6 Step 2
     I_value16h = Packet(i_x)
     Print "I_value16 high " ; Hex(i_value16h) ; " ";
     I_hulp1 = I_x + 1
     I_value16l = Packet(i_hulp1)
     Print "I_value16 low " ; Hex(i_value16l)
     I_value16 = Not I_value16
     Print Hex(i_value16)
     Print "and is added to I_chksum32"
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     Print
     Print "This is an important part."
     Print "If the total count of ICMP-part is ODD, the last byte has to become the high part"
     Print "and we have to put a zero in the low part. Took me some time to get this working."
     I_value16h = Packet(i_x)
     I_value16l = 0
     I_value16 = Not I_value16
     I_chksum32 = I_chksum32 + I_value16
End If
Print "At the end of this calculation I_chksum32 is " ; Hex(i_chksum32)
Print
Print "I_chksum32 is 32 bit long"
Print
Print "And we take only the least significant 16 bit of it"
Print
I_checksum16 = I_chksum32
Print "I_checksum16 = " ; Hex(i_checksum16)
Print
Print "Now we take to most significant 16 bit of it"
Print
I_temp16 = Highw(i_chksum32)
Print "Highw(i_cksum32) = " ; Hex(i_temp16 )
Print
Print "We add them together ";
I_checksum16 = I_checksum16 + I_temp16
Print Hex(i_checksum16)
Print
Print "And then we split them in a Highbyte and a Lowbyte"
T_icmp_cksum0 = High(i_checksum16)
T_icmp_cksum1 = Low(i_checksum16)
Print
Print "High byte of ICMP-checksum " ; Hex(t_icmp_cksum0)
Print "Low byte of ICMP-checksum " ; Hex(t_icmp_cksum1)
Print
End Sub

A snapshot of the Bascom-AVR simulator.

Step 14: UDP
After a big step 13, a small step 14 to get UDP running

First remove the dummy sub udp routine and insert the next UDP-routine:

' Routine to handle UDP-traffic
'
Sub Udp
If T_udp_destport0 = 0 Then
     If T_udp_destport1 = &H07 Then
          'build the IP header
          Call Setipaddrs
          'swap the UDP source and destinations port
          Swap T_udp_srcport0 , T_udp_destport0
          Swap T_udp_srcport1 , T_udp_destport1
          Call Udp_checksum
          Call Write_rtl8019as(nic_cr , &H22)
          Call Echopacket
     End If
End if

This is only a small UDP-routine that echo's the typed characters in the EDTP Electronics Internet Test Panel. The port 5000 isn't used yet, this is the UDP-echo on port 7.

The Udp-checksum routine looks like this:

' Routine to calculate the Udp-checksum
'
Sub Udp_checksum
T_udp_chksum0 = &H00
T_udp_chksum1 = &H00
'checksum TCP header
I_chksum32 = 0
I_value16h = T_ip_srcaddr0
I_value16l = T_ip_srcaddr1
I_chksum32 = I_chksum32 + I_value16
I_value16h = T_ip_srcaddr2
I_value16l = T_ip_srcaddr3
I_chksum32 = I_chksum32 + I_value16
I_value16h = T_ip_destaddr0
I_value16l = T_ip_destaddr1
I_chksum32 = I_chksum32 + I_value16
I_value16h = T_ip_destaddr2
I_value16l = T_ip_destaddr3
I_chksum32 = I_chksum32 + I_value16
'proto
I_chksum32 = I_chksum32 + T_ip_proto
'packet length
I_value16h = T_udp_len0
I_value16l = T_udp_len1
I_chksum32 = I_chksum32 + I_value16
I_odd = T_udp_len1 Mod 2
Result16h = T_udp_len0
Result16l = T_udp_len1
'udp_srcport0 = packet(&h23)
Hulp1 = &H23
Hulp2 = &H23 + Result16
Hulp2 = Hulp2 - 2
For I_x = Hulp1 To Hulp2 Step 2
     I_value16h = Packet(i_x)
     Hulp3 = I_x + 1
     I_value16l = Packet(hulp3)
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     Incr Hulp2
     I_value16h = Packet(hulp2)
     I_value16l = 0
     I_chksum32 = I_chksum32 + I_value16
End If
I_checksum16 = Highw(i_chksum32)
I_checksum16 = I_checksum16 + I_chksum32
' only 16 lower bits of i_chksum32 is taken...
I_checksum16 = Not I_checksum16
T_udp_chksum0 = High(i_checksum16)
T_udp_chksum1 = Low(i_checksum16)
End Sub

Oke. 8086 bytes used and already ARP, ICMP and UDP running. Ehm... 8086, sounds familiar...... 

And here is the Udp-subroutine if you want to use two different ports to send through.

' Routine to handle UDP-traffic
'
Sub Udp
If T_udp_destport0 = 0 Then
     If T_udp_destport1 = &H07 Then
          'build the IP header
          Call Setipaddrs
          'swap the UDP source and destinations port
          Swap T_udp_srcport0 , T_udp_destport0
          Swap T_udp_srcport1 , T_udp_destport1
          Call Udp_checksum
          Call Write_rtl8019as(nic_cr , &H22)
          Call Echopacket
     End If
     Else
          'From within a VB-program
          If T_udp_destport = 5000 Then
                    Select Case T_udp_data
                         'Case 0 : Call action0
                         'Case 1 : Call action1
                         'Case 2 : Call action2
                   End Select
          End If
End Sub

The top part echo's what has been send, the bottom part reacts on port 5000 and you could take some action.

Step 15: UDP Checksum calculation explained
In a few words, the UDP-checksum is calculated as follows:

Take the IP-number of the source
192.168 (as word)
add
0.100     (as word)

Add the IP-number of the destination
192.168 (as word)
add
0.160     (as word)

Add
T_ip_proto (as low part of a word)

Add
t_udp_len1 t_udp_len0 (as a word)

Add the payload from packet(&H23) to the end of the udp-packet.

Add the highword of this result (a long) and the lowword.

Do a one complement

Store LSByte and MSByte in T_udp_chksum0 and T_udp_chksum1

Step 16: TCP

What do we need to get TCP working:

Incoming flags: (1 byte)

FIN
SYN
RST
PSH
ACK
URG

Outgoing flags: (1 byte)

FIN
SYN
RST
PSH
ACK
URG

Incoming Destination port (1 word)
Outgoing Destination Port (1 word)

Incoming Source port (1 word)
Outgoing Source port (1 word)

Incoming sequence-number (4 byte)
Outgoing sequence-number (4 byte)

Incoming acknowledge-number (4 byte)
Outgoing acknowledge-number (4 byte)

Incoming TCP-data (n-bytes)
Outgoing TCP-data (n-bytes)

Incoming TCP-datalength (1 word)
Outgoing TCP-datalength (1 word)

TCP-chksum (1 word)

I will add some dimensions and show you exact where you can find them.

First we have to declare some routines

Declare Sub Http
Declare Sub Tcp_checksum
Declare Sub Send_tcp_packet
Declare Sub Setup_packet
Declare Sub Assemble_ack
Declare Sub Tcpseqtomyseq

 

Then we have to Dimension some variables.

Dim Client_seqnum As Long
Dim Client_seqnum0 As Byte At Client_seqnum Overlay
Dim Client_seqnum1 As Byte At Client_seqnum + 1 Overlay
Dim Client_seqnum2 As Byte At Client_seqnum + 2 Overlay
Dim Client_seqnum3 As Byte At Client_seqnum + 3 Overlay

Dim Incoming_ack As Long
Dim Incoming_ack0 As Byte At Incoming_ack Overlay
Dim Incoming_ack1 As Byte At Incoming_ack + 1 Overlay
Dim Incoming_ack2 As Byte At Incoming_ack + 2 Overlay
Dim Incoming_ack3 As Byte At Incoming_ack + 3 Overlay

Dim My_seqnum As Long
Dim My_seqnum0 As Byte At My_seqnum Overlay
Dim My_seqnum1 As Byte At My_seqnum + 1 Overlay
Dim My_seqnum2 As Byte At My_seqnum + 2 Overlay
Dim My_seqnum3 As Byte At My_seqnum + 3 Overlay

'
Dim Tempword As Word
Dim Tempwordh As Byte At Tempword + 1 Overlay
Dim Tempwordl As Byte At Tempword Overlay

Dim Tcp_fin As Bit
Dim Tcp_syn As Bit
Dim Tcp_rst As Bit
Dim Tcp_psh As Bit
Dim Tcp_ack As Bit
Dim Tcp_urg As Bit

Dim Tcpdatalen_in As Word
Dim Tcpdatalen_out As Word

Dim Checktemp As Word
Dim Checksumout As Word
Dim Ip_packet_len As Word
Dim Flags As Byte
Dim Msg_temp As String * 55
Dim Y As Word
Dim Tempstring1 As String * 1
Dim Expected_ack As Long

Put some constants in the code

Const Synflag = 0
Const Finflag = 1

And here we have all subroutines to get HTTP working

' TCP
'
Sub Tcp
Local Work As Byte
Work = Tcp_flags
Tcp_fin = Work.0
Tcp_syn = Work.1
Tcp_rst = Work.2
Tcp_psh = Work.3
Tcp_ack = Work.4
Tcp_urg = Work.5

If Tcp_destporth = 0 Then
     Select Case Tcp_destportl
          Case 80 : Call Http
          'more TCP-protocols could be put here
     End Select
End If
End Sub

The routine to handle HTTP-traffic

Sub Http
#if Debug = 1
     Print "HTTP"
     Print
     Print "Destination port low " ; Tcp_destportl
     Print "Destination port high " ; Tcp_destporth
     Print
     Print "TCPdatalen_in " ; Tcpdatalen_in
     Print
     Print "Flags:"
     Print "FIN " ; Tcp_fin
     Print "SYN " ; Tcp_syn
     Print "RST " ; Tcp_rst
     Print "PSH " ; Tcp_psh
     Print "ACK " ; Tcp_ack
     Print "URG " ; Tcp_urg
     Print
     Print "Incoming:"
     Print "Tcp_seqnum3 " ; Hex(tcp_seqnum3)
     Print "Tcp_seqnum2 " ; Hex(tcp_seqnum2)
     Print "Tcp_seqnum1 " ; Hex(tcp_seqnum1)
     Print "Tcp_seqnum0 " ; Hex(tcp_seqnum0)
     Print
     Print "Tcp_acknum3 " ; Hex(tcp_acknum3)
     Print "Tcp_acknum2 " ; Hex(tcp_acknum2)
     Print "Tcp_acknum1 " ; Hex(tcp_acknum1)
     Print "Tcp_acknum0 " ; Hex(tcp_acknum0)
#endif
Local Msg_temp2 As String * 10
Local Tempword3 As Word
Local Z As Word
Local Ztemp As Word
Local Tempstring2 As String * 5
Result16h = T_ip_pktlen0
Result16l = T_ip_pktlen1
'
' calculate IP header length
' MSN is version (IPv4)
' LSN is length
Hulp1 = T_ip_vers_len And &H0F
Hulp1 = Hulp1 * 4
' calculate TCP header length
' MSN is length / 4
Hulp2 = Tcp_hdr And &HF0
Shift Hulp2 , Right , 4
Hulp2 = Hulp2 * 4
Tcpdatalen_in = Result16 - Hulp1
Tcpdatalen_in = Tcpdatalen_in - Hulp2
' If an ACK is received and the destination port address is valid
' and no data is in the packet
If Tcp_ack = 1 Then
     If Tcpdatalen_in = 0 Then
          #if Debug = 1
               Print "Tcp_ack = 1 , Tcpdatalen_in = 0 "
          #endif
          Incoming_ack0 = Tcp_acknum0
          Incoming_ack1 = Tcp_acknum1
          Incoming_ack2 = Tcp_acknum2
          Incoming_ack3 = Tcp_acknum3
          If Flags.synflag = 1 Then
               Reset Flags.synflag
               My_seqnum = Incoming_ack
               Tempword3 = &H37
               Read the HTML-code untul you get a line with endblock
              
Restore Htmlcode
               Do
                    Read Msg_temp
                    Msg_temp2 = Right(msg_temp , 8)
                    If Msg_temp2 = "endblock" Then
                         Exit Do
                    End If
                    For Y = 1 To Len(msg_temp)
                         Tempstring1 = Mid(msg_temp , Y , 1)
                         Packet(tempword3) = Asc(tempstring1)
                         Incr Tempword3
                    Next Y
               Loop
               Tcpdatalen_out = Tempword3 - 55 'minus headerlength
             'expect to get an acknowledgment of the message
               Expected_ack = My_seqnum + Tcpdatalen_out
               ' send the TCP/IP packet
               Call Send_tcp_packet
           End If
     End If
End If

' This code segment processes the incoming SYN from the Tenet client ' and sends back the initial sequence number (ISN) and acknowledges ' the incoming SYN packet

If Tcp_syn = 1 Then
     #if Debug = 1
          Print "Tcp_syn = 1"
     #endif
     Tcpdatalen_in = 1
     Set Flags.synflag
     Call Setup_packet
     Swap Tcp_srcportl , Tcp_destportl
     Swap Tcp_srcporth , Tcp_destporth
     Call Assemble_ack
     Call Tcpseqtomyseq
     Tcp_flags = 0
     Set Tcp_flags.1
     Set Tcp_flags.4
     Call Tcp_checksum
     Call Echopacket
End If

If Tcp_fin = 1 Then
     #if Debug = 1
          Print "Tcp_fin = 1"
     #endif
End If

If Tcp_psh = 1 Then
     #if Debug = 1
          Print "Tcp_psh = 1"
     #endif
     ' walk through the packet until GET / is found
     For Z = 40 To 255
          Ztemp = Z
          Tempstring2 = Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          If Tempstring2 = "GET /" Then
                Ztemp = Ztemp + 301
                Exit For
          End If
          #if Debug = 1
               Print "/GET gevonden"
          #endif
     Next Z
     'ztemp > 300 when GET / is found
     'ztemp <= 300 when GET / not found
     'when found ztemp has the pointer to it (plus 300)
     If Ztemp > 300 Then
          Ztemp = Ztemp - 300
          If Chr(packet(ztemp)) = "R" Then
          Incr Ztemp
          Hulp1 = Val(packet(ztemp))
          Select Case Hulp1
               Case 1 To 4 : 'Call Relayon(hulp1)
               Case 5 To 8 : 'Call Relayoff(hulp1)
          End Select
     End If
     End If
End If
End Sub

 

Sub Tcp_checksum
Local Whulp1 As Word
Local Whulp2 As Word
Local Whulp3 As Word
Local Tempword2 As Word
Tcp_cksum = 0
I_chksum32 = 0
Tempwordh = T_ip_srcaddr0
Tempwordl = T_ip_srcaddr1
I_chksum32 = Tempword
Tempwordh = T_ip_srcaddr2
Tempwordl = T_ip_srcaddr3
I_chksum32 = I_chksum32 + Tempword
Tempwordh = T_ip_destaddr0
Tempwordl = T_ip_destaddr1
I_chksum32 = I_chksum32 + Tempword
Tempwordh = T_ip_destaddr2
Tempwordl = T_ip_destaddr3
I_chksum32 = I_chksum32 + Tempword
I_chksum32 = I_chksum32 + T_ip_proto
Tempwordh = T_ip_pktlen0
Tempwordl = T_ip_pktlen1
Tempword2 = T_ip_vers_len And &H0F
Tempword2 = Tempword2 * 4
I_chksum32 = I_chksum32 + Tempword
I_chksum32 = I_chksum32 - Tempword2

Whulp2 = Tempword - 20
Whulp2 = Whulp2 + &H23
Whulp2 = Whulp2 - 2
Val1 = &H23
Val2 = Whulp2
I_odd = Val2 - Val1
I_odd = I_odd Mod
2
Call General_part_checksum(val1 , Val2)
Tcp_cksumh = Val3
Tcp_cksuml = Val4
End Sub

 

' Send the TCP-packet, a bit different then echopacket
'
Sub Send_tcp_packet
Ip_packet_len = 40 + Tcpdatalen_out
T_ip_pktlen1 = Low(ip_packet_len)
T_ip_pktlen0 = High(ip_packet_len)
Call Setup_packet
Swap Tcp_srcporth , Tcp_destporth
Swap Tcp_srcportl , Tcp_destportl
Call Assemble_ack
Call Tcpseqtomyseq
Tcp_flags = 0
'fin_out
Set Tcp_flags.0 ' piggyback FIN onto the page date
'ack_out
Set Tcp_flags.4
If Flags.finflag = 1 Then
     'fin_out
     Set Tcp_flags.0
     'clear the fin flag
     Reset Flags.finflag
End If
Call Tcp_checksum
Txlen = Ip_packet_len + 14
If Txlen < 60 Then
     Txlen = 60
End If
Call Write_rtl8019as(nic_cr , &H22)
Call Write_rtl8019as(nic_tpsr , Txstart)
Call Write_rtl8019as(nic_rsar0 , &H00)
Call Write_rtl8019as(nic_rsar1 , &H40)
Call Write_rtl8019as(nic_isr , &HFF)
Hulp1 = Low(txlen)
Call Write_rtl8019as(nic_rbcr0 , Hulp1)
Hulp1 = High(txlen)
Call Write_rtl8019as(nic_rbcr1 , Hulp1)
Call Write_rtl8019as(nic_cr , &H12)
For I = 1 To Txlen
     Call Write_rtl8019as(nic_rdmaport , Packet(i))
Next
'make sure the DMA operation has succesfully completed
Byte_read = 0
Do
     Hulp1 = Byte_read And Rdc
     Call Read_rtl8019as(nic_isr)
     Loop Until Hulp1 = 0
     Hulp1 = Low(txlen)
     Call Write_rtl8019as(nic_tbcr0 , Hulp1)
Hulp1 = High(txlen)
Call Write_rtl8019as(nic_tbcr1 , Hulp1)
Call Write_rtl8019as(nic_cr , &H24)
End Sub

 

Sub Setup_packet
'Move IP source address to destination address
T_ip_destaddr = T_ip_srcaddr
'Make ethernet module IP address source address
T_ip_srcaddr = My_ip
'Move hardware source address to destination address
Call Packetshape
Call Ip_header_checksum
End Sub

Add the tcpdatalength in to the sequencenumber to get the acknowledgenumber

Sub Assemble_ack
Client_seqnum0 = Tcp_seqnum0
Client_seqnum1 = Tcp_seqnum1
Client_seqnum2 = Tcp_seqnum2
Client_seqnum3 = Tcp_seqnum3
Client_seqnum = Client_seqnum + Tcpdatalen_in
Tcp_acknum0 = Client_seqnum0
Tcp_acknum1 = Client_seqnum1
Tcp_acknum2 = Client_seqnum2
Tcp_acknum3 = Client_seqnum3
End Sub

Sub Tcpseqtomyseq
Tcp_seqnum0 = My_seqnum0
Tcp_seqnum1 = My_seqnum1
Tcp_seqnum2 = My_seqnum2
Tcp_seqnum3 = My_seqnum3
End Sub

And a small detail I nearly forgot, the HTML-code as data at the end of your program:

' htmlcode
'
Htmlcode:
Data "HTTP/1.0 200" , &H0D , &H0A , &H0D , &H0A
Data "<html><head>"
Data "<title>www.achatz.nl / WebCat</title></head>"
Data "<body style=" , &H22 , "font-family: Verdana; font-size: 8pt; "
Data "font-weight: Bold" , &H22 , " link=" , &H22 , "#000000" , &H22 , " vlink=" , &H22
Data "#000000" , &H22 , " alink=" , &H22 , "#000000" , &H22 , ">"
Data "<p><b><font size=" , &H22 , "5" , &H22
Data " color=" , &H22 , "green" , &H22 , ">www.achatz.nl - WebTiger<br><br>"
Data "<img src=" , &H22 , "https://benshobbycorner.nl/hobbycorner/images/walkingman.gif" , &H22 , ">"
Data "</body></html>" , &H0D , &H0A
Data "endblock"

The extra carriage return/line feed after HTTP/1.0 200 is important. Else the browser won't recognize this as a HTML-page.

 

 

This is what you get.... A single HTML-page. The picture is from an external source. A mysterious man walking.....

Here the code until now

If you compile the code and leave DEBUG 1, the size will be 13576 bytes. If you put a 0 in DEBUG, the size will be 12010  bytes.

Step 17: TCP checksum

In a few words the checksum-calculation of TCP is done as follows:

Add this
T_ip_srcaddr0 and T_ip_srcaddr1 as word
T_ip_srcaddr2 and T_ip_srcaddr3 as word
T_ip_destaddr0 and T_ip_destaddr1 as word
T_ip_destaddr2 and T_ip_destaddr3 as word
T_ip_proto as LSbyte of a word
T_ip_pktlen0 and T_ip_pktlen1 as word
The least significant nibble times 4 as LSByte of a word
And then we add the complete payload of the TCP-packet
When the total number of bytes is odd we add the last byte as a MSByte of a word

Because we have done this before on IP and ICMP checksum we use the General_part_checksum routine.
 

Step 18: STK500 and LCD.....

 

 

Connecting the cable for the LEDs (PORTD)

 

and putting the LCD on its socket (PORTB)

 

 

Step 19: Sample of an application: A HTML-page to switch on the STK500 Leds

The LEDS on the STK500 unit are yellow, so I used yellow and gray of LED ON and LED OFF.
The status of the LEDS remain in the variable Ledstatus and is copied to the PORTD.

 

Declare Sub Led_on(byval X As Byte)
Declare Sub Led_off(byval X As Byte)

' Routine to switch on LED X and update status
'
Sub Led_on(x)
Decr X
Ledstatus.x = 1
Portd = Ledstatus
End Sub


' Routine to switch off LED X and update status
'
Sub Led_off(x)
Decr X
Ledstatus.x = 0
Portd = Ledstatus
End Sub

' TCP
'
Sub Tcp
#if Debug = 1
     Print "Sub Tcp"
#endif

'WebTiger
'
Local Work As Byte
Work = Tcp_flags
Tcp_fin = Work.0
Tcp_syn = Work.1
Tcp_rst = Work.2
Tcp_psh = Work.3
Tcp_ack = Work.4
Tcp_urg = Work.5

If Tcp_destporth = 0 Then
     Select Case Tcp_destportl
          Case 80 : Call Http
     End Select
End If

If you want to use another port than the standard HTTP-port (80), you can change that at the Case 80:Call Http line.


End Sub

Sub Http
#if Debug = 1
     Print "HTTP"
     Print
     Print "Destination port low " ; Tcp_destportl
     Print "Destination port high " ; Tcp_destporth
     Print
     Print "TCPdatalen_in " ; Tcpdatalen_in
     Print
     Print "Flags:"
     Print "FIN " ; Tcp_fin
     Print "SYN " ; Tcp_syn
     Print "RST " ; Tcp_rst
     Print "PSH " ; Tcp_psh
     Print "ACK " ; Tcp_ack
     Print "URG " ; Tcp_urg
     Print
     Print "Incoming:"
     Print "Tcp_seqnum3 " ; Hex(tcp_seqnum3)
     Print "Tcp_seqnum2 " ; Hex(tcp_seqnum2)
     Print "Tcp_seqnum1 " ; Hex(tcp_seqnum1)
     Print "Tcp_seqnum0 " ; Hex(tcp_seqnum0)
     Print
     Print "Tcp_acknum3 " ; Hex(tcp_acknum3)
     Print "Tcp_acknum2 " ; Hex(tcp_acknum2)
     Print "Tcp_acknum1 " ; Hex(tcp_acknum1)
     Print "Tcp_acknum0 " ; Hex(tcp_acknum0)
#endif
Local Msg_temp2 As String * 10
Local Tempword3 As Word
Local Z As Word
Local Ztemp As Word
Local Tempstring2 As String * 5
Local Templong1 As Long

Result16h = T_ip_pktlen0
Result16l = T_ip_pktlen1
'
' calculate IP header length
' MSN is version (IPv4)
' LSN is length

Hulp1 = T_ip_vers_len And &H0F
Hulp1 = Hulp1 * 4
' calculate TCP header length
' MSN is length / 4

Hulp2 = Tcp_hdr And &HF0
Shift Hulp2 , Right , 4
Hulp2 = Hulp2 * 4
Tcpdatalen_in = Result16 - Hulp1
Tcpdatalen_in = Tcpdatalen_in - Hulp2
' If an ACK is received and the destination port address is valid
' and no data is in the packet

If Tcp_ack = 1 Then
     If Tcpdatalen_in = 0 Then
          Incoming_ack0 = Tcp_acknum0
          Incoming_ack1 = Tcp_acknum1
          Incoming_ack2 = Tcp_acknum2
          Incoming_ack3 = Tcp_acknum3
          If Flags.synflag = 1 Then
               Reset Flags.synflag
               My_seqnum = Incoming_ack
               Tempword3 = &H37
               If Dsp_present = 0 Then
                    Cls
                    Lcd "HTTP request"
                    Idletime = 0
               End If
               Restore Htmlcode
               Do
                    Read Msg_temp
                    Msg_temp2 = Right(msg_temp , 8)
                    If Msg_temp2 = "endblock" Then
                         Exit Do
                    End If
                    If Msg_temp2 = "get-ipnr" Then
                         Readeeprom Hulp1 , 23
                         If Hulp1 = &HFF Then
                              Msg_temp = "http://" + Str(myip(1))
                              Msg_temp = Msg_temp + "." + Str(myip(2))
                              Msg_temp = Msg_temp + "." + Str(myip(3))
                              Msg_temp = Msg_temp + "." + Str(myip(4))
                         Else
                              Msg_temp = ""

                              For Y = 1 To Hulp1
                                   Hulp3 = Y + 23
                                   Readeeprom Hulp2 , Hulp3
                                   Msg_temp = Msg_temp + Chr(hulp2)
                              Next Y

                        End If
                    End If

                    If Msg_temp2 = "get-info" Then
                         If Ledstatus.0 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                         If Ledstatus.1 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                         If Ledstatus.2 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                         If Ledstatus.3 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                         If Ledstatus.4 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                         If Ledstatus.5 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                         Tempstring1 = Mid(msg_temp , Y , 1)
                         Packet(tempword3) = Asc(tempstring1)
                         Incr Tempword3
                         Next Y
                         If Ledstatus.6 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                         If Ledstatus.7 = 1 Then
                              Msg_temp = "<td bgcolor=gray><b>OFF</td>"
                         Else
                              Msg_temp = "<td bgcolor=yellow><b>ON</td>"
                         End If
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                    Else
                         For Y = 1 To Len(msg_temp)
                              Tempstring1 = Mid(msg_temp , Y , 1)
                              Packet(tempword3) = Asc(tempstring1)
                              Incr Tempword3
                         Next Y
                    End If
               Loop
               Tcpdatalen_out = Tempword3 - 55 'minus headerlength
               ' expect to get an acknowledgment of the message
               Expected_ack = My_seqnum + Tcpdatalen_out
               ' send the TCP/IP packet
               Call Send_tcp_packet
          End If
     End If
End If


' This code segment processes the incoming SYN from the client
' and sends back the initial sequence number (ISN) and acknowledges
' the incoming SYN packet

If Tcp_syn = 1 Then
#if Debug = 1
Print "Tcp_syn = 1"
#endif
Tcpdatalen_in = 1
Set Flags.synflag
Call Setup_packet
Swap Tcp_srcportl , Tcp_destportl
Swap Tcp_srcporth , Tcp_destporth
Call Assemble_ack
Call Tcpseqtomyseq
Tcp_flags = 0
Set Tcp_flags.1
Set Tcp_flags.4
Call Tcp_checksum
Call Echopacket

End If

In this part the payload of the packet is checked for the word GET/


Had to do this because while testing we have seen that Windows-browsers and Linux-browser differ where exactly the GET/ occurs. After GET/ we can find the parameters.


Coketux

 

If Tcp_psh = 1 Then
' walk through the packet until GET / is found
For Z = 40 To 255
Ztemp = Z
Tempstring2 = Chr(packet(ztemp))
Incr Ztemp
Tempstring2 = Tempstring2 + Chr(packet(ztemp))
Incr Ztemp
Tempstring2 = Tempstring2 + Chr(packet(ztemp))
Incr Ztemp
Tempstring2 = Tempstring2 + Chr(packet(ztemp))
Incr Ztemp
Tempstring2 = Tempstring2 + Chr(packet(ztemp))

If Tempstring2 = "GET /" Then
Ztemp = Ztemp + 301
Exit For
End If
Next Z

'ztemp > 300 when GET / is found
'ztemp <= 300 when GET / not found
'when found ztemp has the pointer to it (plus 300)


     If Ztemp > 300 Then
          Ztemp = Ztemp - 300
          If Chr(packet(ztemp)) = "R" Then
               Incr Ztemp
               Hulp1 = Val(packet(ztemp))
               Select Case Hulp1
                    Case 1 To 8 : Call Led_on(hulp1)
               End Select
          End If
          If Chr(packet(ztemp)) = "A" Then
               Incr Ztemp
               Hulp1 = Val(packet(ztemp))
               Select Case Hulp1
                    Case 1 To 8 : Call Led_off(hulp1)
               End Select
          End If
     End If
End If

A parameter R is used to turn on a LED. A parameter A is used to turn off a LED.


If Tcp_fin = 1 Then
     Set Flags.finflag
     Incr Tcpdatalen_in
     Incoming_ack0 = Tcp_acknum0
     Incoming_ack1 = Tcp_acknum1
     Incoming_ack2 = Tcp_acknum2
     Incoming_ack3 = Tcp_acknum3
     If Incoming_ack <= Expected_ack Then
          Templong1 = Expected_ack - Incoming_ack
          My_seqnum = Expected_ack - Templong1
     End If
     Expected_ack = My_seqnum + Tcpdatalen_out
     Call Send_tcp_packet
End If
End Sub

To get this you have to use this as HTML-code

' htmlcode
'
Htmlcode:
Data "HTTP/1.0 200" , &H0D , &H0A , &H0D , &H0A
Data "<html><head><meta http-equiv=" , &H22 , "Refresh" , &H22 , " CONTENT=" , &H22 , "9;URL="

Data "get-ipnr"

Data &H22 , ">"
Data "<meta http-equiv=" , &H22 , "Pragma" , &H22 , " Content=" , &H22 , "no cache" , &H22 , ">"
Data "<title>WebTiger - www.achatz.nl</title></head>"
Data "<body style=" , &H22 , "font-family: Arial Black; font-size: 7pt; "
Data "color: #008000" , &H22 , " link=green vlink=green alink=green>"
Data "<table border=" , &H22 , "10" , &H22 , " width=" , &H22 , "600" , &H22
Data " bordercolor=black height=" , &H22 , "225" , &H22
Data " cellspacing=" , &H22 , "12" , &H22 , "><tr>"
Data "get-info"
Data "<tr>"
Data "<b><td>LED 0</td>"
Data "<td>LED 1</td>"
Data "<td>LED 2</td>"
Data "<td>LED 3</td>"
Data "<td>LED 4</td>"
Data "<td>LED 5</td>"
Data "<td>LED 6</td>"
Data "<td>LED 7</td>"
Data "</b></td><tr>"
Data "<td><a href=" , &H22 , "/R1" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R2" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R3" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R4" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R5" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R6" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R7" , &H22 , ">OFF</a></td>"
Data "<td><a href=" , &H22 , "/R8" , &H22 , ">OFF</a></td>"
Data "</tr><tr>"
Data "<td><a href=" , &H22 , "/A1" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A2" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A3" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A4" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A5" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A6" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A7" , &H22 , ">ON</a></td>"
Data "<td><a href=" , &H22 , "/A8" , &H22 , ">ON</a></td>"
Data "</tr></table></body></html>" , &H0D , &H0A
Data "endblock"

About META TAGS:

<meta http-equiv="Refresh" , CONTENT="9";URL="IP-number">

This one is used to tell the browser to fetch the HTML-page every 9 seconds. The "IP-number" can be the refreshstring we had to enter in the configuration part (below) below. Refresh string is explained later on.

<meta http-equiv="Pragma" , Content="no cache">

This one instructs the browser not to cache the page. Everytime a up to date page is retrieved. If you want to check the switches on the STK500 board, you get the actual status.

 

Step 20: Configuration screen

Declare Sub Configure

Call Configure

We will store the complete configuration in EEPROM

' In the EEPROM:
'
' 00 = .0 - 0 if LCD present
' 00 = .1
' 00 = .2
' 00 = .3
' 00 = .4
' 00 = .5
' 00 = .6
' 00 = .7
' 01 = IP-number msb
' 02 = IP-number
' 03 = IP-number
' 04 = IP-number lsb
' 05 = MAC-address
' 06 = MAC-address
' 07 = MAC-address
' 08 = MAC-address
' 09 = MAC-address
' 10 = MAC-address
' 11 = Portnumber
' 12 = Portnumber
' 13 = GW-IP-number msb
' 14 = GW-IP-number
' 15 = GW-IP-number
' 16 = GW-IP-number lsb

' MAC-address of Gateway is fetched everytime the WebTiger starts

' 23 = Length URL-refresh string
' 24 = start of URL-refresh string
' .... upto end of URL-refresh string

' Routine to get a configuration-screen on the RS232 port
'
Sub Configure
#if Debug = 1
     Print "Sub Configure"
#endif
Local Adr As Word
Local Row As Byte
Local Ipnr As String * 15
Local Dot1 As Byte
Local Dot2 As Byte
Local Dot3 As Byte
Local Dot4 As Byte
Local Dot5 As Byte
Local Iptemp As String * 1
Local Macadr As String * 20
Local Yn As String * 1
Print Chr(&H1b) ; "[2J"; ' ANSI goto 0/0
Print "WebTiger Atmega128 Loader Version 1.30"
Print
Print "Source written by Ben Zijlstra january 2006"
Print
Print "Present configuration:"
Print
Print "Network:"
Print
Print "IP-number : ";
Call Read_ip
Call Print_ip
Call Read_mac
Print
Print "MAC-address : ";
Call Print_mac
'Print Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ; Hex(mymac(3)) ; "-" ; Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ; Hex(mymac(6))
Print
Print "Gateway IP-number : ";
Call Read_gwip
Call Print_gwip
Print
Print "Press 'y' to get into configuration mode"

Test:
Incr Hulp3
If Hulp3 = 65000 Then
     Incr Hulp1
     Hulp3 = 0
End If
If Hulp1 = 50 Then
     Goto Noresponse
End If
sbis USR,7
rjmp test

Configuremode:
If Dsp_present = 0 Then
     Cls
     Lcd "Configure-mode"
End If
Print Chr(&H1b) ; "[2J"; ' ANSI goto 0/0 Print
Print "Configure mode:"
Print
Print "Network:"
Print
Print "IP-number: ";
Print
Call Print_ip
Input "Change IP-number (y/n) " , Yn
If Yn = "y" Then
     Print
     Print "Input IP-numbers as decimals, seperated by a dot (like 192.168.1.106)"
     Print
     Input Ipnr
     Hulp1 = Split(ipnr , Ippart(1) , ".")
     Myip(1) = Val(ippart(1))
     Myip(2) = Val(ippart(2))
     Myip(3) = Val(ippart(3))
     Myip(4) = Val(ippart(4))
     Print
     Print "New IP-number: ";
     Call Print_ip
     Print
     Input "Accept IP-number (y/n) " , Yn
     If Yn = "y" Then
          Writeeeprom Myip(1) , 1 : Writeeeprom Myip(2) , 2
          Writeeeprom Myip(3) , 3 : Writeeeprom Myip(4) , 4
     End If
     Print
     Print "IP-number: ";
     Call Read_ip
     Call Print_ip
End If
Print
Print "MAC-address: ";
Print Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ; Hex(mymac(3)) ; "-" ; Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ; Hex(mymac(6))
Print
Input "Change MAC-address (y/n) " , Yn
If Yn = "y" Then

     Mac:
     Print
     Print "Input MAC-address in hexadecimals, seperated by a - (like 00-34-35-36-37-48)"
     Print
     Input Macadr
     If Len(macadr) <> 17 Then Goto Mac
     Hulp1 = Split(macadr , Mymacs(1) , "-")

     Mymac(1) = Hexval(mymacs(1))
     Mymac(2) = Hexval(mymacs(2))
     Mymac(3) = Hexval(mymacs(3))
     Mymac(4) = Hexval(mymacs(4))
     Mymac(5) = Hexval(mymacs(5))
     Mymac(6) = Hexval(mymacs(6))

     Print
     Print "New MAC-address: ";
     Call Print_mac
     Print
     Input "Accept MAC-address (y/n) " , Yn
     If Yn = "y" Then
          Writeeeprom Mymac(1) , 5 : Writeeeprom Mymac(2) , 6
          Writeeeprom Mymac(3) , 7 : Writeeeprom Mymac(4) , 8
          Writeeeprom Mymac(5) , 9 : Writeeeprom Mymac(6) , 10
     End If
     Print
     Print "MAC-address: ";
     Call Read_mac
     Call Print_mac
End If

Print "Gateway: ";
Call Print_gwip
Print
Input "Change Gateway-IP-number (y/n) " , Yn
If Yn = "y" Then
     Print
     Print "Input Gateway-IP-number as decimals, seperated by a dot (like 192.168.1.254)"
     Print
     Input Ipnr
     Hulp1 = Split(ipnr , Ippart(1) , ".")
     Gwip(1) = Val(ippart(1))
     Gwip(2) = Val(ippart(2))
     Gwip(3) = Val(ippart(3))
     Gwip(4) = Val(ippart(4))
     Print
     Print "New Gateway-IP-number: ";
     Call Print_gwip
     Print
     Input "Accept Gateway-IP-number (y/n) " , Yn
     If Yn = "y" Then
          Writeeeprom Gwip(1) , 13 : Writeeeprom Gwip(2) , 14
          Writeeeprom Gwip(3) , 15 : Writeeeprom Gwip(4) , 16
     End If
     Print
     Print "Gateway-IP-number: ";
     Call Read_gwip
     Call Print_gwip
End If
Print


Print
Input "Would you like to use it's own IP-number as refresh-URL (y/n) " , Yn
If Yn = "y" Then
     Hulp1 = 255
     Writeeeprom Hulp1 , 23
   Else
     Print
     Input "Would you like to have another refresh-URL (y/n) " , Yn
     If Yn = "y" Then
           Input "Type the complete refresh URL " , Msg_temp
           Print
           Print "Refresh URL " ; Msg_temp
           Print
           Input "Accept Refresh-URL (y/n) " , Yn
           If Yn = "y" Then
                Hulp2 = Len(msg_temp)
                Writeeeprom Hulp2 , 23 ' write length of Refresh URL
                For Y = 1 To Hulp2
                      Hulp1 = Y + 23
                      Tempstring1 = Mid(msg_temp , Y , 1)
                      Hulp2 = Asc(tempstring1)
                      Writeeeprom Hulp2 , Hulp1
                Next Y
           End If
     End If
End If

I will try to explain the URL-refresh. Here is a picture of a WebTiger connected to Hub or Switch, to a ADSL/Cable modem, to the Internet.

 

The WebTigers IP-address is 192.168.0.100. But that is an address on your local network. On the Internet you get a public address from your ISP. With portforwarding you can instruct your ADSL/Cable modem to send all information on port 80 (HTTP) to a specific device on your local network. If you forward port 80 to the IP-address of the WebTiger it can be reached from the Internet. With some META TAGS (will explain that later on), you can instruct your browser on the Internet to refresh every N-seconds. In the example at the end of the tutorial I am refreshing the HTML-page every 9 seconds. Furthermore with a META TAG I instruct the PC on the Internet not to cache the page, every 9 seconds a complete HTML-page is fetched.

If you instruct the WebTiger to refresh every 9 seconds and put the public IP-address as the refresh-string, the computer on the Internet will use that public address.

Port Range Forwarding

Port Range Forwarding can be used to set up public services on your network. When users from the Internet make certain requests on your network, the Router can forward those requests to computers equipped to handle the requests. If, for example, you set the port number 80 (HTTP) to be forwarded to IP Address 192.168.0.100, then all HTTP requests from outside users will be forwarded to 192.168.0.100. It is recommended that the computer use static IP address.

You may use this function to establish a web server or FTP server via an IP Gateway. Be sure that you enter a valid IP Address. (You may need to establish a static IP address with your ISP in order to properly run an Internet server.) For added security, Internet users will be able to communicate with the server, but they will not actually be connected. The packets will simply be forwarded through the Router.

Hope this makes any sense....

Print
Hulp5 = 5000
Writeeeprom Hulp5h , 12 ' &h1388 = 5000 dec = 19 high, 136 low.
Writeeeprom Hulp5l , 11

Input "Would you like to change to UDP Portnumber. It's default is 5000 (y/n) " , Yn
If Yn = "y" Then
     Print
     Input "Type the portnumber < 65536 as decimal " , Hulp5
     Print
     Print "Portnumber UDP " ; Hulp5
     Print
     Input "Accept Portnumber UDP (y/n) " , Yn
     If Yn = "y" Then
          Writeeeprom Hulp5h , 12
          Writeeeprom Hulp5l , 11
     End If
End If
Print

Input "Would you like to use a LCD-display? " , Yn
Readeeprom Hulp1 , 0
If Yn = "y" Then
     Dsp_present = 0
     Hulp1.0 = 0
     Writeeeprom Hulp1 , 0
     Print
     Cls
     Lcd "LCD Testmessage"
     Print
     Input "Any key to continue...." , Yn
Else
     Hulp1.0 = 1
     Writeeeprom Hulp1 , 0
End If

There is an errata sheet on the Atmega's about using address 0 on the EEPROM. It can be corrupted. But I have never seen this happen on any of my projects.


Noresponse:
If Dsp_present = 0 Then
     Cls
End If
If you put your terminal in ANSI-mode, this command will clear the screen
Print Chr(&H1b) ; "[2J"; 
Print "Summary:"
Print
If Dsp_present = 1 Then
     Print "LCD-display : not used"
Else
     Print "LCD-display : used"
     Print
End If
Print
Print "Network:"
Print
Print "URL-refresh : ";
Read about portforwarding for Refresh-string
Readeeprom Hulp1 , 23
If Hulp1 = &HFF Then
     Print "http://" ; Myip(1) ; "." ; Myip(2) ; "." ; Myip(3) ; "." ; Myip(4)
Else
     Msg_temp = ""
     For Y = 1 To Hulp1
          Hulp3 = Y + 23
          Readeeprom Hulp2 , Hulp3
          Msg_temp = Msg_temp + Chr(hulp2)
     Next Y
     Print Msg_temp
End If
Print
Print "UDP-port : ";
Readeeprom Hulp5h , 12
Readeeprom Hulp5l , 11
Print Hulp5
Print
Print "IP-number : " ; Myip(1) ; "." ; Myip(2) ; "." ; Myip(3) ; "." ; Myip(4)
Print
Print "Mac-address : " ; Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ; Hex(mymac(3)) ; "-" ; Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ; Hex(mymac(6))
Print
Print "Gateway-IP-number : " ; Gwip(1) ; "." ; Gwip(2) ; "." ; Gwip(3) ; "." ; Gwip(4)
Print
End Sub

Here a few routines that can come in handy 

' Routine to read the IP-numbers from the EEPROM
'
Sub Read_ip
#if Debug = 1
     Print "Sub Read_ip"
#endif
Readeeprom Myip(1) , 1 : Readeeprom Myip(2) , 2
Readeeprom Myip(3) , 3 : Readeeprom Myip(4) , 4
End Sub


' Routine to print the IP-number
'
Sub Print_ip
#if Debug = 1
     Print "Sub Print_ip"
#endif
Print Myip(1) ; "." ; Myip(2) ; "." ; Myip(3) ; "." ; Myip(4)
End Sub


' Routine to read the IP-numbers from the EEPROM
'
Sub Read_gwip
#if Debug = 1
     Print "Sub Read_gwip"
#endif
Readeeprom Gwip(1) , 13 : Readeeprom Gwip(2) , 14
Readeeprom Gwip(3) , 15 : Readeeprom Gwip(4) , 16
End Sub


' Routine to print the GWIP-number
'
Sub Print_gwip
#if Debug = 1
     Print "Sub Print_ip"
#endif
Print Gwip(1) ; "." ; Gwip(2) ; "." ; Gwip(3) ; "." ; Gwip(4)
End Sub


' Routine to read the MAC-address from the EEPROM
'
Sub Read_mac
#if Debug = 1
     Print "Sub Read_mac"
#endif
Readeeprom Mymac(1) , 5 : Readeeprom Mymac(2) , 6
Readeeprom Mymac(3) , 7 : Readeeprom Mymac(4) , 8
Readeeprom Mymac(5) , 9 : Readeeprom Mymac(6) , 10
End Sub


' Routine to print the MAC-address
'
Sub Print_mac
#if Debug = 1
     Print "Sub Print_mac"
#endif
Print Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ; Hex(mymac(3)) ; "-" ; Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ; Hex(mymac(6))
End Sub


' Routine to send a default-message to the LCD
'
Sub Default_message
#if Debug = 1
     Print "Sub Default_message"
#endif
Call Read_ip
If Dsp_present = 0 Then
     Cursor Off
     Cls
     Lcd "www.achatz.nl WebCat"
     Thirdline
     Lcd "IP: "
     Tempstring = Str(myip(1)) + "." + Str(myip(2)) + "." + Str(myip(3)) + "." + Str(myip(4))
     Lcd Tempstring
End If
Wait 2
End Sub

Forgot the IP-number of your device? A reset will show you a few seconds the IP-number.

Step 21: Sample of an application: LM76 temperature sensor....

The code is complete but we have still lots of flash to add some nice features....


A real piece of art.... A LM76 for the STK500.

Here the DIM, Declare and subroutine to read a LM76-temperature sensor. Place them at the right position in the program.

'LM76
Dim Lm76temp As Word 
Dim Lm76high As Byte At Lm76temp + 1 Overlay
Dim Lm76low As Byte At Lm76temp Overlay
Dim Lm76temp2 As Long
Dim Lm76buf(2) As Byte

Declare Sub Lm76_test

Call Lm76_test

' Routine to test the LM76
'
Sub Lm76_test
I2csend &H90 , 0
I2creceive &H90 , Lm76buf(1) , 0 , 2
Print Bin(lm76buf(1))
Print Bin(lm76buf(2))
Lm76high = Lm76buf(1)
Lm76low = Lm76buf(2)
Shift Lm76temp , Right , 3
Lm76temp2 = Lm76temp
Lm76temp2 = Lm76temp2 * 625
Lm76temp2 = Lm76temp2 / 100
Print "Temperature = " ; Lm76temp2
End Sub

This piece of software can be integrated in the UDP and TCP part of the program.

I wonder how he did it....?

Step 22: Sample of an application: Simple Time Protocol Client

Just before I start the main program I fetch the MAC-address of our gateway and in the next routine I use that to get the TIME/DATE from a NTP-server. If you check this code you will see that a character X is send, including CR/LF as a UDP-packet. And the NTP-server responses with the date/time.

In this example I will use 193.67.79.202 and when you do a nslookup you will get this:

So I am using the ntp0.nl.uu.net to get the right time. NL for Netherlands.

To convert the LONG we get from the NTP-server into date and time, we have to use a extra library made by Franz Josef Vogel. It is one of the standard libraries from Bascom-AVR.


$lib "datetime.lbx"

and to force the compiler to implement _div16 we do a dummy calculation
Dim W1 As Word
W1 = W1 / W1 

Dimension some variables
Dim S(4) As Byte
Dim L1 As Long At S Overlay ' Overlay a long variable to receive-string
with overlay you need no transfer from the byte-array to a long-variable
Dim L2 As Long

Declare Sub Udp_send

Call Udp_send

' Routine UDP_send
In this example a UDP-send to a NTP-server and receiving the exact time
'
Sub Udp_send
#if Debug = 1
     Print "Sub Udp_send"
#endif
Disable Int5
T_enetpacketlenl = 66
T_enetpacketlenh = 0
'MAC-header
We use the MAC-address of our gateway as destination hardware address
T_enetpacketdest0 = Gwmac(1)
T_enetpacketdest1 = Gwmac(2)
T_enetpacketdest2 = Gwmac(3)
T_enetpacketdest3 = Gwmac(4)
T_enetpacketdest4 = Gwmac(5)
T_enetpacketdest5 = Gwmac(6)
source (own source)
T_enetpacketsrc0 = Mymac(1)
T_enetpacketsrc1 = Mymac(2)
T_enetpacketsrc2 = Mymac(3)
T_enetpacketsrc3 = Mymac(4)
T_enetpacketsrc4 = Mymac(5)
T_enetpacketsrc5 = Mymac(6)

T_enetpackettype = &H0008
' fill IP-header
T_ip_vers_len = &H45
T_tos = &H00
T_ip_pktlen0 = &H00
T_ip_pktlen1 = &H30

T_id0 = &H4A
T_id1 = &HA5

T_flags = &H00
T_offset = &H00

T_ttl = &H80
'protocol (ICMP=1, TCP=6, UDP=11)
T_ip_proto = &H11
'header checksum
'T_ip_hdr_cksum0
'T_ip_hdr_cksum1
'IP address of source
T_ip_srcaddr0 = Myip(1)
T_ip_srcaddr1 = Myip(2)
T_ip_srcaddr2 = Myip(3)
T_ip_srcaddr3 = Myip(4)
IP address of destination you have to put the IP-number of the NTP-server here (193.67.79.202)
T_ip_destaddr0 = 193 
T_ip_destaddr1 = 67 
T_ip_destaddr2 = 79
T_ip_destaddr3 = 202
'UDP-header

T_udp_srcport0 = &H13
T_udp_srcport1 = &H88

NTP-protocol. 37 dec, 25 hexadecimal
T_udp_destport0 = &H00
T_udp_destport1 = &H25

T_udp_len0 = &H00
T_udp_len1 = &H1C
Call Ip_header_checksum
Call Udp_checksum
Call Echopacket
Enable Int5
End Sub


' Routine to convert the LONG from the NTP-server in to date and time
'
Sub Ntp
#if Debug = 1
     Print "Sub Ntp"
#endif
S(4) = T_udp_data
S(3) = T_udp_data1
S(2) = T_udp_data2
S(1) = T_udp_data3
L2 = L1 + 1139293696 

L2 = L2 + 3600 ' offset UTC + 1 hour
Print "Date : " ; Date(l2)
Print "Time : " ; Time(l2)
Cls
Lcd "Date : " ; Date(l2)
Lowerline
Tempstring = Time(l2)
Lcd "Time : " ; Left(tempstring , 5)
End Sub

And here, at last, the complete code

Don't forget to use the right stack-settings

 

Isn't it a beauty....!!??

Step 23:  In development....


Graphical display (from a Nokia 6230) on STK500

 

Step 24: WegTiger and JTAG - more details later....

Step 25: to be continued....


Thanks to Fred Eady from www.edtp.com

Thanks to Mark Alberts, the creator of Bascom-AVR
www.mcselec.com

Thanks to Hugh Duff / VA3TO from www.drftech.com

Thanks to Franz Achatz from www.achatz.nl


Two nice books!!!