TweetFollow Us on Twitter

Function Resources
Volume Number:1
Issue Number:8
Column Tag:C Workshop

"Function Resources"

By Robert B. Denny, President, Alisa Systems, Inc., MacTutor Editorial Board

Several weeks ago, a group of us were having a "programmer's lament" discussion, centering around some of the seemingly needless holes in the Mac's development environment, in particular the Macintosh Development System (MDS) assembler/ linker package. I happen to know some of the private history behind it's implementation, and why it lacks librarian and selective linking support.

FIG 1 STATUS CODE IN TABLES

FIG 2 STATUS CODES NOT IN TABLES

It is alleged that the developer pleaded for those features but "Apple" refused to pay for them, citing lack of need. Ah, well, such is the plight of us old-timers who got spoiled 5 years ago on 16-bit systems which had 32K of memory!

The conversation meandered to more fertile ground. We discussed such things as sharing code between applications running under Switcher, a common error alert system, and how one could implement a "package-like" resource that could be loaded, locked and jumped into at run time ... without linking it in ... written in C (or at least most of it).

Function Resources

The result of that discussion is a way to implement what I'll call Function Resources. A function resource (FR) is simply a resource that you can read in, lock down and call as a C function. It is not linked into your program (as would be a segment).

Caveat

The techniques presented in this article are specific to the Apple MDS assembler/linker and to the Consulair Mac C compiler, which uses the Apple MDS system for assembly and linking. If you are using another development environment, the ideas presented here will still be of use, within the limits imposed by your development system.

I wrote my first function resource in assembler to get a feel for the way the MDS linker handles code that is assembled following a RESOURCE directive in the assembly source. That FR consists of a list of English language error messages for all of the system error codes, including those generated by the AppleTalk network drivers, and an alert display function.

The purpose was to provide a C-callable "package" that would put up a meaningful alert for any system error, and return an indication of whether the user pressed a "Quit" button or a "Resume" button. The resource contains all of the error messages, not the application. It isn't exactly in the spirit of the Mac interface, but it's very handy.

Techniques

The application program contains a very small transfer function which simply loads the FR resource, locks it down, then does a JSR to it's beginning. Upon return from the FR, the transfer function unlocks the resource, which has the "purgeable" attribute, then returns the FR's function result to the caller.

Since the FR is not linked with the application, any sharing of data must be via parameters supplied with the call. However, there is nothing to prevent you from defining a "work area" in your program, then passing a pointer to the work area as a parameter to the FR.

If you write the FR itself so that it contains no read-write static data, then a single copy of the FR can be called by multiple applications and/or desk accessories. Such an FR is said to be re-entrant. R/W static data is called impure data, while read-only static data is called pure data. For a routine to be re-entrant, it must not contain impure data. FR's can have read-write data if it is allocated on the stack at entry, like automatic variables in C.

With this as a background, lets cover the steps needed to create and use a Function Resource:

1. Write the FR in assembler or C. Begin it with the assembler's RESOURCE directive.

2. Assemble and link the FR. Use the linker's /RESOURCES option prior to the FR module name to create a resource file.

3. Optionally, combine the FR with other resources such as dialogs, alerts and window templates via an RMAKER run.

4. Write a transfer function which will load and lock the resource and transfer control to the code contained therein.

5. Link the transfer function into your program.

6. Call the transfer function to use the function resource.

Creating the Function Resource

The MDS assembler and linker provide the support needed do create the function as a resource without any additional hacking with utilities such as "Fedit" or "ResEdit". The assembly must start with a RESOURCE directive, declaring the remainder of the module as resources rather then normal code. For example:

RESOURCE 'PROC' 2000 'Foo 1.2' 32

This declares the module as a resource, with resource ID of 2000, resource type of "PROC" and with the "purgeable" attribute bit set. The type of PROC is an arbitrary choice on my part. For an FR written in Mac C, put the RESOURCE directive inside #asm/#endasm at the top of the source file. The optional resource name can be anything.

The MDS linker is smart enough to properly handle relocatable references in assembled resources. On the other hand, it cannot resolve inter-module references when linking resources. This means that your FR must be written to assemble as a single .REL file. You can have several source files, but they must be pulled together at assembly or compile time by text "include" directives. Given that you have your FR's .REL file, use the following linker commands to create the resource file, ready to use:

/OUTPUT MyFR.PROC
/Globals -0
/Type 'RSRC'
/Resources
MyFR

$

The /OUTPUT directive gives the resource file it's name. The suffix ".PROC" is an arbitrary choice on my part, the name may be anything reasonable. The reason for the /GLOBALS directive will be covered later, just be sure it's there and you'll be safe. The "-0" is required because the linker wants a negative value for the parameter. The file type "RSRC" indicates the file contains generic resources. The /RESOURCES directive indicates the beginning of resource data.

The result of assembling and linking as just described is a resource file which contains a purgeable resource of type PROC, with resource ID of 2000, which consists of code beginning at the first location in the resource.

A Simple Function Resource

The following is the MDS assembly code for the error alert FR. The tables are not complete but it should be clear how to complete them.

Mac C uses registers D0-D6 to pass arguments, returning the function result in either D0 or A0, depending on whether the function returns a scalar or a pointer, respectively. Remember that the FR is called via the transfer function and not directly from the C program.

The text displayed in the alert box is formatted using the Dialog Manager's ParamText() function. If the error code and it's English message are in the tables, the display is in English. If not, the toolbox "NumToStr" binary to decimal ASCII conversion "package" is called to convert the error code and it is displayed as "unknown error N". See the RMAKER control file which follows the assembly source for more on the use of ParamText().

Figure 1 shows what the error alert looks like for a known status code, Figure 2 shows its appearance for an unknown status code. The "user prefix" is the word "OOPS" followed by a carriage return ('\r').

;
;    ** PERROR **
;
;    Function resource for displaying system/AppleTalk
;    error alert.
;
    Include    MacTraps.D
    Include    SysErr.txt        ; System error codes
    Include    AtalkEqu.D    ; AppleTalk error codes
;
    RESOURCE 'PROC' 2000 'Perror 1.0' 32
;
; P-string format by default
    STRING_FORMAT 3
;
;   ENTRY:
;        D0.W  =   Error/status code
;        D1.L  ->  P-String to precede error message text
;        D2.W  =   Resource ID of alert box to use (normally
;                              2000)
;
BASE:
    Link                a6,#-32     ; Workspace for NumToStr
    Move.W      d2,d7          ; Save Alert ID across ParamText
    Move.L       d1,-(sp)      ; -> Caller's message for       
 
                                                   ; ParamText
    Lea          codes,a0       ; A0 -> base of code table
@10:  
    Move.W      (a0)+,d3    ; D3 = table code  (a0 -> offset)
    Beq.S      @20                ; (oops, end of table)
    Cmp.W    d0,d3           ; Matched?
    Beq.S     @30               ; (yes, display it)
    Addq      #2,a0               ; A0 -> next code
    Bra.S      @10
@20:
     ; Code not in table. Display "unknown error nn"
    Move.L      a6,a0          ; A0 -> temp buffer
    Ext.L          d0                  ; Sign-extend error code in D0
    Clr.W        -(sp)         ; Selector for NumToStr
    _Pack7                  ; A0 -> P-string of error code
     Pea       'Unknown error '         ; P2 = Our prefix 
                                                               ; (for 
ParamText)
     Move.L       a0,-(sp)                   ; P3 = Error code string 

                                                             ; (for ParamText)
    Bra.S          @40
; Code found in table, we have English message
@30:  
    Clr.L           d0                        ; Zero out D0.L
    Move.W     (a0),d0            ; D0 - offset to string
    Lea     Strings,a0              ; A0 -> base of strings
   Add.L         d0,a0                ; A0 -> our string
    Move.L      a0,-(sp)           ; P2 = Error message 
                                                       ; (for ParamText)
    Pea       L999                       ; P3 = nothing (for ParamText)
; Common code to display the alert
@40:  
      Pea      L999                     ; P4 = nothing (for ParamText)
     _ParamText           ; Set the text (wipes d2!)
     Clr.W-(sp)                      ; For function result
     Move.W d7,-(sp)        ; P1 = alert ID
     Clr.L-(sp)                       ; Nil ProcPtr
     _StopAlert                 ; Put up the alert
     Move.W      (sp)+,d0    ; Return alert function result
     Unlk     a6                          ; Clean up
      Rts                                     ; Return to application's 
transfer                                                ; function
;
; The following table contains ordered pairs consisting of ; a system 
error code followed by the byte offset into a 
; list of strings of the error message for that error code.
;
CODES:
dc.w       controlErr,      0
dc.w       statusErr,             (L2 - STRINGS)
dc.w       readErr,                 (L3 - STRINGS)
dc.w      writErr,                     (L4 - STRINGS)
dc.w      badUnitErr,           (L5 - STRINGS)
dc.w      unitEmptyErr,      (L6 - STRINGS)
dc.w     openErr,                 (L7 - STRINGS)
dc.w     closErr,                    (L8 - STRINGS)
dc.w     dRemovErr,         (L9 - STRINGS)
dc.w     dInstErr,                 (L10 - STRINGS)
dc.w     abortErr,                (L11 - STRINGS)
dc.w     notOpenErr,       (L12 - STRINGS)
additional codes & message offsets here
dc.w      0                            ; End of table
;
; This table contains the error messages, addressed by 
; the offsets contained in the previous table.
;
STRINGS:
     L1:         dc.b           'I/O Control failed'
     L2:         dc.b           'I/O Status failed'
     L3:         dc.b          'I/O Read failed'
    L4:        dc.b          'I/O Write failed'
    L5:       dc.b           'Bad unit number'
    L6:       dc.b           'Unit is empty'
    L7:       dc.b           'I/O Open failed'
    L8:       dc.b          'I/O Close failed'
    L9:      dc.b          'Cannot remove open driver'
    L10:   dc.b          'Driver not found'
    L11:   dc.b          'I/O aborted by KillIO'
    L12:   dc.b          'I/O to unopened driver'
additional messages here
L999:     dc.b     0,0        ; Addressable empty string 
      END

Assuming the above was assembled to a file called PERROR.REL, the next step is to link the REL file into a 0-based image in resource format. The linker control file is shown below:

 /OUTPUT Perror.PROC
 /Globals -0
 /Resources
 Perror

 $

The RMAKER control file for the error dialog FR combines the PROC resource with an alert box and item list. Note the statText item with the meta-characters of the form "^n" are used with the Dialog Manager ParamText() call to set up the alert for display.

 Perror
 RSRC????
 * Merge in the function resource
 Include Perror.PROC

 Type ALRT
     ,2000
 40 96 138 416
 2000
 5555
 
 Type DITL
     ,2000
 3

 Button
 68 60 88 130
 Quit

 Button
 68 190 88 260
 Resume

 StaticText Disabled
 7 72 64 310
 ^0^1^2^3

The Transfer Function

The transfer function is linked with the application. It provides the C-callable generic service needed to load the resource, lock it down, call it, unlock the resource and return. The following transfer function is written to be "generic", that is, independent of any particular FR, so that it may be used to call various FR's from Mac C. The first parameter to the transfer function is the resource ID of the PROC resource containing the desired FR. For the error alert FR, this is 2000. The remaining parameters are passed directly to the FR after removing the first parameter (the PROC ID). You supply the code to handle the case where the PROC resource can't be loaded.

; Inputs:
;        D0.W  = Resource ID of PROC
;       Other D-regs contain args for proc
; Outputs:
;       Returns FR's D0
;
; WARNING:  Supports a maximum of 7 parameters 
;                            following PROC ID.
;
         Include MacTraps.D
        XDEFDoProc
DoProc:
       Movem.L     a1-a5,-(sp); Just in case ...
      Clr.L             -(sp)                     ; Gets handle to FR
      Move.L       #'PROC',-(sp)   ; Resource type of FR
      Move.W      d0,-(sp)                ; Resource ID
     _GetResource                    ; Load FR resource
     Move.L         (sp)+,d0             ; D0 ->-> FR? (need test)
    Beq                @10                      ; (didn't get it!)
     Move.L       d0,a0                    ; A0 ->-> FR
     MoveM.L    d1-d6,-(sp)       ; Save d-parameters
     MoveM.L    (sp)+,d0-d5      ; Restore parameters 
                                                              ; shifted 
down in reg's
    Move.L       4(sp),d6              ; Get next parameter
    Move.L      a0,-(sp)                ; Save handle to FR
    Bset.B     #7,(a0)                   ; Lock it down
    Move.L    (a0),a0                   ; A0 -> FR's entry
   Jsr               (a0)                          ; Call perror
   Move.L     (sp)+,a0               ; A0 ->-> FR
  Bclr.B       #7,(a0)                    ; Unlock the FR
   MoveM.L    (sp)+,a1-a5      ; Restore a-regs
   Rts                                                  ; Return FR's 
D0
@10:
    Handle resource load error here 
     Rts
    END

Using the "Perror" Function Resource

To call the error alert FR from a Mac C program, issue a function call of the following form:

          DoProc(2000, stat, prefix, 2000);

where "stat" is the 16-bit system status code for which to display the alert, "prefix" is a P-string containing text to be displayed in the alert prior to the English language error message, and the last 2000 is the resource number of the alert to use for the display. See the RMAKER file above for the description of the alert-2000 box used.

One convenient method of handling errors in Mac C programs is to use the "signal" mechanism to break out of the normal control flow and go to an error handler that invokes the Perror alert FR to display the error in an alert. For example:

   if(stat = CatchSignal())
         {
          DoProc(2000,stat,"\005OOPS\r",2000);
        Whatever other recovery & cleanup
         }

Writing Function Resources in C

The "Perror" function resource is a simple example, written in assembly language to make the concept clear. Typically, however, you'll want to implement function resources in C, with a minimum of assembly "glue".

This is straightforward if you need only have automatic variables. Things get stickier if you want to have multiple functions in the FR, with data having module-wide scope. An additional com- plication arises if you wish to access the calling application's QuickDraw varia- bles (to set patterns, for example).

Mac C normally uses register A5 as a base register for accessing application globals. In fact, this is a Macintosh programming convention; the toolbox expects A5 to point to the boundary between application parameters and application globals.

Static data is declared by the compiler using assembler "DS" directives. The linker collects these and "assigns" stor- age by computing a negative offset for use with a base register for each global item. Normally, this base register is A5. For example:

        static int foo;
                              ...
                             foo = func();

expands to (approximately):

        FOO:        DS.L           1
                    JSR            FUNC
                    MOVE.L         D0,FOO(A5)

Most toolbox calls require A5 to point to the "magic place". Therefore, an FR written in C should use a base register other than A5, usually A4. Fortunately, the Mac C compiler has an option to specify what A-register it is to use as the static data base register. This feature was meant for use by desk accessories written in C:

    #Options    R=4        /* Use A4 for statics */

Now for the final touch. If we are to keep the FR re-entrant, then it cannot contain read/write static data. But suppose we want to have read/write variables with module-wide scope? Here's how.

Declare the "static" variables as usual. Then reserve a chunk of space on the stack as a big automatic array belonging to the "first" FR function, the one which is called by the transfer function. Then, immediately on entry to the FR, bash A4 to point to the last cell in the automatic array. Thus, the automatic array serves as the "globals" area for the FR, based on A4, and the various functions in the FR may access the variables as if they were statically declared in the outermost (module) scope. And the FR is still re-entrant because the space is allocated at run-time on the caller's stack.

When the linker computes the negative offsets to assign to the static variables, it assumes that A5 is being used for the base register and that the statics are the application's globals. This being the case, it automatically reserves space for the QuickDraw globals. This causes the "first" static to have an offset of -200 hex.

This offset can be supressed by including the linker directive "/GLOBALS -0" in the control file used to link the FR. That's "minus zero" ... needed because of a bug/feature in the linker. It wants to see a negative value with the /GLOBALS switch and won't accept zero. But minus zero is OK. The linker command file shown above contains this directive.

There is one VERY important caveat here. Static initializers will not work. Therefore, the FR must manually initialize the static data, Pascal style upon FR entry (after bashing A4, of course).

Perhaps you're saying to yourself, "Is all of this worth it?" Consider the uses for re-entrant run-time loadable functions.

"Packages" similar to the Standard File package can be implemented using this technique. The second FR I wrote uses the lookup dialog described last month to allow selection of named objects on an AppleTalk network.

A family of related applications could share functions contained in a single resource file. Remember that the code in the FR is not linked with the application, therefore reducing the disk space used by the program file.

Common code could be shared between applications running under the Switcher by loading the FR into the system heap. This would save both memory and disk space.

A Template Function Resource in C

Next we'll look at a template for writing FR's in C. The techniques for linking and transfer are the same as those already presented. The DoProc() transfer function will work just fine with the C language FR

.

#asm
    RESOURCE 'PROC' 1234 'Templ_1.0'
    Include        MACTRAPS.D
#endasm

/*
  * Declare pseudo-static variables here, then 
  * define COM_SIZE equal to the total number
  * of bytes needed for the pseudo-statics.
  */
static int a;
static short b;
struct QDVars *QD; /* Our QD pointer */ 
#define COM_SIZE nn

/*
  * Entry is here, at the beginning of the module
  */
Func(p1, p2)
int p1; /* Declare these as needed */
int p2;
 {
 char CommonVars[COM_SIZE]; /* Pseudo statics */
 Ptr appl_port;  /* Saves caller's port */
 /*
  * Preliminary set-up stuff. 
  */
 #GetPort(&appl_port);  /* Save caller's port */
 LocInit(&(CommonVars[COM_SIZE-1]));
 
 The rest of the main function goes here
 
 #SetPort(appl_port);
 return(result);
   }

Other functions may be coded here. References to the "static" variables are normal.

#asm
;
; Initialization and A4 hacking routines
;
save_a4:     dc.l0               ; A4 pointer is kept here
grafSize       equ    $CE       ; Size of QD's variables

LocInit:
    Lea       save_a4,a0           ; Dumb 68000 designers!
    Move.L         d0,(a0)          ; Initialize our "statics" base
    Move.L        d0,a4              ; A4 -> our pseudo-statics
    Move.L        a5,d0             ; D0 -> QD's magic cell
    Sub.L        #grafSize,d0     ; D0 -> QDVars
    Move.L     d0,QD(a4)          ; Save it in our common vars
    Rts
;
; This routine is used to re-establish our A4 context for
; functions which are called back from the toolbox, such
; action routines for TrackControl(), and "filter procs"
; for ModalDialog().
;
GetA4:
    Move.L        save_a4,A4              ; PC-relative, eh?
    Rts

Notice the convenience of PC-relative addressing such as that used for acces- sing "save_a4" above. When I first started working with the 68000, I nearly lost my mind until I discovered that PC-relative is illegal as a destination addressing mode! My reference manual neglected to mention that little fact.

This wraps up our discussion of function resources. It is my hope that, despite the orientation to Apple's MDS and to Mac C, the information given here will be of use to C programmers in general. I'd be interested to hear from readers on how they implemented these ideas with their C systems.

Update Event Handling Revisited

Last month, the C Workshop presented a large example source listing of a callable function that implements a general purpose "selector dialog" similar to that used by the Standard File package. Along with bringing together the ideas discussed over several previous issues, the example showed how to use the "filter procedure" hook provided by the ModalDialog() service of the Dialog Manager.

The update event handler in the filter procedure is incorrect. The "test program" I used to check out the selector dialog function did not uncover a glaring error. Please refer to last month's article for background information.

It turns out that if you do anything at all with update events in your filter procedure, you must handle all update activities. The update procedure must start with a call to BeginUpdate(), which copies the update region into the "vis" region and sets the update region to NIL.

I tried to be tricky and skip the call to BeginUpdate(), draw only the "special" items in the dialog box, then return FALSE to ModalDialog, signalling it that it should handle the update event. Well, under the test program, it worked fine, but in a real application, I discovered that the lack of a call to BeginUpdate() caused my "special stuff" to get redrawn lots of unnecessary times.

So I put in the call to BeginUpdate(), figuring that if I didn't call EndUpdate() and I returned FALSE, ModalDialog() would still finish the job. I should have known...

When ModalDialog() gets control back from the filter procedure and it sees that the event is an update event, it immediately calls BeginUpdate(). This sets the visRgn to the update region, which was set to NIL by my call to BeginUpdate() in the filter procedure.

The moral of the story is that filter procedures which call BeginUpdate() must do all required updates to the dialog and then return TRUE to ModalDialog(). This indicates that the event was completely taken care of by the filter proc.

Rather than show the corrected code for the update portion of the filter procedure, I'll leave the bug fixes as an exercise. Some hints: Use TextBox() to draw statText items. A single call to DrawControls() will update all buttons and scrollers in the dialog. Call TEUpdate() for each EditText item in the dialog.

Feedback Wanted

The articles presented in the C Workshop over the past few months have been highly technical in content. I would like to know if there are a significant number of readers for whom the article content has been too advanced. Until next month ...

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

beaTunes 4.6.14 - Organize your music co...
beaTunes is a full-featured music player and organizational tool for music collections. How well organized is your music library? Are your artists always spelled the same way? Any R.E.M. vs REM?... Read more
iDefrag 5.1.8 - Disk defragmentation and...
iDefrag helps defragment and optimize your disk for improved performance. Features include: Supports HFS and HFS+ (Mac OS Extended). Supports case sensitive and journaled filesystems. Supports... Read more
Day One 2.1.8 - Maintain a daily journal...
Day One is the easiest and best-looking way to use a journal / diary / text-logging application for the Mac. Day One is well designed and extremely focused to encourage you to write more through... Read more
Spotify 1.0.53.758. - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
VirtualBox 5.1.20 - x86 virtualization s...
VirtualBox is a family of powerful x86 virtualization products for enterprise as well as home use. Not only is VirtualBox an extremely feature rich, high performance product for enterprise customers... Read more
Arq 5.7.9 - Online backup to Google Driv...
Arq is super-easy online backup for Mac and Windows computers. Back up to your own cloud account (Amazon Cloud Drive, Google Drive, Dropbox, OneDrive, Google Cloud Storage, any S3-compatible server... Read more
Vienna 3.1.10 :d05d7a5d: - RSS and Atom...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
WhiteCap 6.7 - Visual plug-in for iTunes...
WhiteCap is a sleek and sophisticated music visualizer and screensaver that features futuristic, wireframe mesh visuals with dynamic backgrounds and colors. WhiteCap contains thousands of visual... Read more
Dropbox 24.4.16 - Cloud backup and synch...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keep them up-to-date between systems... Read more
Amazon Chime 4.2.5645 - Amazon-based com...
Amazon Chime is a communications service that transforms online meetings with a secure, easy-to-use application that you can trust. Amazon Chime works seamlessly across your devices so that you can... Read more

Latest Forum Discussions

See All

ChordFlow (Music)
ChordFlow 1.0.0 Device: iOS Universal Category: Music Price: $6.99, Version: 1.0.0 (iTunes) Description: ChordFlow is a chord sequencer with a unique 4-track polyphonic arpeggiator, extensive chord library, MIDI out and Ableton Link... | Read more »
The Walking Dead: A New Frontier is out...
The newest season of Telltale Games'The Walking Dead is well underway. After the release of the third episode, "Above the Law" about a month ago, episode four, "Thicker Than Water" is hot and ready for more zombies and gut-wrenching emotional... | Read more »
Best games we played this week
Another week, another new wave of mobile games do dive into. We've dug through the list of apps that came out this week to tell you which apps are worth your sweet time. And while there weren't too many games this week, there were some big ones.... | Read more »
Vignettes (Games)
Vignettes 1.0.1 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.1 (iTunes) Description: Vignettes is a casual but unique exploration game without text or characters, where objects shapeshift as you spin them around... | Read more »
Get Me Outta Here is an 80s retro shoote...
Are you ready to fight some aliens? Because Crescent Moon Games has released the retro shooter Get Me Outta Here on iOS devices today. [Read more] | Read more »
Get a bunch of Apple productivity apps f...
If you're an Apple Mac owner, you're probably aware of the host of Apple productivity apps the company includes in all new Mac purchases. Apps like iMovie, Keynote, and of course, GarageBand. While you used to be able to also buy these apps... | Read more »
Terra Mystica (Games)
Terra Mystica 1.03 Device: iOS Universal Category: Games Price: $9.99, Version: 1.03 (iTunes) Description: Short Summary:≈≈≈≈≈≈≈≈≈≈≈≈≈ | Read more »
Ms. Spell (Games)
Ms. Spell 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Cast spells and battle monsters in this turn based game, that has you delving into ever the changing Dreadwood to retrieve the lost... | Read more »
Invert - A Minimal Puzzle Game (Games)
Invert - A Minimal Puzzle Game 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: Invert is a minimalist puzzle game for fans of brain teasers, logic puzzles, and Rubik's Cube fiddlers. | Read more »
Evergrow: Paper Forest (Games)
Evergrow: Paper Forest 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: Solve puzzles in the handcrafted forests of Evergrow through the eyes of an imaginative child and his parents. Discover... | Read more »

Price Scanner via MacPrices.net

15-inch Touch Bar MacBook Pros, Apple refurbi...
Apple is offering Certified Refurbished 2016 15″ Touch Bar MacBook Pros for $360 to $420 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2.6GHz... Read more
13-inch MacBook Airs on sale for up to $150 o...
Overstock.com has 13″ MacBook Airs on sale for up to $150 off MSRP including free shipping: - 13″ 1.6GHz/128GB MacBook Air (sku MMGF2LL/A): $869.99 $130 off MSRP - 13″ 1.6GHz/256GB MacBook Air (sku... Read more
15-inch Touch Bar MacBook Pros on sale for $1...
B&H Photo has the new 2016 15″ Apple Touch Bar MacBook Pros in stock today and on sale for up to $200 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.7GHz Touch Bar... Read more
15-inch 2.7GHz Touch Bar MacBook Pros on sale...
Amazon has 2016 15″ 2.7GHz Apple Touch Bar MacBook Pros in stock today and on sale for $150-$200 off MSRP. Shipping is free: - 15″ 2.7GHz Touch Bar MacBook Pro Space Gray (sku MLH42LL/A): $2599 $200... Read more
Apple now offering Certified Refurbished 13-i...
Apple is now offering Certified Refurbished 2016 13″ Touch Bar MacBook Pros for $270-$300 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.9GHz... Read more
MyGiHealth Digestive Symptom Tracker Version...
My Total Health, Inc. has announced the release of MyGiHealth 2.1, an important update to their digestive symptom tracker developed exclusively for iPhone, iPad and iPod touch devices. MyGiHealth is... Read more
Galaxy S8 Materials Costs Highest by Far Comp...
The new Samsung Galaxy S8 equipped with 64 gigabytes (GB) of NAND flash memory carries a bill of materials (BOM) cost that comes out to US$301.60, much higher than for previous versions of the... Read more
iCarMode 4.0 Car Dashboard App For iOS Integr...
Indie developer Diego Resnik has announced the release of iCarMode 4.0, an update to his productivity app developed for iOS devices. iCarMode has positioned itself as a true car dashboard app,... Read more
How to save $150+ on Apple’s 13-inch 2.0GHz n...
Apple Authorized Reseller B&H Photo has non-Touch Bar 13″ 2.0GHz MacBook Pros on sale for $150 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.0GHz... Read more
15-inch 2.2GHz Retina MacBook Pro, Apple refu...
Apple has Certified Refurbished 2015 15″ 2.2GHz Retina MacBook Pros available for $1699. That’s $300 off MSRP, and it’s the lowest price available for a 15″ MacBook Pro. An Apple one-year warranty is... Read more

Jobs Board

Music Marketing Lead, iTunes & *Apple*...
# Music Marketing Lead, iTunes & Apple Music Job Number: 56868140 Culver City, California, United States Posted: Apr. 17, 2017 Weekly Hours: 40.00 **Job Summary** Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
*Apple* Mac Computer Technician - GeekHampto...
…complex computer issues over the phone and in person? GeekHampton, Long Island's Apple Premium Service Provider, is looking for you! Come work with our crew Read more
*Apple* Mobile Master - Best Buy (United Sta...
**493714BR** **Job Title:** Apple Mobile Master **Location Number:** 001024-Weatherford-Store **Job Description:** **What does a Best Buy Apple Mobile Master Read more
Best Buy *Apple* Computing Master - Best Bu...
**496963BR** **Job Title:** Best Buy Apple Computing Master **Location Number:** 001061-Marina-Store **Job Description:** **What does a Best Buy Apple Computing Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.