TweetFollow Us on Twitter

ATalk Mail
Volume Number:4
Issue Number:9
Column Tag:Forth Forum

Appletalk Mail

By Jörg Langowski, MacTutor Editorial Staff

“Appletalk mail for Mach2”

This month I’ll give you a brief re-introduction to Appletalk and Mach2 words for Appletalk handling. The example will be a set of two programs - a message sender and a message receiver which together form a very rudimentary mail utility.

We’ve already had some contributions on Appletalk: for additional reading see the articles by Bob Denny, V1#10 and V1#11; Alan Wootton, V1#10; Dave Kosiur, V3#4; and on the printer access protocol by Mike Schuster, V2#2 and Nicholas Pavkovic, V4#1. Also, for any serious Appletalk programming it is indispensable to have a copy of Inside Appletalk handy, and the Appletalk chapter of Vols. II and V of Inside Macintosh.

I thought it worthwhile to write a sample Appletalk application in Mach2 Forth, especially since the Appletalk example included in the Mach2 system does not give a hint how to use higher level protocols.

Appletalk protocol levels

Let’s - very briefly - review the way Appletalk sends data over the network.

We must view the Appletalk cable as a bus to which up to 32 devices can be attached. The bus is very simple, containing a twisted pair of wires, and information must therefore be passed along it serially. This serial transfer proceeds at a rate of 230.4K baud. The basic Appletalk message unit sent on the bus is a packet of data, which can be zero to 600 bytes long.

Evidently, only one device at a time can send something along the bus. If - let’s say - a program instructs my Mac to send out a packet of data and at the same time the Laserwriter sitting in another room tries to send as well, everybody else will receive only garbage. The Appletalk hardware has a way of detecting this situation, and will automatically re-send the packet after a random wait. This way it is ensured that the two devices probably won’t cross-talk again.

ALAP

You don’t perceive this mechanism (in network language it is called the Appletalk Link Access Protocol or ALAP). If you ask the Appletalk driver to send out a packet of data, you can be sure that - using a transparent handshake mechanism between sender and receiver, and a CRC for error checking - it will be sent to the correct destination on the local net, or else you’ll get an error message.

Devices are identified on the Appletalk line by their node numbers, each device having a different number. The node number is assigned automatically when a device first uses the network; it looks around for other active nodes and chooses a node number that isn’t used yet. All this happens in a completely transparent way.

Packets sent out over the network are identified by their source and destination node numbers, which are contained in their header. Detection of the header is done by the hardware, so only packets addresses to one node will be noticed by that node, anything else on the bus will be ignored.

DDP

A destination node that receives a packet has to know what to do with it. Since one node might have several different usages for packets, another protocol layer ‘on top of’ the ALAP can distribute packets received in one node to different processes, called sockets. The protocol that ensures that a packet arrives at the right socket is called the Datagram Delivery Protocol (DDP).

DDP does the following: When a packet is received on a node, its data contains a socket number in the header. The Appletalk driver that handles DDP maintains a socket table which it searches for a match. If no match is found, the packet is simply ignored. If there is a match, control is passed to a listener routine which processes the rest of the data packet. This listener routine must conform to certain timing restrictions since the data keeps coming in at a rapid rate. Inside Mac Vol.II explains in detail how to write a listener routine.

DDP also handles the transfer of packets that are sent to other networks. As you probably know, several Appletalk networks may be interconnected by bridges to form a large internet. The internet might even include networks like Ethernet which use completely different data transfer mechanisms on the hardware and low-level software levels.

DDP can send a packet to an internet address. Such an address contains the network number, node ID and socket number of the destination. If the network number is the network that the node is on, the packet is simply sent to that node. If the node sits on another network, DDP sends the packet to a bridge on its own network (if there is one). The bridge then will know how to forward the packet. This ‘routing’ process and the way the bridges get to know about all the other networks on the internet is explained in great detail in Inside Appletalk. All we need to know here is that if you call DDP with an internet address that corresponds to an existing node and socket on some network, your data will (hopefully ) get there.

NBP

How does one find out whether a certain node exists on a large internet? Numbers are hard to remember, and they also can change since node IDs are always assigned dynamically, and socket numbers may be. Therefore there exists a mechanism - the name binding protocol (NBP) - to assign unique names to sockets at a node.

Any device on the network can be assigned a name. For instance, the Laserwriter called ‘Laser#1’ on the third floor could be called

“Laser#1:LaserWriter@3rd_floor”

where the first part of the name, up to the colon, is the actual device name, the second part, up to the ‘@’, its type, and the final part the zone where the device is located.

Each network has a zone name assigned. Several networks can have the same zone name, but one network can have only one zone name. Looking for all Laserwriters in a particular zone will give the network addresses of all Laserwriters on all networks included in that zone. Bridges know about zones and the networks they include; Again you don’t need to be concerned with the details how this is achieved. For the curious, this is well explained in Inside Appletalk’s sections on the Routing Table Maintenance Protocol (RTMP) and the Zone Information Protocol (ZIP).

For the less curious it is sufficient to know that there exist Appletalk driver calls which will for instance, given a name like “Laser#1:LaserWriter@3rd_floor” return that printer’s network ID, node ID and socket number, or for “=:LaserWriter@*” return a list of addresses of all Laserwriters in one’s own zone (“=” for the device name or type acts as a wildcard; “*” indicates the zone of one’s own network).

ATP

OK. So now we can, given the name of some device on the network, find out its address. What do we do with it?

Usually we will send some data to that device, and maybe receive some other data in return. Using DDP, this could for instance be done in the following way:

- create a socket on the node and associate to it a listener routine that will store the data received on that socket;

- send a data packet to the other device, using its network address that has been looked up using NBP. DDP will add addressing information to the data so that the receiving end knows where to send its response to.

- when a response is received from the other device, the listener routine will store the data and notify the program that data has arrived.

Note, however, that on a big internet packets might get lost, maybe some bridge breaks down or a whole network is disconnected. This might not be known to the sender at send time. Therefore, the algorithm that I just described will not necessarily ensure that the data arrives at the remote device. We would need to implement some acknowledge mechanism which re-sends a packet a certain number of times if its reception is not confirmed by the remote device after some timeout period.

Fortunately we don’t have to implement such a mechanism, as it has been provided for us in the Appletalk Transaction Protocol (ATP) driver. The two partners in a data transmission - the requesting and the responding end - are called the client and the server and interact in the following way:

- A server opens a responding socket on the network using the routine ATPOpenSocket. ATP automatically assigns a listener routine to that socket that will dump the received data into a previously assigned buffer.

- The server will then call the routine ATPGetRequest. This routine will return (or, for asynchronous calls, the IOCompletion routine will be called) when a request data packet has been received.

- We assume that the client knows the network address of the server. Otherwise, the server would have to register its name on the network and the client could find the address through that name.

- The client calls ATPSendRequest with the network address of the server, and up to 578 bytes of request data in a buffer. This data will be transmitted to the server; ATP makes sure it gets there. ATPSendRequest waits for a response from the server, which can contain up to eight data packets; it will retransmit the request as long as not all data packets have been received. For the duration of the transmission and for receiving the response, ATPSendRequest opens a socket and automatically closes it when it returns. You, the programmer, normally don’t notice this.

- The server receives the request data packet and puts it away into a buffer; ATPGetRequest returns and the server calls ATPSndRsp which transmits the response, up to eight buffers of data of a maximum of 578 bytes each. The response also contains information how many buffers in total will be sent (1 to 8), and which one is being sent in each particular packet. The client end (ATPSendRequest) maintains a table to know which packets have been received. All this is handled automatically by ATP.

There are even higher-level protocols (Printer Access Protocol, Appletalk Session Protocol, Appletalk Filing Protocol) which use ATP to implement particular functions. They don’t concern us here, but are described in Inside Appletalk and the Appletalk chapter of IM Vol.5. We have all the necessary information to implement the example program, a very basic mail utility.

Appletalk mail example

Our example consists of two programs (stand-alone on the source code disk): the sender and the receiver. The receiver will be known on the network as <choosername>:mailbox@<zone> where <choosername> is the user name given in the Chooser dialog and <zone> the zone where the network is located.

The receiver task operates in the following way: First, it opens an ATP responding socket by calling ATPOpenSocket. Then, using its socket number, it makes itself known to the network with a registerName call to the NBP. Last, it calls ATPGetRequest asynchronously and puts itself to sleep (storing SLEEP in its STATUS address and calling PAUSE); the IOCompletion routine will wake it up again when the request has arrived. The request contains the mail message, which is displayed. Then, the receiver task sends a response, which consists of one buffer of data with no particular information in it. At the end, the task closes the socket and removes its name from the network. This cycle is repeated until a key is pressed.

The sender task will first search the network for any devices that match the name description “=:mailbox@*” (i.e., all open mailboxes in its own zone). It will display the list of mailboxes with their network addresses. Then it prompts for the address where the message should be sent, and for one line of message text. It calls ATPSendRequest with the mail message in the request data block. When it has received the response from the receiver (or has received no response, in which case it generates a timeout error), it starts the cycle again, searching for mailboxes.

I should apologize here for the very experimental implementation of this ‘message service’. There are absolutely NO errors trapped, and the example will work only if you start the receiver on one Mac, then start the sender on another Mac, and then start sending mail. It will also not (yet) run in background under Multifinder; and the interface is certainly far from the User Interface Guidelines. However, the example can give you a basis on which to start your own experimentation.

Mach2 Appletalk words

After these general remarks, let’s look at the Forth code. The constant definitions are taken from Inside Macintosh and from the Atalk.h header file in Bob Denny’s Appletalk example from V2#10. All Appletalk routines are accessed through calls to either of two drivers: .MPP, which implements ALAP, DDP, RTMP and NBP, and .ATP, which implements ATP. Their driver IDs are 9 for .MPP and 10 for .ATP.

At the beginning of a program that uses Appletalk you should check whether port B is configured for Appletalk use; if the low four bits of system global SPConfig ($1FB) contain 1, this is the case. (See IM II-305). OpenATalk checks the ports and opens .MPP and .ATP if necessary.

All Appletalk driver calls are passed through a parameter block, ATPblock. I am always using the same global block, thus we can make at most one asynchronous call at a time. For this simple example this is sufficient. The words I wrote to call the different Appletalk functions that we need here all set up the parameter block first, then issue the driver _Control call through call.atp or call.mpp. I have deliberately not used assembler to set up the parameter block, but you might recode the glue in assembler to save code space.

For further details on the Appletalk calls you should refer to IM Vol II. For the ATPGetRequest function, we implement an asynchronous call to the .ATP driver. The word call.atp.async implements this function. In fact, all I/O calls that take more than a few milliseconds should be handled this way, especially in a multitasking environment like Mach2. However, for ATPGetRequest it is more essential than for the other functions, since that call never times out and thus would hang forever if the request is not satisfied.

For an asynchronous I/O call in Mach2, we define a ‘semaphore’ variable that will hold the status address of the task issuing the call. This variable is called ATPout (I couldn’t think of a better name). (get.request.async) does a ATPout get first (ATPOut must be zero for the task to continue, then the task will put its status address into that variable), then the parameter block is set up and the asynchronous call made. After the call, the task puts the value SLEEP into its status address and calls PAUSE. This will put the task to sleep until the status value is changed back to WAKE again. The completion routine does exactly this. Thus execution of the task calling (get.request.async) will continue after the PAUSE when the completion routine is called. If the completion routine is never called (i.e. the task hangs), we can always stop the Mach2 system completely by typing BYE in the main window.

Some remarks on name registration: First, if you ever were curious where a program gets the Chooser user name from, it is contained in ‘STR ‘ resource -16096 (local resource ID=0 of DRVR ID=9). get.choosername gets this name for you.

The calls to register.name and lookup.name are made synchronously, and they take several seconds. If that seems too long to you, you can replace those by asynchronous calls as well, but make sure to use different parameter blocks for each call to avoid big trouble. The name of a network device is contained in a special data structure called a names table entry or NTE, whose format is given in IM II-321 (Fig. 13). The word make.entity is used to construct the ‘entity name’ field of the NTE. When searching for network names with lookup.name, the list of names returned is in the format of several such entities. The words next.field and print.entities (further down in the listing) are used to traverse this list and print the names with their addresses in a readable format. Since the four-byte network address in that table may start at an odd byte, I also had to define >@<, which does a long word fetch at an odd address (I discovered this when I checked the example on a Mac+ and got an address error! There’s a disadvantage using the MacII, you don’t discover such things!).

Finally, while the data from an ATP request is simply put into a buffer, the data for the response must be contained in up to 8 separate buffers. What is actually passed to the (send.response) routine is the address of a buffer descriptor structure (BDS) containing 1 to 8 entries of 12 bytes each. The addresses of the different buffers and the length of the data are contained here (see IM II-288). The words setup.bds and release.bds are provided to get and release heap space for the ATP buffers.

Well, I’ll let you look at the code for the rest of the implementation and finish with some news from my side of the Atlantic that might be interesting to Mac people on both sides.

International shareware payment

- a Solidarsoft initiative -

A great problem for European users with using shareware products written in the US has always been the transfer of money across the Atlantic. Mailing checks is possible in principle, but usually the only ones that get a profit out of that are the banks with the high fees they charge for cashing foreign checks. Buying international money orders also can get expensive, and sending cash might or might not work. The second problem arises when shareware programs are used within a company or organization that needs a receipt for tax or other purposes.

It seems to be so difficult to pay shareware from overseas that the author of McSink has kindly offered his program to overseas users because his bank charges him more for cashing the check than his profit would be after taking away the cost of shipping an update disk overseas!

Solidarsoft, the French non-profit organization that was founded to help improve software developer/publisher/user relationships and that I already told you about, has now come up with a scheme that greatly facilitates international shareware payment. You send a check to Solidarsoft with a letter giving the name and version of the shareware product and the author’s address, or the shareware registration form, and they will handle the rest: make sure that the author gets the correct $$$ amount (by some economic way of money transfer), and that you will be correctly registered with the author. Using this service will be only slightly more expensive for a French shareware user than going to the bank, getting the paperwork done, and paying the author directly. Currently, to cover banking fees and other costs, Solidarsoft is applying the following formula:

You count the US$ at 6.50 French francs (FF). On top of that, you add 43 FF handling charge for international payments and 5 FF for payments within France. If a check does not cover the shareware fees plus handling, e.g. in the case of changing exchange rates (the dollar is going up as of this writing), it will simply be returned with an explanatory note. So to pay a $25 shareware product, you send the registration form together with a check on 205.50 FF to:

Solidarsoft,

66 boulevard Exelmans, F-75016 Paris, France.

Let’s hope this example catches on elsewhere. Till next month, happy threading.

Listing 1: Appletalk handling from Mach2
\ Appletalk general definitions
\ 30.05.88 JL

only forth also assembler

vocabulary network
also network definitions

DECIMAL

27 constant ioPermission
18 constant ioFileName
18 constant userData
24 constant ioRefNum
26 constant csCode
28 constant socket
30 constant addrBlock

4constant atpLoadedBit
1constant useATalk
-97constant portInUse
-98constant portNotCf

9constant mppUnitNum 
10 constant atpUnitNum  
mppUnitNum 1+ negate 
 constant mppRefNum
atpUnitNum 1+ negate
 constant atpRefNum

\ LAP defs
1constant LAPshortDDP
2constant LAPLongDDP
-94constant lapProtErr
-95constant lapExcessCollns

243constant lapWrite
244constant lapDetachPH
245constant lapAttachPH

-1 constant lapOverrunErr
-2 constant lapCRCErr
-3 constant lapUnderrunErr
-4 constant lapLengthErr

\ DDP defs
5constant ddpHdSzShort
13 constant ddpHdSzLong

1constant ddpRTMP
2constant ddpNBP
3constant ddpATP

$7Fconstant ddpMaxWKS
586constant ddpMaxData
$3ff  constant ddpLengthMask
128constant ddpWKS

-91constant ddpSktErr
-92constant ddpLenErr
-93constant ddpNoBridgeErr

\ CsCode values for DDP Control calls- MPP
246constant ddpWrite
247constant ddpCloseSkt
248constant ddpOpenSkt

\ RTMP definitions
1constant rtmpSkt

\ NBP definitions
$10constant nbpBrRq
$20constant nbpLkUp
$30constant nbpLkUpReply
2constant nbpSkt
15 constant nbpTupleMax
ascii = constant nbpEquals
ascii * constant nbpStar

0constant ntLink
4constant ntTuple
7constant ntSocket
9constant ntEntity

-1024 constant nbpBuffOvr
-1025 constant nbpNoConfirm
-1026 constant nbpConfDiff
-1027 constant nbpDuplicate
-1028 constant nbpNotFound
-1029 constant nbpNISErr

249constant nbpLoad
250constant nbpConfirm
251constant nbpLookup
252constant nbpRemove
253constant nbpRegister
254constant nbpKill
255constant nbpUnload
256constant setSelfSend

\ ATP definitions
$40constant atpReqCode      
$80constant atpRspCode
$C0constant atpRelCode
$20constant atpXO
$10constant atpEOM
$08constant atpSTS
$02constant atpTidValid
$01constant atpSendChk
$3Fconstant atpFlagMask
$F8constant atpControlMask

8constant atpMaxNum
578constant atpMaxData

249constant atpRelRspCB
250constant atpCloseSkt
251constant atpAddResponse
252constant atpSendResponse
253constant atpGetRequest
254constant atpOpenSkt
255constant atpSendRequest
256constant atpRelTCB

-1096 constant atpReqFailed
-1097 constant atpTooManyReqs
-1098 constant atpTooManySkts
-1099 constant atpBadATPSkt
-1100 constant atpBadBuffNum
-1101 constant atpNoRelErr
-1102 constant atpCBNotFound
-1103 constant atpNoSendResp
-1104 constant atpNoDataArea
-1105 constant atpReqAborted

$1FA  constant pRamByte
$1FB  constant SPConfig
$291  constant portBUse
$2D8  constant ABusVars
$2DC  constant ABusDCE

0 constant bdsBuffSz
2 constant bdsBuffAddr
6 constant bdsDataSz
8 constant bdsUserData 

.trap   _control,async  $a404

header ATPbuffer 2000 allot
header myBDS 8 12 * allot
header reqBuf 600 allot
header myNTE 100 allot
header ATPblock 50 allot

header MPPName
 DC.B 4
 DC.B ‘.MPP’

header ATPName
 DC.B 4
 DC.B ‘.ATP’

: open.atp
 PortBUse c@ $10 and
 IF 0 \ Appletalk already open!
 ELSE
 [‘] ATPName [‘] ATPBlock ioFileName + !
 0 [‘] ATPBlock ioPermission + c!
 [‘] ATPBlock call Open  
 THEN
;

: open.mpp
 [‘] MPPName [‘] ATPBlock ioFileName + !
 0 [‘] ATPBlock ioPermission + c!
 [‘] ATPBlock call Open  
;

: OpenATalk { | PBUse -- f }
 PortBUse c@ -> PBUse

 PBUse 0<
 IF SPConfig c@ $F AND 2 = 
 IF portNotCf
 ELSE
 open.mpp ?dup 0=
 IF open.atp THEN
 THEN
 ELSE 
 PBUse $F and 1 = 
 IF open.atp 
 ELSE portInUse
 THEN
 THEN
;

: close.atp
 ATPRefNum [‘] ATPBlock ioRefNum + w!
 [‘] ATPBlock call Close
;

: call.mpp
 mppRefNum  [‘] ATPBlock ioRefNum + w!
 [‘] ATPBlock call control
;
 
: call.atp
 atpRefNum  [‘] ATPBlock ioRefNum + w!
 [‘] ATPBlock call control
;

: call.atp.async ( p_complete -- flag )
 atpRefNum  [‘] ATPBlock ioRefNum + w!
 ( p_complete)   [‘] ATPBlock ioCompletion + !
 LEA    ATPBlock,A0
 EXG    D4,A7
 _Control,Async
 EXT.L  D0
 MOVE.L D0,-(A6)
 EXG    D4,A7
;
 
: open.socket  ( addrBlock socket# -- socket# flag )
 ( socket# )   [‘] ATPBlock socket + c!
 ( addrBlock ) [‘] ATPBlock addrBlock + !
 atpOpenSkt [‘] ATPBlock csCode + w!
 call.atp
 [‘] ATPBlock socket + c@
 swap
;

: close.socket ( socket# -- flag )
 ( socket# )   [‘] ATPBlock socket + c!
 atpCloseSkt   [‘] ATPBlock csCode + w!
 call.atp
;

: (send.request)  ( userData atpFlags addrBlock
 reqLength reqPointer 
 bdsPointer numOfBuffs
 timeOutVal retryCount -- 
 reqTID BitMap atpFlags numOfResps flag )
 ( retryCount )  [‘] ATPBlock 47 + c!
 ( timeOutVal )  [‘] ATPBlock 45 + c!
 ( numOfBuffs )  [‘] ATPBlock 44 + c!
 ( bdsPointer )  [‘] ATPBlock 40 + !
 ( reqPointer )  [‘] ATPBlock 36 + !
 ( reqLength ) [‘] ATPBlock 34 + w!
 ( addrBlock )   [‘] ATPBlock 30 + !
 ( atpFlags )  [‘] ATPBlock 29 + c!
 ( userData )  [‘] ATPBlock 18 + !
 atpSendRequest  [‘] ATPBlock csCode + w!
 call.atp
 [‘] ATPBlock 16 + w@
 [‘] ATPBlock 28 + c@
 [‘] ATPBlock 29 + c@
 [‘] ATPBlock 46 + c@
 4 roll ( result code )
;

: (get.request)    ( atpSocket reqLength reqPointer --
 userData atpFlags addrBlock reqLength
 bitMap transID flag )
 ( reqPointer )  [‘] ATPBlock 36 + !
 ( reqLength ) [‘] ATPBlock 34 + w!
 ( atpSocket ) [‘] ATPBlock 28 + c!
 call.atp
 [‘] ATPBlock 18 + @
 [‘] ATPBlock 29 + c@
 [‘] ATPBlock 30 + @
 [‘] ATPBlock 34 + w@
 [‘] ATPBlock 44 + c@
 [‘] ATPBlock 46 + c@
 6 roll ( result code )
;

variable ATPout ( semaphore )

code get.request.compl
 movem.la0/a5,-(a7)
 movea.lcurrenta5,a5
 movea.lATPout,a0
 move.w #wake,(a0)
 movem.l(a7)+,a0/a5
 rts
end-code
 
: (get.request.async)( atpSocket reqLength reqPointer --
 userData atpFlags addrBlock reqLength
 bitMap transID flag )
 ATPout get
 ( reqPointer )  [‘] ATPBlock 36 + !
 ( reqLength ) [‘] ATPBlock 34 + w!
 ( atpSocket ) [‘] ATPBlock 28 + c!
 [‘] get.request.compl call.atp.async
 sleep status w! pause 
 \ wake up when getRequest is completed
 [‘] ATPBlock 18 + @
 [‘] ATPBlock 29 + c@
 [‘] ATPBlock 30 + @
 [‘] ATPBlock 34 + w@
 [‘] ATPBlock 44 + c@
 [‘] ATPBlock 46 + c@
 6 roll ( result code )
 ATPout release
;

: setup.bds ( #buffers )
 0 DO
 600 call NewPtr abort” Could not get buffer memory”
 i 12 * [‘] myBDS + 2+ !
 LOOP
;

: release.bds ( #buffers )
 0 DO
 i 12 * [‘] myBDS + 2+ @ 
 call DisposPtr abort” DisposPtr failed!”
 LOOP
;
 
: (send.response) ( atpSocket atpFlags addrBlock
 bdsPointer numOfBuffs bdsSize transID --
 reqTID userData flag )
 ( transID )[‘] ATPBlock 46 + w!
 ( bdsSize )[‘] ATPBlock 45 + c!
 ( numOfBuffs )  [‘] ATPBlock 44 + c!
 ( bdsPointer )  [‘] ATPBlock 40 + !
 ( addrBlock )   [‘] ATPBlock 30 + !
 ( atpFlags )  [‘] ATPBlock 29 + c!
 ( atpSocket ) [‘] ATPBlock 28 + c!
 atpSendResponse [‘] ATPBlock csCode + w!
 call.atp
 [‘] ATPBlock 16 + w@
 [‘] ATPBlock 18 + @
 rot ( result code )
;

: (add.response) 
 ( userData atpSocket atpFlags addrBlock
 reqLength reqPointer rspNum transID --
 flag )
 ( transID )[‘] ATPBlock 46 + w!
 ( rspNum ) [‘] ATPBlock 44 + c!
 ( reqPointer )  [‘] ATPBlock 36 + !
 ( reqLength ) [‘] ATPBlock 34 + w!
 ( addrBlock )   [‘] ATPBlock 30 + !
 ( atpFlags )  [‘] ATPBlock 29 + c!
 ( atpSocket ) [‘] ATPBlock 28 + c!
 ( userData )  [‘] ATPBlock 18 + !
 atpAddResponse  [‘] ATPBlock csCode + w!
 call.atp ( result code )
;

: load.nbp
 nbpLoad [‘] ATPBlock csCode + w!
 call.mpp
;

: make.entity { object typ zone entity | obL typL -- }
 object entity   over c@ 1+ dup -> obL cmove
 typ    entity obL + over c@ 1+ dup -> typL  cmove
 zone   entity obL + typL +  over c@ 1+            cmove
;

: (lookup.name) ( interval retry buffer size max entity -- 
  matches flag )
 nbpLookup  [‘] ATPBlock csCode + w!
 ( entity ) [‘] ATPBlock 30 + !
 ( max )[‘] ATPBlock 40 + w!
 ( size ) [‘] ATPBlock 38 + w!
 ( buffer ) [‘] ATPBlock 34 + !
 ( retry )[‘] ATPBlock 29 + c!
 ( interval) [‘] ATPBlock 28 + c!
 call.mpp
 [‘] ATPBlock 42 + w@ \ matches found
 swap \ result code
;

: lookup.name ( object typ zone | -- matches flag )
 [‘] myNTE ntEntity + make.entity
 2 10 [‘] ATPbuffer 600 20 [‘] myNTE ntEntity +    
 (lookup.name)
;

: (register.name) ( interval retry ntQEl verify --  flag )
 nbpRegister   [‘] ATPBlock csCode + w!
 ( verify ) [‘] ATPBlock 34 + c!
 ( ntQEl )  [‘] ATPBlock 30 + !
 ( retry )[‘] ATPBlock 29 + c!
 ( interval)   [‘] ATPBlock 28 + c!
 call.mpp \ result code
;

: register.name ( socket# object typ zone ) { ntQEl | -- flag }
 ntQEl ntEntity + make.Entity
 ntQEl ntSocket + c! ( store socket number )
 2 10 ntQEl 1 ( always verify ) (register.name)
;

: remove.name ( ntQEl | flag )
 nbpRemove  [‘] ATPBlock csCode + w!
 ( ntQEl ) ntEntity +[‘] ATPBlock 30 + !
 call.mpp \ result code
;

: set.self.send ( self_send_flag | old_flag -- )
 setSelfSend [‘] ATPBlock csCode + w!
 ( flag ) [‘] ATPBlock 28 + c!
 call.mpp drop \ result code
 [‘] ATPBlock 29 + c@
;

4ascii STR  constant “str
4ascii MAIL constant “mail

: get.choosername
 “str -16096 call getresource
 ?dup IF @ ELSE 1 abort” No Chooser name found!” THEN
;

\ =========== Forth definitions follow ============

also forth definitions

variable mailbox.socket
header mailNTE 110 allot
 
: open.mailbox
 0 0 open.socket abort” could not get free ATP socket”
 dup mailbox.socket !
 get.choosername “ mailbox” “ *” [‘] mailNTE
 register.name abort” registerName failed”
;

: close.mailbox
 [‘] mailNTE remove.name drop
 mailbox.socket @ close.socket drop
;

: sendOK
 cr .” ---- Sending OK response.....”
 1 setup.bds
 “ This mail was received OK.” count dup [‘] myBDS w!
 [‘] myBDS 2+ @ swap cmove
 [‘] myBDS[‘] ATPBlock 40 + !
 1 [‘] ATPBlock 44 + c!
 1 [‘] ATPBlock 45 + c!
 atpSendResponse [‘] ATPBlock csCode + w!
 call.atp
 1 release.bds
;

: get.mail { | trID addr.block -- reqTID userData flag }
 mailbox.socket @ 500 [‘] reqBuf (get.request)
 abort” ATPGetRequest error!”
 5 call sysbeep
 -> trID drop ( don’t need bitmap )
 cr .” ***** Mail received *****”
 cr [‘] reqBuf swap type
 cr .” ***** End of mail   *****”
 dup -> addr.block
 cr .” sender: $” hex . .” , flags: $” . .” , User Data: $” . 
 cr decimal
 sendOK
; 

: get.mail.async { | trID addr.block -- reqTID userData flag }
 mailbox.socket @ 500 [‘] reqBuf (get.request.async)
 abort” ATPGetRequest error!”
 5 call sysbeep
 -> trID drop ( don’t need bitmap )
 cr .” ***** Mail received *****”
 cr [‘] reqBuf swap type
 cr .” ***** End of mail   *****”
 dup -> addr.block
 cr .” sender: $” hex . .” , flags: $” . .” , User Data: $” . 
 cr decimal
 sendOK
; 

: >@< ( odd address fetch, unnecessary on MacII )
 dup 2 mod
 IF dup c@ swap 1+ @ -8 scale $FFFFFF and swap 24 scale +
 ELSE @
 THEN
;
 
: next.field dup c@ + 1+ ;

: print.entities ( #entities entityTable )
 cr swap hex
 0 DO .” $” dup >@< u. .”  - “
  5 + dup count type .” :” 
  next.field dup count type .” @” 
  next.field dup count type cr
  next.field
 LOOP drop
 decimal
;

: find.boxes
 “ =” “ mailbox” “ *” lookup.name
 abort” LookupName failed”
 cr ?dup IF dup . .” mailbox(es) found on the network:” cr
 [‘] ATPBuffer print.entities
 ELSE .” No mailboxes found on the network.” cr
 THEN
; 
 
: send.mail { receiver msg | -- }
 cr .” Sending message to $” 
 receiver hex . decimal .” ...” cr
 1 setup.bds
 “mail %00100000 receiver
 msg count swap [‘] myBDS 1 2 5 
 (send.request)
 ?dup IF .” SendRequest error #” . cr
 ELSE .” Mail delivered” cr 
 THEN
 . . . . cr
 1 release.bds
;

\ ===== MAIL SENDER AND RECEIVER TASKS =====

also mac

NEW.WINDOW Sender
“ Sender” Sender TITLE
40 20 170 400 Sender BOUNDS
Document Visible CloseBox GrowBox Sender ITEMS

400 1000 TERMINAL sendTask

NEW.WINDOW Receiver
“ Mailbox” Receiver TITLE
190 20 320 400 Receiver BOUNDS
Document Visible CloseBox GrowBox Receiver ITEMS

400 1000 TERMINAL rcvTask

: mail.it
 activate
 begin
 cr .” Searching open mailboxes...” cr
 find.boxes
 hex
 begin
 cr .” To address (zero to quit): $” 
 pad dup 1+ 10 expect number? 
 until

 ?dup IF
 cr .” Message: “ 
 pad 1+ 80 expect 
 span pad c!
 pad send.mail
 ELSE bye 
 THEN
 again
;

: get.it
 activate
 begin
 cr .” Registering new Mailbox...”
 open.mailbox
 cr .” Waiting for mail...” cr
 get.mail.async drop
 close.mailbox
 ?terminal until
 bye
;

: setup.main
 0 ATPout !
 open.mpp drop
 open.atp drop
 1 set.self.send drop
;

: setup.sender
 setup.main
 Sender ADD
 Sender sendTask BUILD
 Sender call selectwindow
 sendTask mail.it
;

: setup.rcv
 setup.main
 Receiver ADD
 Receiver rcvTask BUILD
 Receiver call selectwindow
 rcvTask get.it
;
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Apple High Sierra 10.13 - The latest OS...
macOS High Sierra introduces new core technologies that improve the most important functions of your Mac. From rearchitecting how it stores your data to improving the efficiency of video streaming to... Read more
ScreenFlow 7.1 - Create screen recording...
ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your entire monitor while also capturing your video camera, microphone and your... Read more
Airmail 3.5 - Powerful, minimal email cl...
Airmail is an mail client with fast performance and intuitive interaction. Support for iCloud, MS Exchange, Gmail, Google Apps, IMAP, POP3, Yahoo!, AOL, Outlook.com, Live.com. Airmail was designed... Read more
CleanMyMac 3.9.0 - $39.95
CleanMyMac makes space for the things you love. Sporting a range of ingenious new features, CleanMyMac lets you safely and intelligently scan and clean your entire system, delete large, unused files... Read more
Numi 3.17.2 - Menu-bar calculator suppor...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
OmniGraffle 7.4.3 - Create diagrams, flo...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
ExpanDrive 6.1.0 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
VueScan 9.5.86 - 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
Cocktail 11.0 - General maintenance and...
Cocktail is a general purpose utility for macOS that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
OmniGraffle Pro 7.4.3 - Create diagrams,...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more

Morphite guide - how to explore like a p...
The much anticipated space exploration game, Morphite, has finally arrived, and we can't get enough of it. The game is essentially everything we wanted No Man's Sky to be. It's a game that puts a heavy focus on exploring foreign worlds, but the... | Read more »
The best visual novels on mobile
Narrative games have been around for ages, but only now have they been creeping into the mainstream spotlight. These games tell some of the industry's finest stories, and they break new ground in terms of gameplay and mechanics regularly. Here are... | Read more »
The best new games we played this week -...
It's pretty much been one big release after another. We were privy to a bunch of surprises this week, with a lot of games we'd been waiting for quite some time dropping unexpectedly. We hope you're free this weekend, because there is a lot for... | Read more »
Stormbound: Kingdom Wars guide - how to...
Stormbound: Kingdom Wars is an excellent new RTS turned card battler out now on iOS and Android. Lovers of strategy will get a lot of enjoyment out of Stormbound's chess-like mechanics, and it's cardbased units are perfect for anyone who loves the... | Read more »
The best AR apps and games on iOS right...
iOS 11 has officially launched, and with it comes Apple's ARKit, a helpful framework that makes it easier than ever for developers to create mobile AR experiences. To celebrate the occassion, we're featuring some of the best AR apps and games on... | Read more »
Phoenix Wright: Ace Attorney - Spirit of...
Phoenix Wright: Ace Attorney - Spirit of Justice 1.00.00 Device: iOS Universal Category: Games Price: $.99, Version: 1.00.00 (iTunes) Description: ************************************************※IMPORTANT※・Please read the “When... | Read more »
Kpressor (Utilities)
Kpressor 1.0.0 Device: iOS Universal Category: Utilities Price: $4.99, Version: 1.0.0 (iTunes) Description: The ultimate ZIP compression application for iPhone and iPad. - Full integration of iOS 11 with support for multitasking.-... | Read more »
Find out how you can save £35 and win a...
Nothing raises excitement like a good competition, and we’re thrilled to announce our latest contest. We’ll be sending one lucky reader and a friend to the Summoners War World Arena Championship at Le Comedia in Paris on October 7th. It’s the... | Read more »
Another Lost Phone: Laura's Story...
Another Lost Phone: Laura's Story 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: Another Lost Phone is a game about exploring the social life of a young woman whose phone you have just... | Read more »
The Witness (Games)
The Witness 1.0 Device: iOS Universal Category: Games Price: $9.99, Version: 1.0 (iTunes) Description: You wake up, alone, on a strange island full of puzzles that will challenge and surprise you. You don't remember who you are, and... | Read more »

Price Scanner via MacPrices.net

Snag a Certified Refurbished Apple Pencil for...
Apple has Certified Refurbished Apple Pencils available for $85 including free shipping. Their price is $14 off MSRP, and it’s the lowest price available for a Pencil. Read more
12-inch 64GB iPad Pro on sale for $749, save...
Adorama has 12″ 64GB iPad Pros on sale today for $749 including free shipping plus NY & NJ sales tax only. Their price is $50 off MSRP. Read more
Apple Certified Refurbished iPad minis availa...
Apple has Certified Refurbished 128GB iPad minis available today for $339 including free shipping. Apple’s standard one-year warranty is included. Their price is $60 off MSRP. Read more
12-inch 1.2GHz Retina MacBook Pros on sale fo...
B&H Photo has 2017 12″ 1.2GHz Retina MacBooks on sale for $100 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only: 12″ 1.2GHz Space Gray MacBook: $1199 $100 off MSRP 12... Read more
Sunday sale: 13-inch 3.1GHz MacBook Pros for...
Amazon has 2017 13″ 3.1GHz MacBook Pros on sale today for up to $150 off MSRP, each including free shipping: – 13″ 3.1GHz/256GB Space Gray MacBook Pro (MPXV2LL/A): $1649.99 $150 off MSRP – 13″ 3.1GHz... Read more
Looking for a 2017 12″ Retina MacBook? Save $...
Apple has Certified Refurbished 2017 12″ Retina MacBooks available for $200-$240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free.... Read more
Apple Offering Up To $455 Credit Toward iPhon...
iPhone 8 and 8 Plus are now available at the Apple Store, and you can receive up to $375 credit toward a new iPhone purchase when you trade in your eligible smartphone. Photo Courtesy Apple Just... Read more
AnyTrans Offers iOS Users Three Ways For Movi...
iMobie Inc. today announceed AnyTrans v6.0.1, which now can help iOS users move all data to iPhone 8/8 Plus seamlessly. The software is available both on Mac and Windows and fully able to move all... Read more
Snag a 13-inch 2.3GHz MacBook Pro for $100 of...
B&H Photo has 2017 13″ 2.3GHz MacBook Pros in stock today and on sale for $100 off MSRP, each including free shipping plus NY & NJ sales tax only: – 13-inch 2.3GHz/128GB Space Gray MacBook... Read more
Verizon offers new iPhone 8 for $100-$300 off...
Verizon is offering the new iPhone 8 for up to $300 off MSRP with an eligible trade-in: • $300 off: iPhone 6S/6S Plus/7/7 Plus, Google Pixel XL, LG G6, Moto Z2 Force, Samsung Galaxy S7/S7 edge/S8/S8... Read more

Jobs Board

Development Operations and Site Reliability E...
Development Operations and Site Reliability Engineer, Apple Payment Gateway Job Number: 57572631 Santa Clara Valley, California, United States Posted: Jul. 27, 2017 Read more
Data Engineer - *Apple* Media Products - Ap...
Job Summary Apple is seeking a highly skilled data engineer to join the Data Engineering team within Apple Media Products. AMP (home to Apple Music, App Read more
Development Operations and Site Reliability E...
Development Operations and Site Reliability Engineer, Apple Payment Gateway Job Number: 57572631 Santa Clara Valley, California, United States Posted: Jul. 27, 2017 Read more
Development Operations and Site Reliability E...
Development Operations and Site Reliability Engineer, Apple Payment Gateway Job Number: 57572631 Santa Clara Valley, California, United States Posted: Jul. 27, 2017 Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.