Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Networking /
Chapter 7 - Datagram Delivery Protocol (DDP) / Using DDP


Using Registers and Packet Headers

To receive data at the DDP level, you need to include as part of your socket-client application a socket listener that reads packets addressed to your application and passes them to the application for further processing. DDP maintains a table with an entry
for each socket and socket listener pair.

When the .MPP driver receives a packet addressed to your socket-client application, it calls your socket listener, using the CPU's registers to pass pointers to the internal buffer where it has stored the packet's headers and to some data values that your socket listener uses during its processing.

The CPU's registers that the .MPP driver uses to pass parameters to your socket listener are not directly accessible from Pascal. Because a DDP socket listener must read from and write to the CPU's registers, you must write a socket listener in assembly language; you cannot use Pascal. However, you can write the remainder of the client application that includes the socket listener in a high-level language such as Pascal. The client application sample code that this chapter shows is written in the Pascal language.

How the .MPP Driver Calls Your Socket Listener

When a frame addressed to a particular node arrives at that node and the frame contains a DDP packet, the node's CPU is interrupted and the link-access protocol calls the .MPP driver to receive the packet. When the .MPP driver receives a DDP packet, it reads the first 3 bytes of the frame header into an internal buffer called the read-header area (RHA). After the frame header is read into the RHA, 8 bytes of the RHA are still available for your use.

Next, the .MPP driver reads the socket address and calls the socket listener for that socket. The .MPP driver uses the CPU's registers to pass parameters to your socket listener as follows:
Registers on call to DDP socket listener
A0Reserved for internal use by the .MPP driver. You must preserve this register until after the ReadRest routine has completed execution.
A1Reserved for internal use by the .MPP driver. You must preserve this register until after the ReadRest routine has completed execution.
A2Pointer to the .MPP driver's local variables. The value at the offset toRHA from the value in the A2 register points to the start of the RHA.
A3Pointer to the first byte in the RHA past the DDP header bytes (the first byte after the DDP protocol type field).
A4Pointer to the ReadPacket routine. The ReadRest routine starts 2 bytes after the start of the ReadPacket routine.
A5Free for your use before and until your socket listener calls
the ReadRest routine.
D0Lower byte is the destination socket number of the packet.
D1Word indicating the number of bytes in the DDP packet left to be read (that is,
the number of bytes following the DDP header).
D2Free for your use.
D3Free for your use.

When the .MPP driver calls your socket listener, you can read the destination socket number that is in the D0 register and the frame header that is in the RHA. You should assume that only 8 bytes are still available in the RHA for your use. Figure 7-5 shows the beginning of the RHA where the frame header begins; the frame header is followed by either a short or a long DDP header.

The DDP Packet and Frame Headers

A DDP packet includes a packet header followed by data. The DDP packet header is preceded by the frame header. Figure 7-6 shows both headers; they do not include the data portion. The DDP packet header can be long or short; if the destination and source network numbers are different, DDP uses a long header, which includes some additional fields.

The frame header includes

Figure 7-5 The RHA for both long and short DDP headers

Figure 7-6 Data-link frame header and DDP packet header

The DDP long and short packet headers have these fields in common:

A long DDP packet header also includes

The MPW Equates

You can use the following equates from the MPW interface files in writing your socket listener process and the client application that includes it:

;frame header
;
lapDstAdr   EQU   0     ;destination node address [byte]
lapSrcAdr   EQU   1     ;source node address [byte]
lapType     EQU   2     ;LAP type field [byte]
lapHdSz     EQU   3     ;size of LAP header

;DDP packet header
;
ddpHopCnt   EQU   0     ;hop count (only used in long 
                        ; header) [byte]
ddpLength   EQU   0     ;packet length (from this word 
                        ; onward) [word]
ddpChecksum EQU   2     ;checksum [word]
ddpDstNet   EQU   4     ;destination network no. [word]
ddpSrcNet   EQU   6     ;network of origin [word]
ddpDstNode  EQU   8     ;destination node address [byte]
ddpSrcNode  EQU   9     ;node of origin [byte]
ddpDstSkt   EQU   10    ;destination socket number [byte]
ddpSrcSkt   EQU   11    ;source socket number [byte]
ddpType     EQU   12    ;DDP protocol type field [byte]
sddpDstSkt  EQU   2     ;destination socket number (short
                        ; header) [byte]
sddpSrcSkt  EQU   3     ;source socket number (short 
                        ; header) [byte]
sddpType    EQU   4     ;DDP protocol type field (short header)
                        ; [byte]
;
ddphSzLong  EQU   13    ;size of extended DDP header
ddphSzShort EQU   5     ;size of short DDP header
;
shortDDP    EQU   $01   ;LAP type code for DDP (short header)
longDDP     EQU   $02   ;LAP type code for DDP (long header)

Reading an Incoming Packet

Your socket listener calls the ReadPacket and ReadRest processes to read the incoming data packet. You can call ReadPacket as many times as you like to read the data piece by piece into one or more data buffers, but you must always use ReadRest to read the final piece of the data packet. Alternatively, you can read all of the data using only ReadRest. The ReadRest routine restores the machine state (the stack pointers, status register, and so forth) and checks for error conditions.

Note
You can ignore any remaining data instead of reading it
by setting the D3 register to 0 and calling ReadRest.
Before you call the ReadPacket routine, you must allocate memory for a data buffer and place a pointer to the buffer in the A3 register. You must also place the number of bytes you want to read in the D3 register. You must not request more bytes than remain in the data packet.

The buffer that you allocate must be large enough to hold all of the data and--if your socket listener places the packet header in the buffer--the header as well. The maximum amount of data in a DDP data packet is 586 bytes. A long DDP packet header is 13 bytes long; a short header is 5 bytes. The frame header is 3 bytes. Therefore, the maximum amount of data from the packet that the socket listener can return is 602 bytes. You can use the buffer as a data structure to hold other information as well, such as the number of bytes of data actually read by the socket listener, a flag that indicates when the data has been returned, and result codes.

After you have called the ReadRest routine, you can use registers A0 through A3 and D0 through D3 for your own use, but you must preserve all other registers. You cannot depend on having access to your application's global variables.

To call the ReadPacket routine, execute a JSR instruction to the address in the A4 register. The ReadPacket routine uses the registers as follows:
Registers on entry to the ReadPacket routine
A3Pointer to a buffer to hold the data you want to read
D3Number of bytes to read; must be nonzero
Registers on exit from the ReadPacket routine
A0Unchanged
A1Unchanged
A2Unchanged
A3Address of the first byte after the last byte read into buffer
A4Unchanged
D0Changed
D1Number of bytes left to be read
D2Unchanged
D3Equals 0 if requested number of bytes were read, nonzero if error

After every time you call ReadPacket or ReadRest, you must check the zero (z) flag
in the status register for errors because the ReadPacket routine indicates an error
by clearing it to 0. If the ReadPacket routine returns an error, you must terminate execution of your socket listener with an RTS instruction without calling ReadPacket again or calling ReadRest at all.

Call the ReadRest routine to read the last portion of the data packet, or call it after
you have read all the data with ReadPacket routines and before you do any other processing or terminate execution. After you call the ReadRest routine, you must terminate execution of your socket listener with an RTS instruction whether or not the ReadRest routine returns an error.

When you call the ReadRest routine, you must provide in the A3 register a pointer to
a data buffer and must indicate in the D3 register the size of the data buffer. If you
have already read all of the data with calls to the ReadPacket routine, specify a buffer of size 0.

WARNING
If you do not call the ReadRest routine after the last time you call the ReadPacket routine successfully, the system will crash. You do not need to call the ReadPacket routine; you can call only the ReadRest routine to read in the entire packet. However, you must call the ReadRest routine.
To call the ReadRest routine, execute a JSR instruction to an address 2 bytes past the address in the A4 register. The ReadRest routine uses the registers as follows:
Registers on entry to the ReadRest routine
A3Pointer to a buffer to hold the data you want to read
D3Size of the buffer (word length); may be 0
Registers on exit from the ReadRest routine
A0Unchanged
A1Unchanged
A2Unchanged
A3Pointer to first byte after the last byte read into buffer
D0Changed
D1Changed
D2Unchanged
D3Equals 0 if requested number of bytes exactly equaled the size of the buffer;
less than 0 if more data was left than would fit in buffer (extra data equals -D3 bytes); greater than 0 if less data was left than the size of the buffer (extra buffer space equals D3 bytes)

Calling ReadPacket and ReadRest when LocalTalk is the data link
If LocalTalk is the data link that is being used, your socket listener
has less than 95 microseconds (best case) to read more data with a ReadPacket or ReadRest call. If you need more time, you can read another 3 bytes into the RHA, which will allow you an additional
95 microseconds.
In implementing your socket listener, you can use the registers as follows:

Using Checksums

For packets that include a long header, DDP includes a checksum feature that you can use to verify that the packet data has not been corrupted by memory or data bus errors within routers on the internet.

When you use the PWriteDDP function to send a DDP packet across an AppleTalk internet, you can set a flag (checksumFlag) to direct DDP to calculate a checksum
for the packet.

If the checksum flag is set and the socket to which you are sending the packet (the destination socket) has a network number that is different from that of the socket from which you are sending the packet (the source socket), then the PWriteDDP function calculates a checksum for the datagram and includes it in the datagram packet header. In this case, DDP uses a long header for the packet; Figure 7-6 on page 7-15 shows both the long and short DDP headers.

When your socket listener receives a packet that has a long header, the socket listener must determine whether DDP calculated a checksum for the packet, and if so, use the checksum to verify that the data was delivered intact. You can use the equates from the MPW interface files in calculating checksums: see "The MPW Equates" on page 7-16.

To determine this, your socket listener code should take the following steps:

  1. Check the DDP header type field. This is set to 2 for a packet with a long header and 1 for a packet with a short header.
  2. Check the checksum field (checksumFlag). This is set to a nonzero value if the sender specified that DDP should calculate a checksum for the packet; a short header does not include a checksum field.
  3. Calculate the checksum using the following algorithm to calculate the checksum, starting with the byte immediately following the checksum field in the header and ending with the last data byte:

    checksum := checksum + next byte; {unsigned addition}
    Rotate the most significant bit to the least significant bit
    Repeat

  4. Compare the calculated checksum against the value set in the checksum field of the DDP packet header.

    You can use the equates from the MPW interface files in calculating checksums: see "The MPW Equates" on page 7-16.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996