mac-rom/Libs/InterfaceSrcs/piDDP.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

929 lines
33 KiB
Plaintext

;
; File: piDDP.a
;
; Contains: xxx put contents here xxx
;
; Written by: xxx put writers here xxx
;
; Copyright: © 1992 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM3> 1/29/93 RB Do not move the SCCLockout value to the status register, always
; do an ORI.W with it so that NuKernel works and the stack is not
; changed.
; <2> 10/13/92 DCL Steve Bollinger (4-2122) 9/24/92
; Network events don't work anymore, so we always set the packet
; recvd flag. The caller can just poll this to see when we are
; done. set the packet recvd flag
;
;
BLANKS ON
STRING ASIS
EJECT
;_______________________________________________________________________
;
; DDP Routines
;_______________________________________________________________________
;
;_______________________________________________________________________
;
; Socket Listener
;
; This socket listener is called from MPP.
;
; Upon entering the socket listener, the LAP header bytes, and the
; appropriate DDP header bytes have been written into the RHA and the
; RHA's ptr will point past the last byte in the RHA.
;
; A4 and A5 are usable until ReadRest 2(A4) is called. After that,
; the normal interrupt conventions (A0-A3,D0-D3) apply. D2 is
; preserved across ReadPacket and ReadRest.
;
; The RHA contains enough room for LAP and long DDP headers, plus 8
; extra bytes. This socket listener moves as many as 4 bytes into the
; RHA in order to prevent the SCC from overflowing.
;
; If the buffer is to small to hold the incoming packet, it will be
; truncated to fit and a buf2small error will be returned. When this
; happens, checksums and the RetCksErr flag are ignored, so that even
; if the caller specified "don't return checksum errors" he could
; potentially be returned a packet with a checksum error if the buffer
; was too small.
;
; Call:
; A0,A1 .LONG SCC read/write addresses (preserve)
; A2 .LONG local vars (preserve)
; A3 .LONG ptr into the RHA (hdr bytes already loaded in)
; A4 .LONG address of the ReadPacket routine
; D0 .BYTE socket number for this read
; D1 .WORD length still left to read (preserve)
;
;
; Return:
; D0 .WORD error result
;
; Possible errors:
;
; Modification History:
; 8/24/84 Ready for alpha release (GRT)
; 8/24/84 Checksum routine added (ABO)
; 10/11/84 Code-crunching and speedup (RFA)
; 3/15/85 RecoverHandle call added (GRT)
; 5/1/86 GRT Not using _RecoverHandle anymore
;
;_______________________________________________________________________
tmpOffset EQU 0 ; entry num of the handler we are using
tmpRHAPtr EQU tmpOffset+2 ; pointer into RHA at end of DDP hdr
SocketListener PROC
MOVEQ #4,D3 ; max number of bytes to read
CMP D3,D1 ; check to see how many bytes are left
BHS.S @02 ; if more then just use the 4 bytes
MOVE D1,D3 ; else its less, so read however many left
@02 LEA saveArea,A5 ; save A3 because we need it later
MOVE.L A3,tmpRHAPtr(A5) ; (12) save ptr to RHA
MOVE D3,D2 ; remember # of bytes & check for zero
SWAP D2 ; move into upper word for now
MOVE.B D0,D2 ; save socket number in D2 also
TST D3 ; how many bytes to read?
BEQ.S @03 ; if zero, branch around ReadPacket
JSR (A4) ; read the bytes into the RHA and adjust
******************************************************************************
*** BEGINNING OF MAIN TIME CRITICAL SECTION **********************************
BNE.S @08 ; if error, exit
; A4 and A5 are restored upon completion of the ReadPacket call.
; A5 -> ptr to my save area
@03 LEA theSocketTable,A3 ; A3 -> my socket table
MOVEQ #maxSkts-1,D3 ; start at end of list
@05 CMP.B aSktNum(A3,D3),D2 ; match?
DBEQ D3,@05 ; no, check next one
BNE.S @11 ; if not found, goto @11
; If we have reached here, we have found the socket entry in our table, and we
; will now get the buffer ptr stored in the AB record. If a Read call has not
; been issued, we will throw the packet away.
LSL #2,D3 ; get offset into RBPtr field
MOVE D3,tmpOffset(A5) ; save offset * 4
MOVE.L aRBPtr(A3,D3),A3 ; get read block ptr
MOVE.L A3,D0 ; set CCR to see if read buf was NIL
BEQ.S @11 ; if no read buffer, ignore packet
MOVE.L rbABHdl(A3),A3 ; A3 = AB record handle
MOVE.L (A3),A3 ; deref it
MOVE ddpReqCount(A3),D3 ; D3 = number of bytes want to read
MOVE.L ddpDataPtr(A3),A3 ; A3 -> buffer ptr for packet
SWAP D2 ; restore byte count into low word
ADD D2,A3 ; modify ptr
SUB D2,D3 ; D3 = bytes to read - what I've already read
JSR 2(A4) ; read in rest of packet
*** END OF TIME CRITICAL SECTION **********************************************
*******************************************************************************
BNE.S @08 ; if error, just exit
BRA.S @12 ; else go on
@08 RTS ; short branch exit point
; Error exit section - This will exit the socket listener and call MPP (which
; will read the rest of the packet and ignore it.
@11 MOVEQ #0,D3 ; tell MPP we dont want the packet
JMP 2(A4) ; and call MPP
; OK, the packet is now hopefully where it is supposed to be so we can now pass
; other information back to the user.
;
; D3 = number of bytes Read in compared to the number of bytes in the buffer
; (if D3 < 0 then the users buffer was not big enough, so report an error)
;
; Registers D0-D3 and A0-A3 are now usuable
@12 TST D3 ; check to see if we overflowed buffer
BGE.S @13 ; if not then branch
MOVE #buf2SmallErr,D0 ; set error code
; Copy any bytes I temporarily stored in the RHA back into the user's buffer
; A0 -> ABRecord, A1 -> save area, A3 -> 1st DDP data byte in RHA
@13 MOVE.L A2,-(SP) ; save local var ptr
LEA saveArea,A1 ; get save area start point
LEA theSocketTable,A2 ; point to socket table
MOVE tmpOffset(A1),D1 ; restore table offset
MOVE.L aRBPtr(A2,D1),A2 ; A2 -> Read Block
MOVE.L rbABHdl(A2),A0 ; A0 = ABRecord handle
MOVE.L (A0),A0 ; deref it
MOVE ddpReqCount(A0),D1 ; get requested number of bytes
SUB D3,D1 ; get how many we actually read
MOVE D1,ddpActCount(A0) ; actual bytes read in
MOVE.L tmpRHAPtr(A1),A3 ; get saved RHA pointer
MOVE.L ddpDataPtr(A0),A2 ; A2 -> input data buffer
CMP ddpReqCount(A0),D2 ; is buffer big enough to hold 'em?
BLE.S @15 ; yes, proceed
MOVE ddpReqCount(A0),D2 ; no, only copy what will fit
@15 SUBQ #1,D2 ; dec counter
BLT.S @17 ; if neg then exit
MOVE.B 0(A3,D2),0(A2,D2) ; copy RHA bytes into buffer
BRA.S @15 ; do it again until done
; Move any pertinant data to the AB record to send back to the caller.
; We return the dest node ID to the user to let him know if it was a broadcast
; or directed. A $FF means broadcast, else it will be this node's ID
@17 MOVE.L (SP)+,A2 ; restore A2 MPP local vars
MOVE D0,abResult(A0) ; set return result code
CLR.B ddpTypeField-1(A0) ; clear junk byte for Pascal
MOVE.B DDPType-DDPHSzLong(A3),ddpTypeField(A0) ; ddp type
MOVE.B DDPSrcSkt-DDPHSzLong(A3),ddpAddrSkt(A0) ; 'from' skt num
CLR.B ddpNodeID-1(A0) ; clear junk byte for Pascal
MOVE.B ToRHA+LAPDstAdr(A2),ddpNodeID(A0) ; dest node ID
; Set up A1 -> Socket Table, D1 and D2 are offsets into that table
MOVE tmpOffset(A1),D2 ; D2 = entry offset in table * 4
MOVE D2,D1 ; D1 has it too!
LSR #2,D1 ; get actual offset into socket field
LEA theSocketTable,A1 ; A1 -> theSocketTable of info
CMP.B #shortDDP,ToRHA+LAPType(A2) ; is this a short DDP hdr?
BNE.S @18 ; branch for long header
MOVE.B ToRHA+LAPSrcAdr(A2),ddpAddrID(A0) ; stuff source node ID
CLR ddpAddrNet(A0) ; clear network number
MOVE.L aRBPtr(A1,D2),A3 ; get read block ptr
BRA.S @23 ; all done - go clean up
; If DDP long header, we return extended header info in addr blk to user
@18 MOVE.B DDPSrcNode-DDPHSzLong(A3),ddpAddrID(A0) ; 'from' xtend hdr ID
MOVE DDPSrcNet-DDPHSzLong(A3),ddpAddrNet(A0) ; 'from' xtend net ID
TST DDPChecksum-DDPHSzLong(A3) ; examine checksum in packet
MOVE.L aRBPtr(A1,D2),A3 ; get read block ptr
BEQ.S @23 ; if cksum zero, nothing to calculate
TST D0 ; any errors so far (buf2small)?
BNE.S @23 ; if so, forget checksum
BSR CalcCheckSum ; calculate checksum (D3 = error)
BEQ.S @23 ; checksums match
TST.B rbRetCksErr(A3) ; does user want Cksum errs returned?
BEQ.S @40 ; no, ignore this packet
MOVE #ckSumErr,abResult(A0) ; else report the error
@23 MOVE.L rbABHdl(A3),A0 ; A0 = AB rec handle
_HUnlock ; unlock the ABRecord handle
; Steve Bollinger (4-2122) 9/24/92
; Network events don't work anymore, so we always set the packet recvd
; flag. The caller can just poll this to see when we are done.
; set the packet recvd flag
ST aRcvFlag(A1,D1) ; set the pkt recvd flag to true
; unlink and unlock read block from the queue (has to be at the beginning)
; A3 -> read block
@30 MOVE.L 0(A3),aRBPtr(A1,D2) ; unlink read blk
MOVE.L rbHdl(A3),A0 ; A0 = read block handle
JSR UnlockAndLinkHdl ; dispose of the handle
@40 RTS
EJECT
;_______________________________________________________________________
;
; CalcCheckSum
;
; Calculate the checksum of the DDP packet. This checksum includes
; the DDP header bytes (starting from Dst Net field) and the data bytes.
;
; Call:
; A0 -> ABRecord
; A2 -> MPP variables
;
; Return:
; D3 = 0 if checksum match, non-zero otherwise
; D1 and A1 are saved/restored, D0 is trashed
;
; Modification History:
; 8/24/84 ABO Ready for alpha release
; 9/11/84 GRT Saved register A1
; 10/12/84 RFA Code crunch, saved D1
; 11/6/84 GRT If checksum calculated to be zero, then set to -1
;
;_______________________________________________________________________
CalcCheckSum MOVEM.L D1/A1,-(SP) ; save registers
CLR D3 ; start from scratch
LEA ToRHA+LAPHdSz+DDPDstNet(A2),A1 ; A1 -> Dest Net field
MOVEQ #DDPHSzLong-DDPDstNet,D1 ; size of DDP header to cksum
BSR.S DoCheckSum ; Checksum DDP header fields
MOVE.L ddpDataPtr(A0),A1 ; A1 -> user data
MOVE ddpActCount(A0),D1 ; D1 = user data count
BSR.S DoCheckSum ; Checksum user data
TST D3 ; check final result of checksum
BNE.S @10 ; if not zero then branch
SUBQ #1,D3 ; however, if it was, then force to -1
@10 SUB ToRHA+LAPHdSz+DDPChecksum(A2),D3 ; Subtract off pkt's cksum
MOVEM.L (SP)+,D1/A1 ; restore registers
RTS ; D3 zero if all ok, CCR set
;_____________________________
;
; DoCheckSum - accumulate ongoing checksum
;
; Call:
; D1 = number of bytes to checksum
; D3 = current checksum
; A1 -> bytes to checksum
;
; Return:
; Uses D0
; D3 = accumulated checksum
;_____________________________
DoChecksum CLR D0 ; Clear D0 high byte
SUBQ #1,D1 ; Decrement count by 1 for DBRA
@10 MOVE.B (A1)+,D0 ; D0 (word) = next byte
ADD D0,D3 ; Accumulate checksum
ROL #1,D3 ; Rotate checksum
DBRA D1,@10 ; And keep going
RTS
ENDPROC
;_______________________________________________________________________
;
; LookUpSkt
;
; This routine looks up a socket in my socket table. It is called by
; both DDPCloseSocket and DDPRead.
;
; Entry:
; D3 = socket number to find in table
;
; Exit
; D1 = offset value in table
; z-bit set in CCR if found else cleared
;_______________________________________________________________________
LookUpSkt PROC
LEA theSocketTable,A1 ; point to start of socket table
MOVEQ #maxSkts-1,D1 ; start at end of table
@10 CMP.B aSktNum(A1,D1),D3 ; check for match
DBEQ D1,@10 ; found it?
RTS
ENDPROC
EJECT
;_______________________________________________________________________
;
; DDPOpenSocket
;
; Opens a socket for DDP and optionally installs a socket listener
; for the requested socket. Socket number 0 means ephemeral. Socket
; number 1 owned by the RTMP socket listener (automatically attached
; when MPP initializes.
;
; This interface maintains its own socket table, apart from MPP's.
; This table holds info for all sockets opened by DDPOpenSocket for
; which the default socket listener was specified.
;
; A NIL value in sktListener will automatically install this interfaces
; built-in socket listener into my socket table.
;
; FUNCTION DDPOpenSocket(VAR theSocket : Byte; sktListener : Ptr): INTEGER;
;
; Stack upon entry:
;
; TOS => .LONG Return address
; .LONG Pointer to socket listener (see above)
; .LONG address of the sockets variable
; .WORD function result code
;
; Returns: .WORD Result code
;
; Possible errors: No more sockets
;
; The IO queue element is allocated on the stack for the control call.
; This call is made synchronously.
;
; Modification History:
; 8/24/84 GRT Ready for alpha release
; 10/12/84 RFA Code-crunch
;
;_______________________________________________________________________
DDPOpenSocket PROC EXPORT
JSR RemoveHdlBlks ; check to see if handles need to be disposed
MOVE.L (SP)+,D2 ; save return address
MOVE.L (SP)+,A1 ; get socket listener ptr
MOVE.L A1,D1 ; set ccr; is it zero??
BNE.S @10 ; if not then branch and continue
LEA SocketListener,A1 ; get address of built-in listener
MOVE.L A1, D1 ; save it away in D1
@10 MOVE.L (SP)+,A1 ; address of socket variable
MOVE.L D3,-(SP) ; save D3 register
MOVE.B 1(A1),D3 ; get socket number
SUB #IOQElSize,SP ; allocate space for the IOQEl
MOVE.L SP,A0 ; A0 -> IO queue element block
MOVE.B D3,socket(A0) ; socket number
MOVE.L D1,listener(A0) ; socket listener
MOVE #OpenSkt,CSCode(A0) ; code for attach proto hndlr
MOVE #MPPRefNum,IORefNum(A0) ; set refnum up
_Control
TST D0 ; check D0 - CCR doesn't get set right by _Control
BNE.S @90 ; if error, branch and exit
; Return the socket number to the user. Check skt listener address to see if
; we need to add it to our table. If it was not sent as NIL then don't add it.
MOVE.B Socket(A0),D3 ; get the (possibly) new socket
MOVE.B D3,1(A1) ; return skt num to user
LEA SocketListener,A0 ; see if we're using the default
CMPA.L D1,A0 ; socket listener
BNE.S @90 ; if not, then don't add to our table
; Caller wants to use the default socket listener, so store the socket number
; in my own table for use by DDPRead and the socket listener.
LEA theSocketTable,A1 ; A1 -> my table of skt listeners
MOVE #maxSkts-1,D1 ; start at the end of the list
@20 TST.B aSktNum(A1,D1) ; test for zero
DBEQ D1,@20 ; find it?
BNE.S @80 ; if not found then branch with error
; Found an entry so set up info in the socket table
@30 MOVE.B D3,aSktNum(A1,D1) ; store the socket number
CLR.B aRcvFlag(A1,D1) ; zero out the pkt rcvd flag
LSL #2,D1 ; offset to actual place in table
CLR.L aRBPtr(A1,D1) ; no read blocks linked in yet
BRA.S @90
@80 MOVE #ddpSktErr,D0 ; no free entry in table (ERROR)
@90 ADD #IOQElSize,SP ; deallocate the queue element
JMP ExitD3 ; restore D3 and exit
ENDPROC
EJECT
;_______________________________________________________________________
;
; DDPCloseSkt
;
; Detaches the socket listener and socket number from the table. Unlocks
; all Read Blocks associated with the socket and queues them for disposal.
; Unlocks all ABRecords associated with the Read Blocks. Error
; is returned by MPP if the socket was not found in the table.
;
; FUNCTION DDPCloseSocket(theSocket : Byte): INTEGER;
;
; Stack upon entry:
;
; TOS => .LONG Return address
; .BYTE pascal filler
; .BYTE socket number
; .WORD function result code
;
; Returns: .WORD Result code
;
; Possible errors: MPP errors: Socket not found
;
; The IO queue element is allocated on the stack for the control call.
; This call is made synchronously.
;
; Modification History:
; 8/24/84 GRT Ready for alpha release
; 10/12/84 RFA Code-crunch
; 10/16/84 RFA Clean up all Read Blocks and unlock all ABRecs
; 3/15/85 GRT RecoverHandle call added
;
;_______________________________________________________________________
DDPCloseSocket PROC EXPORT
JSR RemoveHdlBlks ; memory handles to be disposed?
MOVE.L (SP)+,D2 ; save return address
MOVE (SP)+,D0 ; get socket number (stored in word order)
MOVE.L D3,-(SP) ; save register
MOVE.B D0,D3 ; skt number
SUB #IOQElSize,SP ; allocate IOQElement
MOVE.L SP,A0 ; A0 -> IOQEl
MOVE.B D3,socket(A0) ; pass parameter to IOQ
MOVE #CloseSkt,CSCode(A0) ; control code
MOVE #MPPRefNum,IORefNum(A0) ; set refnum up
_Control ; make the call
ADD #IOQElSize,SP ; deallocate the IOQ element
TST.B D0 ; did we get an error
BNE.S @15 ; if so then branch
; Scan through table until either an matched entry is found, or until
; the end of the table is reached. If the entry is not found, we can't assume
; an error since the socket might have not been for our socket table.
JSR LookUpSkt ; check for socket
BNE.S @15 ; if not found then just exit
CLR.B aSktNum(A1,D1) ; zero out the field to nullify it
CLR.B aRcvFlag(A1,D1) ; just to be on the safe side
LSL #2,D1 ; get offset into RBPtrs
MOVE.L aRBPtr(A1,D1),A0 ; get first Read Block
CLR.L aRBPtr(A1,D1) ; zero it out for cleanup
MOVE.L A0,A1 ; A1 -> Read Block
@10 MOVE.L A1,D0 ; is it nil?
BEQ.S @15 ; if so, we're done
MOVE.L rbABHdl(A1),A0 ; A0 = AB rec handle
_HUnlock ; and unlock it
MOVE.L A1,A0
_RecoverHandle ; recover handle to read block
MOVE.L (A1),A1 ; but first, get ptr to next RdBlk
JSR UnlockAndLinkHdl ; unlock and queue it up for disposal
BRA.S @10
@15 JMP ExitD3 ; return
ENDPROC
EJECT
;_______________________________________________________________________
;
; DDPWrite
;
; Write a packet out to the cable either synchronously or asynchronously.
; If sync, do the operation and return when completed. If async, pass
; control back after queueing the call, and when completed, post a network
; event back to the application with a message consisting of the ABRecHandle.
;
; The ABRecHandle is locked down during most of the call. abResult will
; contain a 1 in it as long as the operation is still in progress (ASYNC
; only)
;
; FUNCTION DDPWrite(abRecord : ABRecHandle; doCheckSum : BOOLEAN;
; async : BOOLEAN): INTEGER;
;
; Stack upon entry:
;
; TOS => .LONG Return address
; .BYTE pascal filler
; .BYTE async flag
; .BYTE pascal filler
; .BYTE checkSum flag
; .LONG handle to record data structure
; .WORD function result code
;
; Returns: .WORD Result code
; Additional data in record structure
;
; Possible errors:
;
;
; Upon allocation of the IOQElement block, an additional 36 bytes are
; allocated at the end. The data structure looks like this:
;
; |=========================|
; : :
; : IOQElement blk : 50 bytes
; : :
; : :
; |=========================|
; : AB record hdl : 4 byte hdl to AB record
; |-------------------------|
; : Q element hdl : 4 byte hdl to this queue element
; |-------------------------| <----------- WDS1Start
; : WDS element1 (hdr info) : 6 bytes (2 byte length, 4 byte ptr)
; |-------------------------|
; : WDS element2 (write) : 6 bytes (points to data to write)
; |-------------------------|
; |_ WDS Termination _| 2 bytes
; | Word |
; |-------------------------|
; | async flag | 1 byte
; |=========================| <----------- WDS1stEntry
; : 7 bytes for LAP : 7 bytes (THIS IS ODD ALIGNED!)
; : header, DDP length :
; : and checksum fields :
; |-------------------------|
; |_ Destination Network _| 2 Bytes
; | Number |
; |-------------------------|
; |_ _| 2 Bytes unused in call
; | |
; |-------------------------|
; | Dest Node Address | 1 Byte
; |-------------------------|
; | | 1 Byte unused in call
; |-------------------------|
; | Dest Skt Number | 1 Byte
; |-------------------------|
; | | 1 Byte unused in call
; |-------------------------|
; | Level 2 Type | 1 Byte
; |-------------------------|
; | | 1 Byte unused in call
; |=========================|
;
; Modification History:
; 8/24/84 GRT Ready for alpha release
; 10/12/84 RFA D0 must be .long when calling GetHdlAndLock
; 12/17/84 GRT CmpEventPost changed to Cmp1EventPost
; 4/30/86 GRT Saving Q element handle in the queue element
;_______________________________________________________________________
DDPWrite PROC EXPORT
JSR RemoveHdlBlks ; check for disposable handles
LEA retAddr,A1 ; save place for ret address
MOVE.L (SP)+,(A1) ; save return address; A1 available now
MOVE.B (SP)+,D1 ; save async flag
MOVE.B (SP)+,D2 ; save checksum flag
MOVE.L (SP)+,A0 ; abRecord handle
MOVEM.L A2-A3,-(SP) ; save A2/3
_HLock ; lock abRecord down
TST D0 ; check error - MUST DO THIS!
BNE @90 ; if not zero return an error
MOVE.L (A0),A2 ; A2 -> Ptr to ABRecord
MOVE.L A0,A1 ; save another copy of handle for later
MOVE #1,abResult(A2) ; still in execution
MOVE.B #tDDPWrite,abOpCode(A2) ; put call type in record
; We are allocating an extra n bytes at the end of the IOQElement for storage
; of the misc data (see diagram)
MOVE.L #IOQElSize+WDSXtraDDPSize,D0 ; number of bytes to allocate
JSR GetHdlAndLock ; allocate the memory A0->IOQElement Hdl
BNE.S @90 ; error if didn't work
MOVE.L A0,D0 ; save handle in D0
MOVE.L (A0),A0 ; A0 -> IOQElement blk (dereferenced)
MOVE.L D0,qElHdl(A0) ; save handle in queue element
; A2 -> ABRecord. A0 -> IOQElement
MOVE.L A1,abRecHdl(A0) ; save hdl in IOQElement
LEA WDS1Start(A0),A3 ; start of our vars in IOQ blk
MOVE #16,(A3)+ ; length is always 16 for the WDS hdr
LEA WDS1stEntry(A0),A1 ; address of the WDS header entry
MOVE.L A1,(A3)+ ; A1 = WDS entry 1 ptr
MOVE ddpReqCount(A2),(A3)+ ; buffer size to write out
MOVE.L ddpDataPtr(A2),(A3)+; buffer data pointer
CLR (A3)+ ; zero means end of WDS
MOVE.B D1,(A3) ; async flag stored in IOQELBlk
; Fill in the parameters of the IO Q element
MOVE.B ddpTypeField(A2),dType(A0) ; store ddp type field
MOVE.B ddpSocket(A2),Socket(A0) ; store source socket
MOVE ddpAddrNet(A2),dDstNet(A0) ; store dest network
MOVE.B ddpAddrID(A2),dDstNodeAddr(A0); store dest node address
MOVE.B ddpAddrSkt(A2),dDstSkt(A0) ; store dest socket number
LEA WDS1Start(A0),A2 ; get pointer to WDS block
MOVE.L A2,WDSPointer(A0) ; move it into the IOQElement
MOVE.B D2,ChecksumFlag(A0) ; pass checksum flag to DDP
MOVE #WriteDDP,CSCode(A0) ; set control code
LEA DDPWrteCompletion,A2 ; A2 -> our IO completion routine
JSR DoControlCall ; make the control call
@90 MOVEM.L (SP)+,A2-A3 ; restore A2/3
MOVE.L retAddr,D2 ; get back return address
JMP ExitD0 ; and exit
;_______________________________________________________________________
;
; DDPWrteCompletion
;
; This routine is called when the IO has been completed
; A0 -> IOQElBlk
;_______________________________________________________________________
DDPWrteCompletion
MOVEM.L D0/A0-A2,-(SP) ; save registers
JSR CmpEntrance ; set up registers and unlock ab record hdl
JSR Cmp1EventPost ; if event needs to be posted, do it
MOVEM.L (SP)+,D0/A0-A2 ; restore registers
RTS
ENDPROC
EJECT
;_______________________________________________________________________
;
; DDPRead
;
; Read a packet coming in from the cable.
;
; IMPORTANT: The DDPRead call can only be used with the built in socket
; listener.
;
; The ABRecHandle is locked down until the call completes. The socket
; listener is responsible for unlocking it. (unless an error occurs in the
; read queueing, in which case its unlocked by this routine)
;
; All packets received with a long DDP header are examined to see if they
; contain a valid checksum. If so, we calculate the checksum and see if they
; match. If they don't, the retCksumErrs flag is checked to see if the user
; wants packets with checksum errors returned to him/her. If they do, we
; return the packet but with a ckSumErr error. If not, the packet is thrown
; away and the Read does not complete.
;
; FUNCTION DDPRead(abRecord : ABRecHandle; retCksumErrs : BOOLEAN;
; async : BOOLEAN): INTEGER;
;
; Stack upon entry:
;
; TOS => .LONG Return address
; .BYTE async flag
; .BYTE pascal filler
; .BYTE return checksum errs flag
; .BYTE pascal filler
; .LONG handle to record data structure
; .WORD function result code
;
; Returns: .WORD Result code
; Additional data in record structure
;
; Possible errors: Bad socket (zero or not open)
;
;
; A 14 byte read block is allocated dynamically for every read call. This
; block is linked in a FIFO queue for every active socket number . When a
; pkt is received, the first read block data in the link is read and the
; socket listener attempts to read the packet into the buffer pointed to by
; the ddpDataPtr parameter in the ABRecord.
;
; |-------------------------|
; | Link to next | 4 byte pointer link
; | read block |
; | |
; |-------------------------|
; | AB record handle | 4 byte hdl to AB record
; | |
; | |
; |-------------------------|
; | async flag | 1 byte
; |-------------------------|
; | return cksum errs flag | 1 byte
; |-------------------------|
; | read block hdl | 4 byte hdl to this read block
; | |
; | |
; |-------------------------|
;
; Modification History:
; 8/24/84 GRT Ready for alpha release
; 10/12/84 RFA Code-crunch, D0 must be .long when calling GetHdlAndLock,
; & handles being recovered wrong under error conditions
; 3/15/85 GRT RecoverHandle call added
; 4/30/86 GRT Read block handle saved in the read block
;_______________________________________________________________________
DDPRead PROC EXPORT
JSR RemoveHdlBlks ; check disposable handles
LEA returnAddr,A1 ; address of save longword
MOVE.L (SP)+,(A1) ; save return address
MOVE.B (SP)+,D2 ; save async flag
SWAP D2 ; in upper word of D2
MOVE.B (SP)+,D2 ; save checksum errs flag
MOVE.L (SP)+,A0 ; record handle
MOVEM.L D3/A2-A3,-(SP) ; save registers for later
MOVE.L A0,A3 ; save ABRecHdl in A3
_HLock ; lock abRecord down
TST D0 ; test result of the lock operation
BNE ReadExit ; if not zero return an error
; first check my socket listener table for the matching socket number
MOVE #readQErr,D0 ; assume an error
MOVE.L (A0),A2 ; A2 -> ABRecord
MOVE.B ddpSocket(A2),D3 ; get the socket number
BEQ.S Read1Exit ; zero type is invalid
JSR LookUpSkt ; check for socket number in table
BNE.S Read1Exit ; if not found then leave
; we found the socket, so get a Read Block and set it up
MOVE #1,abResult(A2) ; in execution
MOVE.B #tDDPRead,abOpCode(A2) ; put call type in
MOVEQ #rdBlkSize,D0 ; size of a read blk entry
JSR GetHdlAndLock ; get a handle to the memory
BNE.S Read1Exit ; exit if error (must clean up)
MOVE.L A0,D0 ; save handle in D0
MOVE.L (A0),A0 ; A0 -> my read block (dereference)
MOVE.L D0,rbHdl(A0) ; put handle in read block
; A0 -> my read block A2 -> AB record stuff the read blk full of data
CLR.L rbNxPtr(A0) ; zero out ptr to next element
MOVE.L A3,rbABHdl(A0) ; copy the AB rec hdl into it
MOVE.B D2,rbRetCksErr(A0) ; save the checksum flag
SWAP D2 ; get the async flag
MOVE.B D2,rbAFlag(A0) ; save the async byte flag
; must reset the pkt recvd flag before the read buffer is linked in
; SCC interrupts are being turned off while linking the block in
; A0 -> read blk, A1 -> theSocketTable (set up by LookUpSkt)
@10 CLR.B aRcvFlag(A1,D1) ; zero out the pkt recvd flag
MOVE D1,D2 ; D1,D2 has offset
LSL #2,D2 ; D2 * 4 = actual offset in table
MOVE SR,-(SP) ; save status register on stack
ORI.W #SCCLockOut,SR ; lock out the SCC interrupts <SM2> rb
LEA aRBPtr(A1,D2),A3 ; A3 -> read element entry link
@15 TST.L (A3) ; is next link empty?
BEQ.S @20 ; yes, we're at then end
MOVE.L (A3),A3 ; no, step to next link
BRA.S @15 ; and repeat
@20 MOVE.L A0, (A3) ; put Read Block at end of list
MOVE (SP)+,SR ; restore interrupt status
CLR D0 ; I guess that there are no errors!
TST.B rbAFlag(A0) ; is this an async call?
BNE.S ReadExit ; if so exit (don't unlock the handle)
; Since the call is sync, we wait until the pkt recvd flag says one has come in
; The result code will have been put in the record by the protocol handler.
@25 TST.B aRcvFlag(A1,D1) ; has a packet been received?
BEQ.S @25 ; if not, then wait
MOVE abResult(A2),D0 ; pick the result code out of the record
BRA.S ReadExit ; exit
; If an error occurred we must unlock the ABRecord block
Read1Exit MOVE D0,D1 ; save error code
MOVE.L A2,A0
_RecoverHandle ; recover handle to abRec
_HUnlock ; unlock the handle
MOVE D1,D0 ; recover error code
ReadExit MOVEM.L (SP)+,D3/A2-A3 ; restore registers
MOVE D0,(SP) ; stuff function result
MOVE.L returnAddr,A1 ; get return address
JMP (A1) ; return
EJECT
ENDPROC
EJECT
;_______________________________________________________________________
;
; DDPRdCancel
;
; Cancel a pending asynchronous DDPRead and free its associated data structures.
;
; IMPORTANT: The DDPRdCancel call can only be used with asynchronous DDPRead
; calls on a socket whose socket listener is the default.
;
; The ABRecord is unlocked, and the associated Read Block is dequeued from
; the list of pending Read requests and marked for disposal. This call can
; only be made synchronously.
;
; FUNCTION DDPRdCancel(abRecord : ABRecHandle): INTEGER;
;
; Stack upon entry:
;
; TOS => .LONG Return address
; .LONG handle to record data structure
; .WORD function result code
;
; Returns: .WORD Result code
; The abResult field of the abRecord is set to zero
;
; Possible errors: Bad socket (zero or not open)
; abRecord not found
;
; Note that by the time the Read request queue is searched for the Read Block
; pointing to the specified abRecord, a packet may arrive and satisfy the
; request. An "abRecord not found" error will then be returned. In such a
; case, the caller should check the abResult field of the abRecord to see
; if it indeed has completed. If it did, it will contain zero or a negative
; error indication. If it is equal to 1, then something is seriously wrong.
;
; Modification History:
; 10/23/84 RFA New today
; 3/15/85 GRT RecoverHandle call added
; 5/1/86 GRT changed rbABPtr to rbABHdl
; 12/10/86 SJF fixed _RecoverHandle Bug
;_______________________________________________________________________
DDPRdCancel PROC EXPORT
JSR RemoveHdlBlks ; check disposable handles
MOVEM.L (SP)+, D2/A0 ; D2 = return adr, A0 = abRec hdl
MOVEM.L D3/A2-A4, -(SP) ; save some registers
; first check my socket listener table for the matching socket number
MOVE #readQErr,D0 ; assume an error
MOVE.L (A0),A2 ; A2 -> ABRecord
MOVE.B ddpSocket(A2),D3 ; get the socket number
BEQ.S RdCnclExit ; zero is invalid
JSR LookUpSkt ; check for socket number in table
BNE.S RdCnclExit ; if not found then leave
; we found the socket, so search list of Read Blocks for one that points to abRec
MOVE #recNotFnd, D0 ; assume an error
LSL #2, D1 ; get offset into Read Blocks
LEA aRBPtr(A1,D1), A1 ; A1 = Read Block queue header
MOVE SR, -(SP) ; save old status
ORI.W #SCCLockout, SR ; disable interrupts <SM2> rb
@10 MOVE.L 0(A1), A3 ; check next element
CMP.L #0, A3 ; is there a next element?
BNE.S @20 ; yes, continue checks
MOVE (SP)+, SR ; no, we're at the end & no match!
BRA.S RdCnclExit ; restore interrupt state & return
@20 MOVE.L rbABHdl(A3),A4 ; get handle to ABRecord
CMP.L (A4),A2 ; is this the RdBlk for our abRec?
BEQ.S FoundIt ; yes!
MOVE.L A3, A1 ; no, skip down to next Read Block
BRA.S @10 ; and check it
; we found the right Read Block, so dequeue it and dispose of it
FoundIt MOVE.L (A3), (A1) ; dequeue the Read Block
MOVE (SP)+, SR ; safe now to re-enable interrupts
CLR abResult(A2) ; clear result field in abRecord
_HUnlock ; unlock the abRecord
MOVE.L A3,A0
_RecoverHandle ; recover read block handle
_HUnlock ; unlock it and dispose of it
_DisposHandle ; (hopefully clears D0 as well)
RdCnclExit MOVEM.L (SP)+, D3/A2-A4 ; restore registers
MOVE D0, (SP) ; set function result
MOVE.L D2, A0 ; restore return address
JMP (A0) ; and return
ENDPROC