TweetFollow Us on Twitter

HyperAppleTalk 4
Volume Number:5
Issue Number:4
Column Tag:HyperChat™

Related Info: AppleTalk Mgr

XCMD Corner: HyperAppleTalk IV

By Donald Koscheka, Arthur Young & Co., MacTutor Contributing Editor

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

I sometimes think that, to most programmers, the network is best left to the handful of specialists that ply their craft on this arcane art.

Think of the network as adding a new dimension to your programs, much as perspective enabled medieval artists to add depth and realism to their paintings. While many artists may have scoffed at perspective, I have no trouble believing that the populace was awed by its discovery. Perhaps networking will shine the same light as we enter the renaissance of computer programming.

If you’ve been following this column over the last three months, you may be asking yourself, “Are we there yet?” This month, we tie the pieces together by introducing the rest of AppleTalk XCMDs. I apologize for publishing listings last month that you can’t really use until this month. I hope this issue makes up for it and in the future I will do my best to provide “soup to nuts” code in each issue.

Conceptually, an AppleTalk interface can be reduced to the six steps: (1) Open access to the transaction protocol and allocate any memory that will be needed to interface to it. I chose the AppleTalk Data Stream Protocol(ADSP). ADSPOpen hooks us up to ADSP, establishes a connection listener and returns the connection listener’s socket address to Hypercard. NBPOpen initializes a data structure that we use to interface to NBP. The January ’89 issue of this column provides the code for these XCMDs.

(2) Given the address of the connection listener, register a name and type with the network using the name binding protocol. Once registered, our entity’s name and address will be listed in the “directory”.

(3) If we want to be able to “dial up” other entities, we next perform a lookup which returns all current names in the lookup table which acts just like a directory. The names are returned as a list of items to Hypercard without their addresses. This step is optional, if all you expect to do is respond to requests (as in the case of a server), then you don’t need to know the names of the requesting entities.

(4) To call another entity, invoke the ADSPCall XCMD with the entity’s name. ADSPCall extracts the network address from the lookup table using the routine “NBPGetAddress”. This technique allows us to de-couple names and addresses; your stacks need never be concerned with the address of an entity, the name is sufficient at the Hypercard script level.

(5) Once a connection is established with another entity (called the remote end), you send information to that entity using ADSPTalk. You receive information from the remote end using ADSPListen. ADSPListen is the heart of the interface, in addition to checking for incoming messages, it also monitors the status of all open connections.

(6) When the session completes, remove your name from the names table using the NBPUnregister XFCN. Once unregistered, close down the interface to ADSP with the ADSPClose and then close down your NBP interface with NBPClose.

--The XCMDS--

While the process is conceptually quite simple, the amount of code needed to implement this interface may seem overwhelming. The rest of this article focuses on ADSPCall, ADSPTalk, ADSPListen and ADSPHangup. Information on both NBP and the open and close xcmds are available in that last two installments of this column as well as in recently published literature from APDA.

(1) ADSPCall

ADSPCall (listing 1) first extracts the address of the entity from the lookup table using NBPGetAddress. If you pass it the name of a currently visible entity, you have a reasonable assurance that the call will complete.

Upon retrieving the address, adspcall walks the connection list checking to see if a connection already exists with the remote end. If not, it creates a connection by calling DSPCreate.

Outgoing calls are placed asynchronously, control is passed back to Hypercard while the call completes in the “background”. Asynchronous communications pays big dividends, adspcall attempts to establish the connection as many times as you request in the retry field. Waiting for a call to complete can “hang” the system, a particularly uncomfortable state for the user. If for any reason, the remote end has gone off line, Hypercard won’t be stuck with an unbearable delay waiting for the call to timeout.

Outgoing calls, like all other transactions on the network, must be monitored by the listener. ADSPListen detects when the remote end picks up the phone.

To invoke ADSPCall from your script, use the following command line:

--1

-- get the name of the entity from the lookup table.
-- We let HyperAppleTalk handle the defaults for
-- retry, interval and buffer size. 
--
-- The two above globals will have been initialized by
-- NBPOpen and ADSPOpen respectively.
--
global globalNBPData, globalADSPData
ADSPCall theEntity

(2) ADSPTalk

Once a connection is established with a remote end, we can send messages to that entity. Like ADSPCall, ADSPTalk uses NBPGetAddress to get the address given a name. Because NBPGetAddress returns 0 if the name had no corresponding address in the lookup table, we can determine whether we have a connection to the remote end. ADSPTalk scans the connection list to match the address with that of an entity in our connection list (pointed to bey adsp^.ends). AdspTalk calls DSPTalk (covered last month) to send the information. Outgoing messages are also sent asynchronously so that the user is not made to suffer any delays while the transmission completes.

Like ADSPCall, the method of invoking ADSPTalk is straightforward:

--2

-- “theEntity” is the name of the remote end that you
-- called using ADSPCall and the card field contains the 
-- data that you wish to send to the remote end.
--
global globalNBPData, globalADSPData
ADSPTalk theEntity, card field “outgoing message”

(3) ADSPListen

The quality of any conversation depends more on the skill of the listener than that of the talker. This is no less true for the network - talking is the easy part, the listener does the hard part. Like any good listener, ADSPListen must perform several tasks at once -- (1) Answer incoming calls, (2) Monitor the status of outgoing calls, (3) Receive incoming messages and (4) periodically check the status of each connection.

Putting ADSPListen in the idle method of a card or stack provides a method for periodically checking up on each connection.

For each established connection an entry exists in our adsp^.ends list. While most of the data stored in the connection (cn) block is for our own use, the network keeps us apprised of the connection’s status via two records: the parameter block (pb) and the connection control block (ccb). The parameter block behaves as it would for any low-level I/O on the Macintosh, it is the interface to the ADSP device driver. The connection block is used by adsp to keep us up to date on the status of each connection. In essence, we relinquish control of the parameter block and the connection control block to ADSP. The rest of the structures in the connection data are for our own use.

To determine what is going on in any connection, we check the state field in the connection control block. The following states are currently supported (see listing 2 for the corresponding code):

sClosed: The connection went down for some reason or other. At this point, we have no choice but to Hang up.

sOpen: A connection opens in stages. When we first create or detect an opening connection, we set the mode to NOP indicating the connection is only “half open”. A half open connection is one in which only one of the two ends is ready. Receiving the sOpen state from the remote end informs us that our connection is now open.

A connection that is already open signals that it’s closing with the “eClosed” message. If a connection is closing, hang up on it.

The next two tasks, talking and listening, are monitored by Checkmessages. Recall that ADSPTalk queues outgoing calls; they complete some time in the future. Similarly, incoming messages can complete at any time. To determine whether any incoming or outgoing messages need attention, we issue a Status call to adsp. The ioCrefNum field of the dspParamBlock in checkMessages gets the driver number of the adsp driver. The ccbRefnum field gets the connection number of the connection we are checking.

The status call is issued synchronously, we must wait for it to complete before testing the status of a connection. If recvQPending is not 0, we have an incoming block of data. On input, we call ReadADSPData which will accumulate the data into the connection’s input buffer until ADSP signals that there is no more data with the end-of-message flag (eom).

ReadADSPData will read data from the connection’s input buffer into the handle we stored in conn^.inbuf (see listing 3). Accumulating data is a simple task : resize the handle by the number of bytes that we need to read in (given to us by adsp in recvQpending. If recvQpending is 0, there are no bytes to read). Once the handle is resized, point to the old end of the handle, and read the data directly into that are in memory. The read returns the exact number of bytes read in. If that number differs from recvQPending (under ordinary circumstances this won’t happen), then resize the handle to reflect the actual number of bytes read in.

After reading the data, we check the end-of-message flag (eom) which ADSP sets only after it sends the last block of the message down stream. If eom is set, there is no more data to read. We can now send the data back to hypercard. If we know the name of the connection, we send it back to hypercard via the ‘ADSPCaller’ global container. This gives your stack a way of determining who sent the input data. The data itself is put in the hypercard container, ‘HyperADSPData’. Once the data and name have been reported to Hypercard, we can optionally send a message back, perhaps to invoke some special input processing. If the connection stored a message in its ‘callme’ field, we now invoke that message via a callback.

Monitoring outgoing calls is a bit simpler. If the connection’s sendQPending field is not 0, we still have outgoing data in the output buffer. In this case, don’t do anything. If on the other hand, the send queue has emptied out and there was data in it (the connection outbuf field is not nil), then dispose the memory currently occupied by the output data.

The last task that ADSPListen must handle is responding to incoming calls. It does this by calling its respondtoListen Procedure. If a connection request is pending, the connection listener’s ioResult will be set to noErr (a negative result means an error occurred, a positive result means that no connection requests are pending, the listener still has its ears to the wall).

If a connection request is pending, we can open up a connection to the remote end whose address is passed in the remoteCID field. Once the connection is established,we can try to discover its name by issuing a call to NBPGetName with the pending connection’s name. Knowing the name of an incoming caller is optional so we don’t take extraordinary efforts to discover the name. If however, you needed the entities name (as in the case of a game), then you can do a lookup if NBPGetName turns up empty-handed. This is an advanced feature and, in the interest of space, I’ve omitted it. If you absolutely need this capability, drop me a line.

Once the pending request is serviced, the connection listener can put its ear back to the wall by requeuing the connection listener call. The information needed to queue a listening request is already stored in the connection listener’s parameter block (adsp^.pb). Requeuing the request becomes a simple call to PBControl with the connections parameter block.

The following script, added to your card, will do the trick:

--3

on idle

global globalNBPdata, globalADSPData
global adspCaller-- name of the caller global HyperADSPData    
 -- where incoming data goes
--
 ADSPListen “ShowData”

end idle

-- Show data is the method to invoke to handle
-- incoming data.  Assuming that we have a card field
-- named “show me”, the following method will display the
-- incoming message:
--
on ShowData
 global HyperADSPData

 put HyperAdspData into card field “show me”
end showdata

(4) ADSPHangup

We’ve already seen how to detect and respond to a connection hanging up on us. Use ADSPHangup (listing 4) to inform the remote end that the connection is going down. Once again, the drill is the same: get the address from the lookup table, check to see that a connection exists with this entity and call DSPHangup to inform the entity that the connection is going down. We don’t remove the connection from the connection list at this point because we have to wait for the remote end to acknowledge our disconnection request. ADSPListen receives this acknowledgement in the form of an eClosed message. Once closed, we can tear down the connection and deallocate its memory (dspRemove).

--4

--
-- A simple script for hanging up a connection
--
on Disconnect
 global HyperADSPData, globalNBPData

 ADSPHangup theEntity
End Disconnect

You now have a reasonably complete interface to AppleTalk. ADSP and NBP both offer more features than I’ve been able to show you in a short time. Notably, the out of stream and forward reset features of ADSP enable some very powerful enhancements to the basic protocol. I chose not to discuss these features in this series because the average application can get by quite well without them. If, however, you are writing a network server in Hypercard, you may want to explore these features in more detail. I will be happy to present the code for a full implementation of ADSP. In keeping with the spirit of MacTutor, contact me immediately to report any bugs in the code. I can be reached through this Magazine or on AppleLink (N0735) or MacNet.

Next month, I’m going to switch gears back into low and explore the file manager from within Hypercard.

Listing 1:  ADSPCall.p

(********************************)
(* file:  ADSPCall.p *)
(* *)
(* Establish a connection with*)
(* the entity whose name is *)
(* passed as a parameter. *)
(* *)
(* params[1] = Name of entity *)
(* params[2] = listen message *)
(* params[3] = number of retries*)
(* params[4] = retry interval *)
(* params[5] = Input buffer siz  *)
(* params[6] = Output buffer siz*)
(* *)
(* If  already connected, just*)
(* return with noErr *)
(* *)
(* Defaults:*)
(* Input Buffer  = 578    *)
(* Output Buffer = 578    *)
(* Retry count   = 0 *)
(* Retry interval= 0 *)
(* Out: the Result ::= error  *)
(* message, empty if none.*)
(* *)
(* ----------------------------  *)
(* By:  Donald Koscheka   *)
(* Date:9-Jan-89 *)
(* All Rights Reserved    *)
(* *)
(********************************)

(********************************
 Build Sequence

pascal ADSPCall.p -o ADSPCall.p.o 
link -m ENTRYPOINT  -rt XCMD=1302 -sn Main=ADSPCall
 ADSPCall.p.o
 “{libraries}”Interface.o 
 -o YourStackNameHere
  
*********************************)

{$R-}
{$S ADSPCall}
UNIT Koscheka;

(*********************************)
 INTERFACE 
(*********************************)

Uses  MemTypes, QuickDraw, OSIntf, 
 ToolIntf, PackIntf, HyperXCmd,
 AppleTalk, ADSP, adspxcmd;

Procedure EntryPoint( paramPtr : XCmdPtr );

(*********************************)
 IMPLEMENTATION
(*********************************)
TYPE Str31 = String[31];

PROCEDURE ADSPCall( paramPtr: XCmdPtr ); FORWARD;

Procedure EntryPoint( paramPtr : XCmdPtr );
Begin
 ADSPCall( paramPtr );
End;
 
Procedure ADSPCall( paramPtr : XCmdPtr );
VAR
 adsp   : ADSPPtr;
 error, terr: OSErr;
 inBufSiz : Integer;
 outBufSiz: Integer;
 retry  : Integer;
 interval : Integer;
 eAddr  : AddrBlock; 
 cn: CBPtr;
 nbp    : NBPPtr;
 found  : BOOLEAN;
 
{$I XCmdGlue.inc }
{$I XCMDADSP.inc } 

(*********************************)
BEGIN
 error  := noErr;
 found  := TRUE; {*** true if  already established ***}
 
WITH paramPtr^ DO
BEGIN
 IF (paramCount <> 0) AND (params[1]^ <> NIL) THEN 
 BEGIN
 adsp := ADSPPtr(RetrieveData( ‘GLOBALDSPDATA’ ));
 IF adsp <> NIL THEN
 BEGIN
 adsp^.checkPoint := RECEIVING;
 
 {**** Before we go too far, let’s ***}
 {*** look at some of the settings ***}
 {*** set the retransmit count***}
 IF params[3] <> NIL THEN
 retry := StringToSHort( params[3]^ )
 ELSE
 retry  := 0;
 
 {*** Set the retransmit interval  ***}
 IF params[4] <> NIL THEN 
 interval := StringToSHort( params[4]^ )
 ELSE
 interval := 0;
 
 {*** Set the input buffer size    ***}
 IF params[5] <> NIL THEN
 inBufSiz := StringToSHort( params[5]^ )
 ELSE
 inBufSiz := ATPBSIZE;
 
 {*** Set the output buffer size   ***}
 IF params[6] <> NIL THEN
 outBufSiz := StringToSHort( params[6]^ )
 ELSE
 outBufSiz:= ATPBSIZE;
 
 {*** (1) Get the entity’s address ***}
 HLock( Handle( params[1]) );
 eAddr := NBPGetAddress( params[1]^ );
 HUnlock( Handle( params[1] ) );

 {*** Walk through the connection  ***}
 {*** list to see if the connection***}
 {*** already exists.***}
 
 IF LongInt(eAddr) <> 0 THEN
 BEGIN
 cn := adsp^.ends;
 found := FALSE;
 
 WHILE (NOT found) DO
 IF cn = NIL THEN
 found := TRUE
 ELSE
 IF LongInt( eAddr ) = LongInt( cn^.adr ) THEN
 found := TRUE
 ELSE
 cn := cn^.next;
 
 {*** If connection already exists ***}
 {*** cn will not be NIL  so we won’t ***}
 {*** have to reconnect the two ends,***}
 {*** just fall out, otherwise, try to***}
 {*** make the connection.***}
 
 IF cn = NIL  THEN {*** establish connection ***}
 BEGIN
 cn^.remName:= NBPGetName( eAddr );
 cn := DSPCreate(eAddr,inBufSiz,
 outBufSiz,retry,interval,params[2] );
 IF cn <> NIL THEN
 BEGIN
 cn^.remName := NBPGetName( eAddr );
 
 {*** try to discover the name   ***}
 IF cn^.remName = NIL THEN
 BEGIN
 nbp := NBPPtr(RetrieveData(‘GLOBALNBPDATA’));
 IF nbp <> NIL THEN 
 BEGIN
 SendCardMessage(‘DoALookUp’);
 cn^.remName := NBPGetName( eAddr );
 END;
 END;
 
 {*** Now “dial for dollars”***}
 WITH cn^.dspPB DO 
 BEGIN
 ioCRefNum  := adsp^.dspRefNum;
 ccbRefNum:= cn^.ccbRef;
 csCode := dspOpen;
 ocMode := ocRequest;
 ioCompletion:= NIL;
 remoteCID:= 0;{*** don’t know this yet***}
 localCID := 0;{*** adsp allocates id***}
 remoteAddress:= eAddr; 
 filterAddress.aNet:= 0;
 filterAddress.aNode := 0;
 filterAddress.aSocket:= 0;
 SendSeq:= 0;
 RecvSeq:= 0;
 SendWindow := 0;
 attnSendSeq:= 0;
 attnRecvSeq:= 0;
 ocInterval := INTERVAL;
 ocMaximum:= RETRY;
 error  := PBControl( @cn^.dspPB, ASYNC );
 END;
 END;
 END;
 END; {(LongInt(eAddr) <> 0) }
 adsp^.checkPoint := CLOSE_OK;
 END;
 END;
 returnValue := PasToZero( NumToStr( LongInt(error) ) );
END; {*** with paramPtr^ ***}
END;

end.
Listing 2:  ADSPTalk.p

(****************************)
(* file:  ADSPTalk.p *)
(* *)
(* Send information to an *)
(* established connection.  *)
(* The connection must be *)
(* been established via the *)
(* ADSPCALL xcmd.*)
(* *)
(* In:  *)
(* params[1] = entity name*)
(* of the remote end.*)
(* *)
(* ------------------------ *)
(* By:  Donald Koscheka   *)
(* Date:2-Mar-89 *)
(* All Rights Reserved    *)
(* *)
(* ------------------------ *)
(****************************)

(*****************************
 Build Sequence

pascal -o ADSPTalk.p.o ADSPTalk.p
link -m ENTRYPOINT  -rt XCMD=1305 -sn Main=ADSPTalk
 ADSPTalk.p.o
 “{libraries}”Interface.o 
 -o YourStackNameHere
  
*****************************)

{$R-}
{$S ADSPTalk}
UNIT DummyUnit;

(****************************)
 INTERFACE 
(****************************)

Uses  MemTypes, QuickDraw, OSIntf,
 ToolIntf, PackIntf, HyperXCmd,
 AppleTalk, ADSP, adspxcmd;


Procedure EntryPoint( paramPtr : XCmdPtr );

(****************************)
 IMPLEMENTATION
(****************************)
TYPE Str31 = String[31];

PROCEDURE ADSPTalk( paramPtr: XCmdPtr ); FORWARD;

Procedure EntryPoint( paramPtr : XCmdPtr );
 Begin
 ADSPTalk( paramPtr );
 End;
 
Procedure ADSPTalk( paramPtr : XCmdPtr );
VAR
 adsp   : ADSPPtr;
 error  : OSErr;
 eAddr  : AddrBlock;
 cb: cbPtr;
 
{$I XCmdGlue.inc }
{$I XCMDADSP.inc } 

BEGIN
 error := noErr;
 adsp := ADSPPtr(RetrieveData( ‘GLOBALDSPDATA’ ));
 IF(adsp <> NIL) 
 AND  (paramPtr^.params[1] <> NIL)
 AND  (paramPtr^.params[2] <> NIL) THEN
 BEGIN
 adsp^.checkPoint := RECEIVING;
 {*** (1) Get the entity’s address ***}
 HLock( paramPtr^.params[1] );

 eAddr := NBPGetAddress( paramPtr^.params[1]^ );
 cb := adsp^.ends;

  {*** (2) Find entity in connection list***}
 
 IF LongInt( eAddr ) <> 0 THEN
 WHILE cb <> NIL DO
 IF (LongInt(cb^.adr)=LongInt(eAddr)) 
 AND (cb^.ccb.state=sOpen) THEN  
 BEGIN
 error := DSPTalk(cb, paramPtr^.params[2] );
 cb := NIL;
 END
 ELSE
 cb := cb^.next;
 
 adsp^.checkPoint := CLOSE_OK;
 HUnlock( Handle(paramPtr^.params[1]) );
 END;
 paramPtr^.returnValue := PasToZero( NumToStr( LongInt(error) ) );
END;

end.
Listing 3:  ADSPListen.p

(****************************)
(* file:  ADSPListen.p    *)
(* *)
(* Perform the following: *)
(* (1) Listen for incoming*)
(* calls on the connection*)
(* listener *)
(* (2) Check to see if any*)
(* outgoing calls have been*)
(* answered *)
(* (3) Check connections  *)
(* for incoming data *)
(* (4) Check connections for*)
(* attention messages*)
(* *)
(* In:  *)
(* params[1] = name of    *)
(* message to call hypercard*)
(* with on incoming data  *)
(* *)
(* Defaults:*)
(* container = HyperADSPData*)
(* message  = none.*)
(* *)
(* ---------------------- *)
(* All Rights Reserved    *)
(* *)
(* ------------------------ *)
(* By:  Donald Koscheka   *)
(* Date:2-Mar-89 *)
(* All Rights Reserved    *)
(* *)
(* ------------------------ *)
(****************************)

(*****************************
 Build Sequence

pascal -o “{hpo}”ADSPListen.p.o “{adsp}”ADSPListen.p
link -m ENTRYPOINT  -rt XCMD=1303 -sn Main=ADSPListen
 ADSPListen.p.o
 “{libraries}”Interface.o 
 -o YourStackNameHere
  
******************************)

{$R-}
{$S ADSPListen}
UNIT Koscheka;

(****************************)
 INTERFACE 
(****************************)

Uses MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, HyperXCmd, AppleTalk, 
ADSP, adspxcmd;

Procedure EntryPoint( paramPtr : XCmdPtr );

(****************************)
 IMPLEMENTATION
(****************************)
TYPE Str31 = String[31];

PROCEDURE ADSPListen( paramPtr: XCmdPtr ); FORWARD;

Procedure EntryPoint( paramPtr : XCmdPtr );
 Begin
 ADSPListen( paramPtr );
 End;
 
Procedure ADSPListen( paramPtr : XCmdPtr );
VAR
 myFlags: SignedByte;
{ flags from incoming unsolicited message    }
 adsp   : ADSPPtr; { pointer to ADSP Control block}
 error  : OSErr; 
 err    : OSErr;
 cn,nn  : cbPtr; { pointers to established conections}
 nbp    : NBPPtr;{ pointer to the memory  NBP interface}
 mybPtr : Ptr;   { used for getting and setting flags}
 
{$I XCmdGlue.inc }
{$I XCMDADSP.inc } 

PROCEDURE RespondToListen;
(********************************)
(* If the connection listener *)
(* has completed, then someone*)
(* is trying to call. If we can  *)
(* initialize a connection, *)
(* acknowledge receipt of  call.*)
(********************************)
VAR
 err    : Integer;
 conn : CBPtr;
 tH: Handle;
 
BEGIN
 IF  adsp^.pb.ioResult = noErr THEN 
 BEGIN  {*** We Have an incoming call.***}
 {*** Create a connection end to***}
 {*** the caller and set the***}
 {*** state to active indicating ***}
 {*** the connection is accepted ***}
 conn := DSPCreate(adsp^.pb.remoteAddress,
 ATPBSIZE,ATPBSIZE,RETRY,INTERVAL,NIL );
 
 IF conn <> NIL THEN
 WITH conn^.dspPB DO
 BEGIN  {*** accept the connection request***}
 ioCRefNum  := adsp^.dspRefNum;
 ioCompletion    := NIL;
 csCode := dspOpen;
 ocMode := ocAccept;
 ccbRefNum:= conn^.ccbRef;
 remoteCID:= adsp^.pb.remoteCID;
 localCID := 0;
 remoteAddress   := adsp^.pb.remoteAddress;  
 filterAddress.aNet:= 0;
 filterAddress.aNode := 0;
 filterAddress.aSocket := 0;
 RecvSeq:= 0;
 attnRecvSeq:= 0;
 SendSeq:= adsp^.pb.SendSeq;
 attnSendSeq:= adsp^.pb.attnSendSeq;
 SendWindow := adsp^.pb.SendWindow;
 ocInterval := INTERVAL;
 ocMaximum:= RETRY;
 err := PBControl( @conn^.dspPB, ASYNC );
 
 {*** Call back Hypercard to inform  ***}
 {***it of an incoming call.***}
 
 conn^.remName := NBPGetName( adsp^.pb.remoteAddress );
 END;
 
 {*** Reset connection listener to ***}
 {*** wait for more calls ***}
 err := PBControl( @adsp^.pb, ASYNC );
 END
END;

PROCEDURE ReadADSPData( conn:CBPtr; cnt:Integer; callMe:Handle );
(********************************)
(* Poll the connection to *)
(* determine whether any  *)
(* data is pending.  If so, *)
(* read it and pass it  back to *)
(* hypercard via the callback *)
(* mechanism.    *)
(* *)
(* IN:  *)
(* conn : pointer to connection *)
(* cnt  : num. of bytes to read  *)
(* callMe : callback method *)
(********************************)
VAR
 Tempb  : dspParamBlock;
 err    : Integer;
 temp   : LongInt;
 inSize : LongInt;
 globName: Str255; 
 tp: Ptr;
 tH: Handle;
 
BEGIN
 globName:= ‘HyperADSPData’;
 
 WITH conn^ DO
 BEGIN  {*** read the data ***}
 IF inBuf = NIL THEN
 BEGIN
 inBuf := NewHandle( LongInt(cnt) );
 MoveHHi( inBuf );
 HLock( inBuf );
 tp:= inBuf^;
 END
 ELSE {*** inBuf already allocated, add to end of it***}
 BEGIN
 HUnlock( inBuf );
 inSize := GetHandleSize(inBuf);
 temp   := inSize;
 inSize := inSize + LongInt( cnt );
 SetHandleSize( inBuf, inSize );
 MoveHHi( inBuf );
 HLock( inBuf );
 temp   := temp + Ord4( inBuf^ );
 tp:= Pointer(temp);
 END;
 
 WITH Tempb DO {*** now read the data ***}
 BEGIN
 ioCRefNum  := adsp^.dspRefNum;
 ccbRefNum  := conn^.ccbRef;
 csCode := dspRead;
 reqCount := cnt;
 dataPtr  := tp; 
 END;
 err := PBControl( @Tempb, SYNC );
 HUnlock( inBuf );

 IF err=noErr THEN
 BEGIN
 inSize := GetHandleSize( inBuf );
 inSize := inSize - (cnt-Tempb.actCount);
 SetHandleSize( inBuf, inSize );
 END;
 
 IF (err=noErr) AND (Tempb.eom=1) THEN
 BEGIN
 IF inBuf <> NIL THEN {***Call back with data***}
 BEGIN
 IF remName <> NIL THEN
 SetGlobal( ‘ADSPCaller’, remName );

 tp     := Pointer( Ord4( inBuf^ ) + LongInt(inSize) );
 tp^    := 0;
 
 SetGlobal( globName, inbuf );
 DisposHandle( inbuf );
 inbuf := NIL;
 END;
 
 IF callMe <> NIL THEN {***Set remote-end name***}
 BEGIN
 HLock( callMe );
 ZeroToPas( Pointer( callMe^ ), globName );
 HUnlock( callMe );
 SendCardMessage(  globName );
 END;
 END;
 END;{*** read the data ***}
END;

Function CheckMessages( cb : cbPtr ): OSErr;
(************************************)
(* check this connection for: *)
(* (1) Completed Incoming Messages *)
(* (2) Completed Outgoing Messages *)
(************************************)
VAR
 myErr  : OSErr;
 tpb    : dspParamBlock;
BEGIN
 WITH tpb DO
 BEGIN
 csCode := dspStatus;
 ioCRefNum:= adsp^.dspRefNum;
 ccbRefNum:= cn^.ccbRef;
 END;
 myErr := PBControl( @tpb, SYNC );
 
 IF myErr = noErr THEN
 BEGIN
 {*** (3) Check for pending incoming data    ***}
 IF tpb.recvQPending <> 0  THEN
 ReadADSPData( cn, tpb.recvQPending, paramPtr^.params[1] );

 {***(4) See if any write operations have completed***}
 IF (tpb.SendQPending = 0) AND (cn^.OutBuf <> NIL ) THEN
 BEGIN
 HUnlock( cn^.outBuf );
 DisposHandle( cn^.outBuf );
 cn^.outBuf := NIL;
 END;
 END; { myErr <> noErr }
 CheckMessages := myErr;
END;

(**********************************)
(* ADSPListen    *)
(**********************************)

BEGIN
 error  := noErr;
 adsp := ADSPPtr(RetrieveData( ‘GLOBALDSPDATA’ ));

 IF adsp <> NIL THEN
 BEGIN
 adsp^.checkPoint := RECEIVING;
 cn := adsp^.ends;

 {***(2) Monitor status on all connections***}
 WHILE cn <> NIL DO WITH cn^ DO 
 BEGIN
 nn := next;
 err := noErr;
 
 CASE ccb.state OF { connection states } 
 sClosed: err := DSPHangUp( cn );
 
 sOpen:
 BEGIN
 IF mode = NOP THEN {***connection is opening ***}
 BEGIN  {***inform Hypercard of connection***}
 SetGlobal( ‘ADSPCaller’, remName );
 SendCardMessage( ‘ConnectionMade’ );
 mode := EST;
 END
 ELSE
 BEGIN  {*** Check an established connection ***}
 myBPtr := @cn^.ccb.userFlags;
 
 IF BAND(myBPtr^, eClosed)=eClosed THEN 
 err := DSPHangUp( cn )
 ELSE
 BEGIN
 err := CheckMessages( cn );
 
 myBPtr^ := 0;   { clear user flags}
 END;
 END;
 END;
 END; { case of connection states }
 
 IF err <> noErr THEN error := err;
 cn := nn;
 END;
 RespondToListen;{*** check for opening calls ***}
 adsp^.checkPoint := CLOSE_OK;
 END; {*** IF adsp <> NIL ***}

 paramPtr^.returnValue := PasToZero( NumToStr( LongInt(err) ) );;
END;

end.
Listing 4:  ADSPHangUp.p

(****************************)
(* file:  ADSPHangUp.p    *)
(* *)
(* Disconnect an established*)
(* connection and deallocate*)
(* the data  associated with*)
(* the connection. *)
(* *)
(* In:  params[1] Name of *)
(* the entity to hangup on*)
(* *)
(* Out: error message,    *)
(* empty if none.*)
(* *)
(* ------------------------ *)
(* By:  Donald Koscheka   *)
(* Date:2-Mar-89 *)
(* All Rights Reserved    *)
(* *)
(* ------------------------ *)
(****************************)

(*****************************
 Build Sequence

pascal -o ADSPHangUp.p.o ADSPHangUp.p
link -m ENTRYPOINT  -rt XCMD=1304 -sn Main=ADSPHangUp
 ADSPHangUp.p.o
 “{libraries}”Interface.o 
 -o YourStackNameHere
  
******************************)

{$R-}
{$S ADSPHangUp}
UNIT DummyUnit;

(****************************)
 INTERFACE 
(****************************)

Uses  MemTypes, QuickDraw, OSIntf,
 ToolIntf, PackIntf, HyperXCmd, 
 AppleTalk, ADSP, adspxcmd;

Procedure EntryPoint( paramPtr : XCmdPtr );

(****************************)
 IMPLEMENTATION
(****************************)
TYPE Str31 = String[31];

PROCEDURE ADSPHangUp( paramPtr: XCmdPtr ); FORWARD;
Procedure EntryPoint( paramPtr : XCmdPtr );
 Begin
 ADSPHangUp( paramPtr );
 End;
 
Procedure ADSPHangUp( paramPtr : XCmdPtr );
VAR
 adsp   : ADSPPtr;
 error  : OSErr;
 pb: DSPParamBlock;
 eAddr  : AddrBlock;
 cb: CBPtr;

{$I XCmdGlue.inc }
{$I XCMDADSP.inc } 
(****************************)
BEGIN
 error := noErr;
 IF ( paramPtr^.paramCount > 0 )
 AND ( paramPtr^.params[1] <> NIL ) THEN           
 BEGIN
 adsp := ADSPPtr(RetrieveData( ‘GLOBALDSPDATA’ ));
 IF adsp <> NIL THEN
 BEGIN
 adsp^.checkPoint := RECEIVING;
 {*** (1) Get the entity’s address ***}
 HLock( Handle(paramPtr^.params[1]) );
 eAddr := NBPGetAddress( paramPtr^.params[1]^ );
 cb := adsp^.ends;
 
 {*** match the entity to the list ***}
 IF LongInt(eAddr) <> 0 THEN
 WHILE cb <> NIL DO
 IF LongInt(cb^.adr ) = LongInt( eAddr ) THEN
 BEGIN
 error := DSPHangUp( cb );
 cb := NIL;
 END
 ELSE
 cb := cb^.next;
 
 HUnlock( Handle(paramPtr^.params[1]) );
 adsp^.checkPoint := CLOSE_OK;
 END
 END;
 paramPtr^.returnValue := PasToZero( NumToStr( LongInt(error) ) );
END;

end.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Macs Fan Control 1.5.14 - Monitor and co...
Macs Fan Control allows you to monitor and control almost any aspect of your computer's fans, with support for controlling fan speed, temperature sensors pane, menu-bar icon, and autostart with... Read more
VueScan 9.7.96 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
FileMaker Pro 19.6.1 - Quickly build cus...
FileMaker Pro is the tool you use to create a custom app. You also use FileMaker Pro to access your app on a computer. Start by importing data from a spreadsheet or using a built-in Starter app to... Read more
Duet 3.1.0.0 - Use your iPad as an exter...
Duet is the first app that allows you to use your iDevice as an extra display for your Mac using the Lightning or 30-pin cable. Note: This app requires a iOS companion app. Release notes were... Read more
Firefox 107.0.1 - Fast, safe Web browser...
Firefox offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and casual... Read more
War Thunder 2.21.1.91 - Multiplayer war...
In War Thunder, aircraft, attack helicopters, ground forces and naval ships collaborate in realistic competitive battles. You can choose from over 1,500 vehicles and an extensive variety of combat... Read more
Numbers 12.2.1 - Apple's spreadshee...
With Apple Numbers, sophisticated spreadsheets are just the start. The whole sheet is your canvas. Just add dramatic interactive charts, tables, and images that paint a revealing picture of your data... Read more
DEVONthink Pro 3.8.7 - Knowledge base, i...
DEVONthink is DEVONtechnologies' document and information management solution. It supports a large variety of file formats and stores them in a database enhanced by artificial intelligence (AI). Many... Read more
Drive Genius 6.2.3 - $79.00
Drive Genius features a comprehensive Malware Scan. Automate your malware protection. Protect your investment from any threat. The Malware Scan is part of the automated DrivePulse utility. DrivePulse... Read more
VLC Media Player 3.0.18 - Popular multim...
VLC Media Player is a highly portable multimedia player for various audio and video formats (MPEG-1, MPEG-2, MPEG-4, DivX, MP3, OGG, ...) as well as DVDs, VCDs, and various streaming protocols. It... Read more

Latest Forum Discussions

See All

‘Genshin Impact’ Version 3.3 Pre-Install...
Following the reveal of the release date and more for Genshin Impact (Free) version 3.3 ‘All Senses Clear, All Existence Void’, HoYoverse showcased the Genius Invokation TCG that arrives this week in the update. | Read more »
TouchArcade Game of the Week: ‘Sling Min...
The world of PC games has always blown my mind because there’s just SO MUCH stuff out there that it’s not uncommon at all for there to be a game that’s well-liked and well-reviewed, and seemingly quite popular with a solid fanbase, and have it be... | Read more »
SwitchArcade Round-Up: Reviews Featuring...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for December 2nd, 2022. So, today turned out a little quieter than the usual Friday. It was so quiet, in fact, that I decided to pen a few reviews. The Knight Witch, Railbound, and Donut... | Read more »
Blue Archive reveals its latest event st...
Nexon has announced the new update for Blue Archive, under the name of An Unconcealed Heart. Featuring a battle between two academies, the story will follow a group struggling to gain recognition, and will bring three new students to recruit. [... | Read more »
Dead Cells+ Is Out Now on Apple Arcade a...
Following the major update for Dead Cells on iOS and Android a few days ago, Playdigious has brought Dead Cells+ () to Apple Arcade. As an App Store Great, Dead Cells+ includes all prior paid DLC and content updates. It also has exclusive mobile... | Read more »
SwitchArcade Round-Up: ‘Romancing SaGa’,...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for December 1st, 2022. Wow, December. We’re already at the last month of the year? Phew. I have a lot of work to finish in the next few weeks. As for today, we’ve got a little news, a... | Read more »
‘Railbound’ Update Now Available Adding...
One of our favorite puzzlers released this year is Railbound from Afterburn Games, which hit in early September and earned our Game of the Week recognition for being an absolutely ace logic puzzler. The goal is to place rail pieces down in order to... | Read more »
The Seven Deadly Sins: Grand Cross celeb...
Netmarble Corporation has pulled out all the stops to celebrate the 3 and a half year anniversary of The Seven Deadly Sins: Grand Cross. The Grand Cross 3.5th Year Anniversary the Ultimate One, a rather wordy title, brings with it a brand new... | Read more »
‘Skullgirls Mobile’ Major Update 5.2 Out...
Developer Hidden Variable pushed out a major update for Skullgirls Mobile (Free) a few hours ago. The version 5.2 update brings in Black Dahlia (before the console and PC game), Retakes, XP Treats, free gifts, and more. Since launch, Skullgirls... | Read more »
Out Now: ‘Disgaea 4’, ‘Romancing SaGa: M...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »

Price Scanner via MacPrices.net

Holiday Sale: Apple AirPods Pro for only $199...
Amazon has new 2022 AirPods Pro in stock and on sale for $199.99 shipped as part of their Holiday sale. Their price is $50 off Apple’s MSRP, equaling their Black Friday price, and it’s the lowest... Read more
New Holiday Sale: Apple retailers are offerin...
Several Apple retailers lowered prices on 10.9″ iPad Airs overnight to lows of $100 off MSRP starting at $499. Their prices are the lowest available for iPad Airs anywhere this Holiday season right... Read more
New Holiday sale at Amazon: Take $50 off Appl...
Amazon has Apple’s new 10th-generation iPads in stock and on sale, for the first time, for $50 off MSRP starting at only $399. Their discount applies to all models and all colors. With the discount,... Read more
Holiday Sale: Get an 8.3″ Apple iPad mini for...
Sams Club has 10.9″ 64GB iPad minis on Holiday sale for $80-$100 off MSRP through December 7, 2022. With their discount, prices start at $399 — the cheapest price for a new iPad mini from any of the... Read more
Sams Club Holiday December Event sale: Apple...
Apple AirPods Max headphones are on sale at Sams Club for $110 off MSRP ($439) as part of their December Event sale, ending on December 7, 2022, valid for all colors. Sale price for online orders... Read more
Apple’s 10.2″ 64GB 9th-generation iPads are o...
Sams Club has 9th-generation 64GB iPads on Holiday sale for $60 off MSRP through December 7, 2022. With their discount, prices start at $259 — the cheapest price for a new iPad from any of the Apple... Read more
11″ 128GB WiFi M2 iPad Pro on sale for $749,...
B&H Photo has the new 11″ 128GB WiFi M2-powered iPad Pro (in Space Gray or Silver) on Holiday sale for $749 including free 1-2 day shipping to most US addresses. Their price is $50 off MSRP and... Read more
Find the best Holiday sale price on an iPad u...
We’ve updated our iPad Price Trackers with the latest information on the new 10th-generation iPads, M2-powered iPad Pros, M1 iPad Airs, iPad minis, and 9th generation iPads from Apple’s authorized... Read more
Apple retailers are offering $100-$150 Holida...
Apple retailers have posted their most-recent Holiday sale prices on 13″ MacBook Airs. Take up to $150 off MSRP on M2-powered Airs with these sales with prices starting at only $1099. Free shipping... Read more
Holiday Sale: Apple’s 14″ MacBook Pros with M...
B&H Photo is offering $200-$300 discounts on Apple’s 14″ MacBook Pros with M1 Pro CPUs as part of their Holiday 2022 sale, with prices starting at $1799. Free 1-2 day shipping is available to... Read more

Jobs Board

Support Technician II - *Apple* Support - O...
…problems and acting as a liaison between customers and resolving groups. As an Apple Technical Specialist, you will be supporting many of our popular Apple Read more
*Apple* Electronic Repair Technician - PlanI...
…a highly motivated individual to join our Production Department as an Apple Electronic Repair Technician. The computer repair technician will diagnose, assemble, Read more
Lead Developer - *Apple* tvOS - Rumble (Uni...
…earnings, and positive sentiment About the role: We are looking for a Lead Apple tvOS Developer to join our application engineering team to expand our video centric Read more
Tier 1 Endpoint Engineer - *Apple* - Red Ri...
…Desk on site, at our Client's location, with a focus on support to Apple products. This position will handle technical support requests directly from customers and Read more
Product Manager II - *Apple* - DISH (United...
…you will be doing We seek an ambitious, data-driven thinker to assist the Apple Product Development team as our new Retail Wireless division continues to grow and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.