mac-rom/Libs/InterfaceSrcs/piDDP.a

929 lines
33 KiB
Plaintext
Raw Normal View History

;
; File: piDDP.a
;
; Contains: xxx put contents here xxx
;
; Written by: xxx put writers here xxx
;
; Copyright: <09> 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