TweetFollow Us on Twitter

Volume Number:3
Issue Number:5
Column Tag:Forth Forum

Background Processing Under Switcher

By Jörg Langowski, MacTutor Editorial Board, Grenoble, France

Switcher is the closest thing to a multiprocessing system on the Macintosh (not counting Servant, a fully functional version of which has not seen the light of day as I write this, or Finder 6.0 which Apple is rumored to be developing). It is not really multiprocessing, since an application that is running in one partition of Switcher is put to sleep when you switch to another one. True multiprocessing is not easy to achieve on the Mac because it lacks memory management hardware (this, of course, has changed with the Mac II).

Switcher does support some background processing, however. The problem is that the application has to cooperate to allow background tasks to run. There is no way (no easy way, at least) that Switcher could interrupt a running application and transfer control to another one. It is the application itself that has to draw a clear line between foreground and background tasks, setting up pieces of its code to be run in the background that will by themselves transfer control back to Switcher.

This is very similar to the way multiprocessing is achieved in the various Forth and Forth-based systems that you've been reading about in this column. In those systems, tasks have to be cooperative and transfer control back to the task switcher at convenient points in their code (through words like PAUSE in MacForth and Mach2).

A Macintosh application under Switcher contains one interface point where control may be transferred to another task: the GetNextEvent routine. In fact, Switcher will only switch tasks on a call to GetNextEvent (when a switch request has been made).

It is at this point where background processing can take place. Every time an application calls GetNextEvent, Switcher will not only check whether you clicked the mouse on the double arrow or issued a keyboard command to initiate switching, but also check whether any of the installed applications contain background tasks that have to be run while they are inactive.

Such a background task routine may be a short piece of code that checks whether a character has come in from the serial port and stores it away in a file, or vice versa output from a file to a serial line. It may also run some ongoing calculations, collect data from an instrument, etc. It should return not too late after it has been called, in order not to cause too much slowing down of the active application. The backgound task will be part of an application installed under Switcher, where the task setup is done by the active part of the application and the task takes over when the application becomes inactive.

I'm sure none of you will be able to write a background task from these very general remarks. So how does one actually do it? A very useful document to read at this point is Inside Switcher (available through APDA). This document takes the same holistic approach as Inside Macintosh in that it requires you to understand all of it before you can make sense of the first paragraph (or nearly so), but otherwise gives a very good account about the things to observe when writing applications that are to run under Switcher.

First, lets look at the way task information is stored in Switcher. There is one global variable, SwitcherGlobals ($282), which contains a pointer to the beginning of the Switcher global variable area in the system heap. The structure of this area is described in Inside Switcher, and I'll shortly summarize it here.

The example listing contains the definitions for the Switcher tables. Each application is contained in a designated piece of memory, called a world, which is pointed to by one of the world pointers at the beginning of the Switcher globals. An application's world starts with an 18-byte header, followed by the application stack and heap area. The last 32K of a world are reserved for use by Switcher.

The header of a Switcher world contains in sequence:

- PSRPtr, a handle to the process state record of the application (explained later),

- a 2-byte flag field,

- BkgdProc, a pointer to a background procedure. Each installed application may have one BkgdProc associated with it, and Switcher calls them in sequence whenever a null event is fetched from the event queue. If this pointer is NIL, nothing is executed.

- SavScrBits, a handle to the part of memory that contains the saved screen bitmap of the application. If this is =0, the screen is not being saved.

- DAPtr, a window pointer to a virtual desk accessory that the Switcher uses for Clipboard conversion. (This procedure is explained in Inside Switcher, and I won't go into details here).

So all we'll have to do to execute a background routine under Switcher is to store a valid pointer to the routine in the BkgdProc field of the world header. Then Switcher will jump to that routine on each null event and probably come up with a beautiful bomb display unless the routine to execute is either extremely simple (such as RTS) or we have taken further precautions.

Ideally, we'd like to write the background task in Forth and execute it in our Forth system's world. To do this, you need some interface to the Forth code that can be jumped to directly and returns via an RTS. In Mach2, the Forth code itself has those properties, since it is subroutine threaded. In the case of MacForth (for example), we'll have to store the address of the routine's code field in the BkgdProc field of the Switcher world, and make sure the code returns via an RTS (and not through the Forth system's NEXT routine). The example program is in Mach2, but MacForth users will easily be able to adapt the code to their system following these principles. NEON users will have to define the background routine using :proc and ;proc, the words provided for defining routines whose addresses may be passed as parameters to toolbox traps.

The problem with executing a background task written in Forth using the surrounding support of the Forth system is of course that all existing Forth systems depend heavily on a set of registers in which parameter stack, return stack, user area pointers, loop indices, etc. are kept. The status of the registers, however, is completely undefined when Switcher calls the background routine. Therefore, the simple approach described above must fail.

In order to properly execute a background routine from outside, we have to do what is known as context switching, meaning that a) the register file has to be restored to the values used by the Forth system, and b) any system global variables that are used by the Forth system have to be restored as well. All this is done automatically by Switcher on switching applications, but not when calling a background task; that would be much too much overhead.

The full context switching is effected by Switcher through the information stored in the process state record of the application. This table is pointed to by the handle PSRPtr mentioned above. Its first four bytes contain its total size, followed by a copy of many of the low-memory globals used by the application. The location of important ones among them is given in Listing 1. Notice that StkLowPt and HeapEnd are contained here, these variables being consulted by the 'stack sniffer' to determine whether to display a System Error 28.

What interests us most here is that the last four bytes of the process state record contain the value of the stack pointer left behind when control was transferred to another application. The last thing saved on that stack before switching are registers A0-A6 and D0-D7, through a MOVEM.L A0-A6/D0-D7, -(A7).

Therefore, restoring A7 to the value left behind and then popping the other registers off the stack will leave us with a register set equal to the one left behind on switching. But... trying to do so is almost certain to get you a number 28 bomb, because A7 it totally out of line with the actual contents of StkLowPt and HeapEnd. There is an excellent chance that the stack sniffer will notice this and bang. The method I chose in the glue routine for calling background procedures is to use A0 as a temporary stack pointer and restore the registers from there. Since I do not change StkLowPt and HeapEnd, A7 must stay the same as well, so the A7 stack of the calling application is used for the background routine. This really doesn't make a big difference, as long as your routine doesn't do funny things with JSR return addresses left on the stack.

After the registers have been restored, of course, they're all valid for the context of the background task (including A5).

How does a background task know which Switcher world it is contained in (we need to know this to find the process state record and get the stack pointer)? Lets look at the example listing again at this point. I have provided a couple of words that are useful when running under Switcher. isSwitcher looks at the SwitcherGlobals pointer; if it is positive (i.e. the pointer is valid), Switcher is installed. Worlds will display the names of all installed applications. The way this is done is by looking at all of the eight world pointers, access the process state record for each application and display the string starting at PSR+344. This is where a copy of the system global AppName is kept. The general layout of the global variable space in the PSR can be seen from the listing; not all of the system globals are transferred on switching, but the variables for Quickdraw, Toolbox and the Segment Loader (starting at $800 in the 'real world') are saved consecutively starting at PSR+72.

ActiveWorld# displays the number of the world that the active application (Mach2, in this case) is currently running in. This routine gives interesting information, but is useless for code in a background routine to determine whether it is active or not (because we still don't know which application contains the background routine). Inside Switcher gives a recommendation: store the value of ApplZone (global $2AA) in variable space in your program when the application is initialized (storeApplZone does this), and later compare this saved value with the current value (IamActive is provided for this purpose).

I hope you're well enough 'primed' with some aspects of Switcher now so that we may continue with the actual problem of the day, installation of a Forth-written background routine that performs some useful task.

The routine that Switcher executes will be a glue routine that makes sure the register file is ok, calls the Forth routine that has to be executed, then restores the registers and returns to Switcher.

Switcher passes one parameter to the background routine, the task's WorldPtr in register A1. The glue routine first determines whether the application that contains the task is active or not. If the application is active, the process state record may be invalid (it is updated only when the application becomes inactive); therefore the actual background task will be skipped over in that case.

If the application is inactive, the glue routine gets the PSRPtr from the world header and restores the register file as described above. Then, control is passed to the background task. On return, everything is restored.

The background task in the example will open a text file and do a draft quality print (ASCII mode) on the Imagewriter. The program is adapted (rather heavily adapted) from an example given by Palo Alto Shipping on their demonstration disk. The original program was meant to print a file as a background task under Mach2, and therefore contained PAUSEs and async I/O calls. These features are not really needed here and have been removed. In fact, they're dangerous for what we are doing for the following reason:

It is not really possible to jump into the context of the Forth system by just restoring all registers and expecting everything else to behave. The problem is with the low memory system globals, which would have to be updated, too. It seems that anything that contains a PAUSE will bomb. One of the reasons for that might be that transfer will be given to other Mach2 tasks that try to write to the screen, and that is a no-no for background tasks according to Inside Switcher.

The other Forth routines, notably those which are called by offsetting from A5 or going through a jump table, will execute normally from the background. Macintalk will bomb; anything that requires calls to the Segment Loader, probably, too (I haven't tried this).

The printing routine, then, does the following: From the foreground, a file to print is selected. Then the background task is installed; when the application becomes inactive, it will start to read the file in 512-byte chunks and dump it on the printer. Meanwhile, you will be able to use other applications under Switcher. However: 512 byte blocks are really too large to make the other programs run smoothly. I haven't changed the block size in the example, but it is an easy matter to do so; it might even be better to transfer only one character on each call of the background task. Anyway, the example does illustrate the point, and you'll be able to install other background processes this way (and/or translate the whole thing to NEON or MacForth).

After a file has been printed, the SFGetFile dialog comes up again (in the foreground), and you can select another one. Cancelling the dialog will remove the printing routine from the Switcher worlds.

Concluding remarks

There is a lot more to be said about Switcher: how to access one application's variables from another program, how to deal with VBL routines and asynchronous I/O, how to switch automatically from within a program, clipboard conversion, suspend/resume events, etc. etc. But I cannot repeat Inside Switcher here, so I'd strongly suggest you to get a copy from APDA in case these subjects are of any interest to you.

Last time I had promised to give some more hints about DA programming from Forth; we'll put that off for a later column.

There is one last comment I have to make concerning the eternal MacForth vs. The Other Forths issue. I happen to use Mach2 for the examples in my column; this shouldn't be viewed as a bias on the part of MacTutor or myself for one particular system. It is just that I prefer to become familiar with one development system rather than trying to cover everything. I try to keep the Forth code quite generic, to facilitate translation into other dialects, or use assembler. Since (I hope) this column is also read by people who develop in other languages, it is also important to stay rather close to the toolbox, which Mach2 happens to do. Combine this with an assembler syntax which conforms to the Motorola syntax, and the code should be easily translatable into C or Pascal as well.

I realize that there are many of you readers out there who do a lot of development in other Forth dialects. As you know, I always welcome contributions by readers to add some variety to this column. So if you have an interesting example in MacForth, Mach2, MasterForth or another Forth, or NEON, by all means write it up and send it to me (directly to my address given in the masthead).

We're hoping to hear from you. Until next month, happy threading.

Listing 1: Switcher background printing example
( Forth background applications under Switcher )
( Feb 1987 J. Langowski / MacTutor )
( raw printing example adopted from Mach2 example disk )
( for background printing under Switcher )

only forth also assembler also mac 

( =============== File System Equates ============= )

1CONSTANT fsRdPerm ( read-only)
2CONSTANT fsWrPerm ( write-only)
3CONSTANT fsRdWrPerm ( read-write)

1CONSTANT fsFromStart
3CONSTANT fsFromMark


0CONSTANT    rGood
6CONSTANT    rVolume

CCONSTANT ioCompletion
10 CONSTANT ioResult
12 CONSTANT ioFileName
18 CONSTANT ioRefNum
1A CONSTANT ioFileType
1B CONSTANT ioPermssn
20 CONSTANT ioBuffer
24 CONSTANT ioReqCount
28 CONSTANT ioActCount
2E CONSTANT ioPosOffset

 ( error code -39. used in file read operations )  
HEADER  typeList

HEADER  sfReplyA
 DC.B 0 ( good )
 DC.B 0 ( copy   pg.25 PackSF )
 DC.B 'TEXT'( fType )
 DC.W 0 ( vRefNum )
 DC.W 0 ( version )
 DCB.B  64,0( fName [string 63] )  
 DC.B 5
 DC.B '.Bout'
( ====  Various Imagewriter commands ======= )

( Top-Of-Form    CTRL-L   $0C  )
( Software Reset ESC C    $1B $63  )
( LF's after CR'sESC D @ CTRL-@ $1B  $44 $80 $00  )
( No LF's after CR's ESC Z @ CTRL-@ $1B      $5A $80 $00  )
( cancels all unprinted   CTRL-X   $18  )

 DC.B $1B
 DC.B $44
 DC.B $80
 DC.B $00
 DC.B $1B
 DC.B $28
 DC.B '008,016,024,032,040,048,056,064'
 DC.B $0C
 DS.L 1
LABEL LeavePrintFlag
 DS.W 1
LABEL MyBuffer
 DS.B 512
LABEL FilePBlock
 DS.B 50
LABEL PrintPBlock
 DS.B 50

( These are the old routines )
( given by Palo Alto Shipping in the Mach 2.0 examples. )
( Since it's all in assembler, we can be sure that no calls )
( are made to the multitasking system which would interfere )
( with our background processing )

( ====  Get File Info ====== )

( Ask for the file to be converted)

 MOVE.W #80,-(A7)( where.h )
 MOVE.W #50,-(A7)( where.v )
 CLR.L  -(A7)    ( prompt not used )
 CLR.L  -(A7)    ( we don't need filter )
 MOVE.W #1,-(A7) ( look for 1 type )
 PEA    typeList ( pointer to typeList )
 CLR.L  -(A7)    ( no dlgHook )
 PEA    sfReplyA ( push address of reply record)
 MOVE.W #2,-(A7) ( for SFGetFile )
 _Pack3 ( call package )
 LEA    sfReplyA,A0( Point to sfReply )
 TST.B  rGood(A0)(If cancel was clicked, return)
 BEQ.S  @2
( Open the source file)

 LEA    filePBlock,A1( Point to param block )
 CLR.L  ioCompletion(A1)  ( No completion routine )
 PEA    rName(A0)( Point to file name )
 MOVE.L (A7)+,ioFileName(A1)
 ( Store pointer in diskPBlock)
 MOVE.W rVolume(A0),ioVRefNum(A1) 
 ( Store vRefNum in diskPBlock)
 CLR.B  ioFileType(A1)  ( Must set file type = 0 )
 MOVE.B #fsRdPerm,ioPermssn(A1) 
 ( Open for read only )
 CLR.L  ioMisc(A1) ( Use system buffer  )
 MOVE.L A1,A0    ( A0 points to diskPBlock  )
 _Open  ( Open the file )
 TST.B  D0
 BNE.S  @1
 MOVE.W 24(A0),22(A0)
 ( move ioRefNum into position)
 MOVE.W #1,44(A0)( ioPosMode from beginning)
 CLR.L  46(A0)   ( ioPosOffset of 0)
 _SetFPos ( this will reset the file)
 LEA  CRCounter,A0
 CLR.L  (A0)
@1 MOVE.W #1,-(A7)
@2 _ExitToShell

( ***** Initialize Port B  ***** )

CODE InitPortB ( SerOpenBOut) 
 LEA    PrintPBlock,A0  ( Head of PBBlock)  
 CLR.L  $C(A0)   ( No completion routine )
 LEA    PortBOut,A1( ioNamePtr )
 MOVE.L A1,$12(A0) 
 MOVE.B #0,$1B(A0) ( Ask for all permission )
 MOVE.W #-9,$18(A0)( ioRefNum )

( SerRstBOut)  
 LEA    PrintPBlock,A0  ( Head of block )
 MOVE.W #-9,$18(A0)( ioRefNum  )
 MOVE.W #$0008,$1A(A0)  ( Control number )
 MOVE.W #$CC0A,$1C(A0)  
 ( 9600 Baud,8d,2s,no parity )
( Configure)
 LEA    PrintPBlock,A0
 CLR.L  12(A0)   ( ioCompletion )
 LEA    AddLFs,A1
 MOVE.L A1,32(A0)( ioBuffer  )
 MOVE.L #4,36(A0)( ioReqCount )
 LEA    PrintPBlock,A0
 CLR.L  12(A0)   ( ioCompletion )
 LEA    AddTabs,A1
 MOVE.L A1,32(A0)( ioBuffer )
 MOVE.L #34,36(A0) ( ioReqCount )
( ClrLeaveFlag)  
 LEA    LeavePrintFlag,A0
 CLR.W  (A0)
( ====   Read a block from the file ======= )

CODE ReadBlock
 LEA  FilePBlock,A0
 CLR.L  12(A0)   ( ioCompletion)
 LEA    MyBuffer,A1
 MOVE.L A1,32(A0)( ioBuffer)
 MOVE.L #512,36(A0)( ioReqCount)
 CLR.W  44(A0) ( ioPosMode, from current mark)
@3 CMPI.L #512,40(A0)( ioActCount )
 BEQ.S  @2
 LEA    LeavePrintFlag,A1
 MOVE.W #$FF,(A1)
@2 RTS

( ***** Put in the page breaks ***** )

( Search thru MyBuffer for CR's -> $0D. 
  In place of every 60th one, I will insert a $0C, 
  which, when sent to an Imagewriter, will generate a form feed.  )

CODE PutPageBreaks
 LEA    FilePBlock,A0
 MOVE.L 40(A0),D0( ioActCount  )
 LEA    MyBuffer,A1
 LEA    CRCounter,A0
 CLR.L  D1
@1 CMPI.B #$0D,0(A1,D1) (  carriage returns )
 BEQ.S  @2
@3 ADDQ.L #1,D1  ( increment address offset )
 SUBQ.L #1,D0  ( decrement byte counter )
 BNE.S  @1
 RTS    ( ***** this is the exit point *** )
@2 ADDQ.L #1,(A0)
 CMPI.L #60,(A0)
 BNE.S  @3( if this is the 60th CR )
 MOVE.B #$0C,0(A1,D1)( then insert a FF char  )
 CLR.L  (A0)( and reset the crcounter )
 BRA.S  @3
( *****  Write out a block to port B ***** )

CODE WriteBlock
 LEA  PrintPBlock,A0
 CLR.L  12(A0)   ( ioCompletion)
 LEA  MyBuffer,A1
 MOVE.L A1,32(A0)( ioBuffer)
 LEA  FilePBlock,A1
 MOVE.L 40(A1),36(A0)
 ( move ActCount into ReqCount)
( *****   Feed the final form feed  ***** )

CODE DoFormFeed
 LEA  PrintPBlock,A0
 CLR.L  12(A0)   ( ioCompletion )
 LEA  FormFeed,A1
 MOVE.L A1,32(A0)( ioBuffer )
 MOVE.L #1,36(A0)( ioReqCount)
( ***** Close the file ***** )

 LEA    FilePBlock,A0
 CLR.L  12(A0) ( ioCompletion)
 _Close ( ioRefNum should still be there)
( *****  Main Thing ***** )
: Print.bkg
 LeavePrintFlag W@ 0=
 IF   ( 5 call sysbeep )  ( for testing )
 ReadBlock( read a block in from file )
 PutPageBreaks ( insert form feeds every 60 lines)
 WriteBlock ( write the block to port B )

( **** globals )
282 CONSTANT SwitcherGlobals
286 CONSTANT MemToSwitch
910 CONSTANT CurApName

( **** Switcher globals )
 0 CONSTANT World0 ( pointer )
 4 CONSTANT World1 ( pointer )
 8 CONSTANT World2 ( pointer )
12 CONSTANT World3 ( pointer )
16 CONSTANT World4 ( pointer )
20 CONSTANT World5 ( pointer )
24 CONSTANT World6 ( pointer )
28 CONSTANT World7 ( pointer )
32 CONSTANT HostTask ( pointer )
36 CONSTANT TheTask( pointer )
40 CONSTANT Flags( word )
50 CONSTANT ArrowEnable ( byte )
51 CONSTANT ClipConvert ( byte )
52 CONSTANT Hibernation ( word )
54 CONSTANT MainZone ( pointer )
58 CONSTANT CoreTable( 6 LongInts )
90 CONSTANT NextTask ( pointer )
96 CONSTANT SwitcherName  ( Str255 )

( **** structure of a world )
 0 CONSTANT PSRPtr ( pointer to PSR )
 4 CONSTANT WorldFlags  ( word )
 6 CONSTANT BkgdProc ( pointer to background routine )
10 CONSTANT SavScrBits  ( pointer to saved Screenbits )
14 CONSTANT DAPtr( pointer to virtual DA )
18 CONSTANT StackHeap( stack & heap starts here... )

( **** Switcher process state record )
 0 CONSTANT Size ( LongInt : PSR size )
 4 CONSTANT myMonkey ( word )
 6 CONSTANT MemTop ( address of end of RAM for this process)
10 CONSTANT BufPtr ( address of end of jump table )
14 CONSTANT StkLowPt ( lowest stack address )
18 CONSTANT HeapEnd( address of top of heap )
22 CONSTANT TheZone( address of current heap zone )
26 CONSTANT ApplLimit( application heap limit )
30 CONSTANT SEvtEnb( 4 bytes )
34 CONSTANT MyApplZone  ( application heap zone )
38 CONSTANT MinStack
72 CONSTANT GrafBegin
332 CONSTANT MyA5( hex 14C )
836 CONSTANT DefVCBPtr  ( pointer to default VCB )

( **** event record structure )
 0 CONSTANT      what     ( Integer )
 2 CONSTANT      message  ( LongInt )
 6 CONSTANT      when     ( LongInt ) 
10 CONSTANT      where    ( Point )
14 CONSTANT      modifiers  ( Integer )

VARIABLE thisApplZone ( for intermediate storage )

( **** definitions )

: =string { aStr bStr | -- flag }
 aStr count 65536 * bStr count rot + swap
 call CmpString 0=

: isSwitcher SwitcherGlobals @ 0> ;

: Worlds { | SwGl -- }
 SwitcherGlobals @ -> SwGl 
 SwGl 0> 
 8 0 DO
 I 4 * SwGl + @ ?dup ( world # i non-NIL ? )
 cr ." World No." i . ." : "
 @ @ MyName + count type 

: ActiveWorld# { | SwGl wn -- world number }
 SwitcherGlobals @ -> SwGl
 -1 -> wn 
 SwGl 0> 
 8 0 DO 
 I 4 * SwGl + @ ?dup ( world # i non-NIL ? )
 @ @ MyApplZone + @ ApplZone @ =
 IF I -> wn leave THEN

( this routine should be called immediately after launch )
( or in any case before a background routine is installed )
: storeApplZone ApplZone @ thisApplZone ! ;

: IamActive thisApplZone @ ApplZone @ = ;

HEADER tempA7    DC.L 0
HEADER myWorld   DC.L 0

CODE Bkg.glue
 MOVE.W   SR,-(A7)
 MOVEM.L  D0-D7/A0-A6,-(A7)
 LEA    tempA7,A0
 MOVE.L   A7,(A0)
 LEA    myWorld,A0
 MOVE.L   A1,(A0)
 MOVE.L   (A1),A1( PSRPtr -> A1 )
 LEA    myPSRPtr,A0
 MOVE.L   (A1),(A0)
 LEA    SwitcherGlobals,A0
 MOVE.L   (A0),A0
 CMPA.L theTask(A0),A1
 BEQ.S  @1
 ( we're active, so don't execute background )
 MOVE.L   (A1),A1( start of PSR )
 MOVE.L   (A1),A0( size of PSR )
 ADDA.L   A1,A0  
 SUBQ.L   #4,A0  (left stack pointer behind here)
 MOVE.L   (A0),A0
 MOVEM.L  (A0)+,D0-D7/A0-A6  
 ( restore MACH2 register set, except A7 )
 LEA    myPSRPtr,A0
 MOVE.L   (A0),A1   
 ( just in case the background routine needs it )

 JSR    print.bkg

@1 LEA  tempA7,A0
 MOVE.L   (A0),A7
 MOVEM.L  (A7)+,D0-D7/A0-A6
 MOVE.W   (A7)+,SR

1000 6000 BACKGROUND printer
: initBkg
 IamActive IF
 GetFInfo ( open file to print, exit if cancel )
 ['] Bkg.Glue
 SwitcherGlobals @ ActiveWorld# 4 * + @
 BkgdProc +

: rmvBkg
 IamActive IF
 SwitcherGlobals @ ActiveWorld# 4 * + @
 BkgdProc +

: startme
 BEGIN PAUSE LeavePrintFlag w@ UNTIL

: backgo ACTIVATE startme ;

: main  printer BUILD
 printer backgo
(  turnkey main backprint 
 would now create the background printing application. )
Apple Inc.
Microsoft Corpora
Google Inc.

MacTech Search:
Community Search:

Software Updates via MacUpdate

Viber 4.1.0 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Apple iOS 7.1.1 - The latest version of...
The latest version of iOS can be downloaded through iTunes. Apple iOS 7 brings an all-new design and all-new features. Simplicity Simplicity is often equated with minimalism. Yet true simplicity is... Read more
1Password 4.3 - Powerful password manage...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Lens Blur 1.3.0 - True out-of-focus boke...
Let Lens Blur transform your existing photo into true SLR-quality out-of-focus bokeh effect! Everyone needs a gorgeous personalized background for a social profile, blog, Web/UI design, presentation... Read more
VMware Fusion 6.0.3 - Run Windows apps a...
VMware Fusion allows you to create a Virtual Machine on your Mac and run Windows (including Windows 8.1) and Windows software on your Mac. Run your favorite Windows applications alongside Mac... Read more
Tweetbot 1.5.1 - Popular iOS twitter cli...
Tweetbot is a full-featured OS X Twitter client with a lot of personality. Whether it's the meticulously-crafted interface, sounds and animation, or features like multiple timelines and column views... Read more
Mac DVDRipper Pro 4.1.7 - Copy, backup,...
Mac DVDRipper Pro is the DVD backup solution that lets you protect your DVDs from scratches, save your batteries by reading your movies from your hard disk, manage your collection with just a few... Read more
PDFpenPro 6.2 - Advanced PDF toolkit for...
PDFpenPro allows users to edit PDF's easily. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Create... Read more
PDFpen 6.2 - Edit and annotate PDFs with...
PDFpen allows users to easily edit PDF's. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Features... Read more
Monolingual 1.5.9 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more

Latest Forum Discussions

See All

Zeebox is Now Beamly TV – Still Offers t...
Zeebox is Now Beamly TV – Still Offers the Same Social Networking TV Fun, Plus a New Look Posted by Rob Rich on April 22nd, 2014 [ permalink ] | Read more »
Bandai Namco Unveils Upcoming Slate of M...
At their recent Global Gamers’ Day event, Bandai Namco was largely focused on their console and PC offerings for the upcoming year. However mobile still had a small presence, with some upcoming titles revealed by the company – though few were in a... | Read more »
The Story Behind Grammar Girl’s Grammar...
Learning is so much more rewarding when it’s also fun. That’s part of the reason why recent educational app, Grammar Pop HD, garnered such a positive review from us last week. The brainchild of Mignon Fogarty, otherwise known as Grammar Girl, we... | Read more »
Pinnacle Studio for iPhone Review
Pinnacle Studio for iPhone Review By Jennifer Allen on April 22nd, 2014 Our Rating: :: SIMPLY POWERFULiPhone App - Designed for the iPhone, compatible with the iPad Video editing while on the move has never been so convenient.   | Read more »
Taste Savant Review
Taste Savant Review By Jennifer Allen on April 22nd, 2014 Our Rating: :: TASTY RECOMMENDATIONSiPhone App - Designed for the iPhone, compatible with the iPad In need of some new restaurant recommendations? This app will be ideal,... | Read more »
Where’s My Water? Featuring XYY Adds Mor...
Where’s My Water? Featuring XYY Adds More Than Just Sheep to the Liquid Physics Puzzler Posted by Rob Rich on April 22nd, 2014 [ permalink ] | Read more »
FarmVille 2: Country Escape Review
FarmVille 2: Country Escape Review By Jennifer Allen on April 22nd, 2014 Our Rating: :: STEADY FARMINGUniversal App - Designed for iPhone and iPad FarmVille is bigger and brighter than ever before, but don’t expect it to sway you... | Read more »
Doctor Who: Legacy receives big update,...
Doctor Who: Legacy receives big update, adds Perk System and more Posted by Tre Lawrence on April 22nd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Strongarm Universal Mount Hardware Revie...
Made by: Viatek Price: $14.99 for 2 Hardware/iOS Integration Rating: 3.5 out of 5 stars Usability Rating: 4 out of 5 stars Reuse Value Rating: 3.75 out of 5 stars Build Quality Rating: 3.5 out of 5 stars Overall Rating: 3.69 out of 5 stars | Read more »
Boxer Review
Boxer Review By Campbell Bird on April 21st, 2014 Our Rating: :: KO YOUR INBOXUniversal App - Designed for iPhone and iPad Turn your inbox into a task list or tune it to your own needs with this highly customizable, user friendly... | Read more »

Price Scanner via

Updated iPad Price Trackers
We’ve updated our iPad Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers. Using a mobile device? We’ve also updated... Read more
Everything You Wanted To Know And Probably Mo...
Macworld UK’s Lou Hattersley takes a look inside Apple’s A7 System On Chip (SoC) , noting that its processor module is much more powerful than other smartphone chipsets. He notes that the A7 was a... Read more
Mavericks Now Runs Half Of Internet Connected...
Computerworld’s Greg Keizer reports that half of all Macs that went online in March were running OS X Mavericks with Version 10.9 accounting for the largest adoption percentage of any individual OS X... Read more
WinZip Mac 3 Simplifies Cloud Sharing And Pri...
WinZip has announced WinZip Mac 3, a new file sharing app that makes it simple to manage, protect and share files across email and the cloud. With direct support for Dropbox and Google Drive,... Read more
2.5GHz Mac mini, Apple refurbished, available...
The Apple Store has Apple Certified Refurbished 2.5GHz Mac minis available for $509, $90 off MSRP. Apple’s one-year warranty is included, and shipping is free. Read more
13-inch MacBook Pro, Apple refurbished, avail...
The Apple Store continues to offer Apple Certified Refurbished 13″ 2.5GHz MacBook Pros (4GB RAM/500GB HD) for $999 which is $200 off MSRP. Apple’s one-year warranty is included, and shipping is free. Read more
15-inch MacBook Pros (refurbished) available...
The Apple Store has Apple Certified Refurbished October 2013 15″ Retina MacBook Pros available starting at $1699, with models up to $400 off MSRP. Apple’s one-year warranty is standard, and shipping... Read more
$100 off cellular iPad minis plus free $20 gi...
Best Buy is discounting cellular iPad minis with Retina Displays by $100 on their online store for a limited time. They’ll also throw in a free $20 Best Buy gift card. Choose free shipping or free... Read more
Updated Price Trackers
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers: - 15″ MacBook Pros - 13″ MacBook... Read more
Apple refurbished MacBook Airs available for...
The Apple Store has Apple Certified Refurbished 2013 MacBook Airs available for up to $200 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Inc. Research Data Specialist - Appl...
…of Worldwide Market Research & Intelligence. The team is responsible for conducting Apple branded consumer market research. It is also responsible for analyzing data Read more
*Apple* Automotive Parts Department position...
Apple Automotive is one of the fastest growing dealer…and it shows. Consider making the switch to the Apple Automotive Group today! At Apple Automotive, we Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.