TweetFollow Us on Twitter

Hypercard, Forth
Volume Number:3
Issue Number:12
Column Tag:Forth Forum

Extending HyperCard from Forth

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

HyperCard external functions and commands

One of the major events in the last couple of months on the Macintosh scene has been the introduction of Hypercard (You’ve read an article on it in the last MacTutor). This month’s column will deal with interfacing Forth code to Hypercard; we shall see how an external command can be implemented in Mach2.

The syntax of HyperTalk commands

A ‘command’ in the Hypertalk language is a word followed by a number of parameters, separated by spaces. Example:

dial it with modem 

will execute the dial command and pass the parameter string: “it with modem”. Dial will then know how to deal with that string; in this case, get the ASCII string stored in it, interpret it as a phone number to be dialed and issue the appropriate Hayes-compatible modem command.

Similarly, if we define our own command, munch, we may send parameters to it, “three bars of milk chocolate”, and the corresponding Hypertalk line will read:

 munch three bars of milk chocolate

The concept, as you see, is very simple. All we now need to know is how such an external command is implemented, i.e., how the Hypertalk system finds the command and how the parameters are passed.

How an external command is found

An external command is stored as a resource of type XCMD. The resource name is the name of the external command as called through Hypertalk. I have not peeked inside Hypertalk’s guts yet, but it seems to me very probable that a GetNamedResource is used to get an XCMD that is to be executed.

The resource is a piece of executable 68000 code, just like a WDEF or MDEF would be. When the XCMD is executed, the system jumps to the beginning of the resource. There, on top of the stack a pointer to a parameter block is passed, which looks like the following (in Pascal notation):

XCmdPtr = ^XCmdBlock;
XCmdBlock = RECORD
 paramCount:  INTEGER;    { the number of arguments }
 params:  ARRAY[1..16] OF Handle;   { the arguments }
 returnValue: Handle;   { the result of this XCMD }
 passFlag:  BOOLEAN;    { pass the message on? }
 
 entryPoint:   ProcPtr;   { call back to HyperCard }
 request:   INTEGER;   { what you want to do }
 result:  INTEGER; { the answer it gives }
 inArgs:   ARRAY[1..8] OF LongInt; 
 { args XCMD sends HyperCard }
 outArgs:   ARRAY[1..4] OF LongInt;
 { answer HyperCard sends back }
 END;

A Hypertalk function, in contrast to a command, will always return a value and its syntax is different from that of a command. For example:

 the exp of number

will return the exponential of number. A Hypertalk line like that has no meaning, so we’ll write, for instance:

 put the exp of number into it

and it will contain the result. An alternative syntax would be

 put exp(number) into it.

An external function is stored as a resource of type XFCN, with the resource name being the name of the function, like for the XCMDs; again, it is called by jumping to the start of the resource.

The parsing of the line following the command or function is automatically done by Hypercard; the arguments (i.e., the words following the command) are passed to the XCMD in form of handles to zero-terminated strings. The XCMD that we write can do whatever necessary with them. If you want, you may return a result by storing a handle to it in returnValue. (All data values going to and from HyperTalk are zero-terminated ASCII strings). A result that has been stored in this field can be accessed by the user in the variable ‘the result’ after executing a command. In a function, returnValue is always expected to contain something.

passFlag is used to determine whether the command has been executed successfully. If this flag is false, the XCMD or XFCN has handled the message and the script resumes execution. If passFlag is true, HyperCard searches the remaining inheritance chain for another handler or XCMD with the same name. This is just like the ‘pass’ control structure in a script.

You may also call HyperCard from within your code. This is what the second part of the parameter block is used for. To call HyperCard commands, you put your arguments into inArgs, the command code into request, and call the routine pointed to by entryPoint. HyperCard returns the values you requested in outArgs and a result code in result.

The command and result codes are part of a Pascal definition in the Hypercard developer package materials. They are given in Listing 1, together with a description of what the commands do.

Mach2 implementation

The example program (Listing 1) will create an XCMD that inverts the screen a given number of times, like the built-in flash command. The difference is that the result will return Done when the command was executed successfully and Error when a negative number has been given as a parameter (incorrect). Its syntax will be:

 flashy <number> 

where <number> is a string that is interpreted as a decimal number.

Now look at the example. You’ll recognize the glue code that is used in the XCMD definition to setup the Forth stack and transfer the parameters from the Pascal to the Forth stack; I defined in the form of a MACH macro so the definition of the actual glue routine becomes very simple. The XCMD is embedded between the labels flashy.start and flashy.end. callJSR is a redefinition of the kernel word execute, which does a JSR to an address given on the stack. This word is needed in the two following words, ZeroToPas and StrToNum, which call Hypercard routines through the entryPoint parameter in the Hypercard parameter block. ZeroToPas converts a C string (zero-terminated) to a Pascal string (count byte followed by characters). StrToNum takes a Pascal string and interprets it as a number. Both routines also need to be given the address of the Hypercard parameter block containing Hypercard’s entry point.

For the other Hypercard functions that may be called from outside, see their request codes and Pascal definitions given at the beginning of the listing. With the two routines given here as an example, you can easily figure out how to call the remaining ones. I also strongly recommend reading the documentation and Pascal or C example files that come with the Hypercard developer’s package, which you may obtain through APDA (DDA in France, see below).

The actual XCMD, flashy, first gets the first parameter, which is a handle to a string, then converts this string to a number. If the number is negative, it will return “Error” as a return string, if it is positive, it flashes the screen for this number of times and returns “Done”.

The last part of the listing will simply create a file containing the new resource.

Calling the XCMD from Hypercard

When you’ve created the file “xcmd.res”, enter ResEdit and install the XCMD resource in your home stack (or any other stack, for that matter). Then you can call Hypercard and, for example, create a button which has the following script:

on mouseup
 flashy 10
 put the result
end mouseup

Pushing this button, then, should flash the screen ten times and display “Done” in the message window. If you change the number to -10, no flashing should happen and “Error” should be displayed in the message box.

You may try to change the resource type to XFCN; this will change the syntax to (for example)

on mouseup
 put flashy(10)
end mouseup

but should not otherwise affect the behavior of the command.

Feedback Dept.

Mac Applications in Forth?

Paul Thomas

Danville, CA

Q. Can Forth be used on the Mac to do software stuff: i.e. games, engineering applications, etc.? I just picked up “Mach2” last month and I’ve been reading Leo Brodie’s “Starting Forth”. I was studying the Mac Toolbox using Turbo Pascal, but I went over to Palo Alto Shipping and visited with Rick Miley. I’m an engineering student (mechanical engineering) and he got me excited about Forth. Most of your articles tend to deal with hardware, data transfer, low level stuff. I realize that Forth is great for that type of work, but I’d like to program the Mac in Forth. I haven’t been able to find very many example programs in Forth. I guess I could try my hand at translating from Pascal and C to Forth. It’s going to be tough though since I’m new at Forth - and it really IS different from Pascal or C. Are most of your readers interested in low level Forth work? Do you know of anyone who is programming applications in Forth? Most people that I know that program the Mac work in Pascal or C.

I do enjoy your articles [Thanks! JL]. I’d love to send you a message over CalvaCom, but it’s a bit too expensive for me. I can’t even afford Compuserve. Do you have an address on GEnie?

A. Can Forth be used to do software stuff: why, yes! don’t we do software stuff? In fact, I see what you mean and can only say that one full-fledged application in each column would probably be too much. If you’ve seen the space taken up by long articles in MacTutor, you’ll understand that we cannot print a regular column that long each time. I might, however, think of working out a longer project and distribute that over several columns. The columns that I write, of course, also reflect to some extent how I use the Mac for my other work. Let me take this opportunity to repeat once again: This magazine lives through YOUR contributions, and nothing would I appreciate more than article submissions for the Forth column.

Most people I know program the Mac in Pascal or C, too. Should I say unfortunately? The two Forth systems that I use, Mach2 and MacForth, are by far the fastest development systems that I know of. Having to write this month’s example in Pascal without the glue routines available would have been a nightmare. With Mach2, the whole cycle of changing a line, recompiling, rewriting the resource file, calling ResEdit, installing the resource, calling Hypercard and testing the XCMD takes about one minute. If I hadn’t been that lazy, I’d have written a small routine that installs the XCMD in my Home stack directly, gaining another 30 seconds.

[I can be reached on GEnie now. Mail address: J.Langowski. I wish you all happy holidays, and happy threading.]

Listing 1: A screen invert Hypercard external command in Mach2
( *** Hypercard external commands. J.L. October 1987 *** )

ONLY FORTH ALSO ASSEMBLER ALSO MAC

4ascii XFCN CONSTANT “xfcn
4ascii XCMD CONSTANT “xcmd

$9DE CONSTANT WMgrPort

\ structure of a Hypercard parameter block

0  CONSTANT paramCount    \ INTEGER; the number of arguments
2  CONSTANT params \ ARRAY[1..16] OF Handle; the arguments
66 CONSTANT returnValue \ Handle; the result of this XCMD
70 CONSTANT passFlag \ BOOLEAN; pass the message on?
72 CONSTANT entryPoint  \ ProcPtr; call back to HyperCard
76 CONSTANT request\ INTEGER; what you want to do
78 CONSTANT result \ INTEGER; the answer it gives
80 CONSTANT inArgs \ ARRAY[1..8] OF LongInt;
 \ args XCMD sends HyperCard
112 CONSTANT outArgs \ ARRAY[1..4] OF LongInt;
 \ answer HyperCard sends back

\ result codes
0 CONSTANT xresSucc
1 CONSTANT xresFail
2 CONSTANT xresNotImp
   
\ request codes
1 CONSTANT  xreqSendCardMessage
2 CONSTANT  xreqEvalExpr
3 CONSTANT  xreqStringLength
4 CONSTANT  xreqStringMatch
5 CONSTANT  xreqSendHCMessage
6 CONSTANT  xreqZeroBytes
7 CONSTANT  xreqPasToZero
8 CONSTANT  xreqZeroToPas
9 CONSTANT  xreqStrToLong
10 CONSTANT xreqStrToNum
11 CONSTANT xreqStrToBool
12 CONSTANT xreqStrToExt
13 CONSTANT xreqLongToStr
14 CONSTANT xreqNumToStr
15 CONSTANT xreqNumToHex
16 CONSTANT xreqBoolToStr
17 CONSTANT xreqExtToStr
18 CONSTANT xreqGetGlobal
19 CONSTANT xreqSetGlobal
20 CONSTANT xreqGetFieldByName
21 CONSTANT xreqGetFieldByNum
22 CONSTANT xreqGetFieldByID
23 CONSTANT xreqSetFieldByName
24 CONSTANT xreqSetFieldByNum
25 CONSTANT xreqSetFieldByID
26 CONSTANT xreqStringEqual
27 CONSTANT xreqReturnToPas
28 CONSTANT xreqScanToReturn
39 CONSTANT xreqScanToZero \ was supposed to be 29. Oops!
 
( **** Pascal definitions for the Hypercard routines follow:
   
PROCEDURE SendCardMessage(msg: Str255);
{  Send a HyperCard message (a command with arguments) to the current 
card. }
 
FUNCTION EvalExpr(expr: Str255): Handle;
{  Evaluate a HyperCard expression and return the answer.  The answer 
is a handle to a zero-terminated string. }
 
FUNCTION StringLength(strPtr: Ptr): LongInt;
{  Count the characters from where strPtr points until the next zero 
byte. Does not count the zero itself.  strPtr must be a zero-terminated 
string.  }
 
FUNCTION StringMatch(pattern: Str255; target: Ptr): Ptr;
{ Perform case-insensitive match looking for pattern anywhere in target, 
returning a pointer to first character of the first match, in target 
or NIL if no match found.  pattern is a Pascal string, and target is 
a zero-terminated string. }
 
PROCEDURE ZeroBytes(dstPtr: Ptr; longCount: LongInt);
{  Write zeros into memory starting at destPtr and going for longCount 
number of bytes. }
 
FUNCTION PasToZero(str: Str255): Handle;
{  Convert a Pascal string to a zero-terminated string.  Returns a handle 
to a new zero-terminated string.  The caller must dispose the handle. 
You’ll need to do this for any result or argument you send from your 
XCMD to HyperTalk. }
 
PROCEDURE ZeroToPas(zeroStr: Ptr; VAR pasStr: Str255);
{  Fill the Pascal string with the contents of the zero-terminated string. 
 You create the Pascal string and pass it in as a VAR parameter.  Useful 
for converting the arguments of any XCMD to Pascal strings.}
 
FUNCTION StrToLong(str: Str31): LongInt;
{  Convert a string of ASCII decimal digits to an unsigned long integer. 
}
 
FUNCTION StrToNum(str: Str31): LongInt;
{  Convert a string of ASCII decimal digits to a signed long integer. 
Negative sign is allowed.  }
 
FUNCTION StrToBool(str: Str31): BOOLEAN;
{ Convert the Pascal strings ‘true’ and ‘false’ to booleans. }
 
FUNCTION StrToExt(str: Str31): Extended;
{  Convert a string of ASCII decimal digits to an extended long integer. 
}
 
FUNCTION LongToStr(posNum: LongInt): Str31;
{  Convert an unsigned long integer to a Pascal string.  }
 
FUNCTION NumToStr(num: LongInt): Str31;
{  Convert a signed long integer to a Pascal string.  }
 
FUNCTION NumToHex(num: LongInt; nDigits: INTEGER): Str31;
{  Convert an unsigned long integer to a hexadecimal number and put it 
into a Pascal string.  }
 
FUNCTION BoolToStr(bool: BOOLEAN): Str31;
{  Convert a boolean to ‘true’ or ‘false’.  }
FUNCTION ExtToStr(num: Extended): Str31;
{ Convert an extended long integer to decimal digits in a string.}
 
FUNCTION GetGlobal(globName: Str255): Handle;
{  Return a handle to a zero-terminated string containing the value of 
the specified HyperTalk global variable.  }
 
PROCEDURE SetGlobal(globName: Str255; globValue: Handle);
{  Set the value of the specified HyperTalk global variable to be the 
zero-terminated string in globValue.  The contents of the Handle are 
copied, so you must still dispose it afterwards.  }
 
FUNCTION GetFieldByName(cardFieldFlag: BOOLEAN; fieldName: Str255): Handle;
{  Return a handle to a zero-terminated string containing the value of 
field fieldName on the current card. You must dispose the handle.  }
 
FUNCTION GetFieldByNum(cardFieldFlag: BOOLEAN; fieldNum: INTEGER): Handle;
{  Return a handle to a zero-terminated string containing the value of 
field fieldNum on the current card.  You must dispose the handle.  }
 
FUNCTION GetFieldByID(cardFieldFlag: BOOLEAN; fieldID: INTEGER): Handle;
{  Return a handle to a zero-terminated string containing the value of 
the field whose ID is fieldID.  You must dispose the handle.  }
 
PROCEDURE SetFieldByName(cardFieldFlag: BOOLEAN; fieldName: Str255; fieldVal: 
Handle);
{  Set the value of field fieldName to be the zero-terminated string 
in fieldVal.  The contents of the Handle are copied, so you must still 
dispose it afterwards.  }
 
PROCEDURE SetFieldByNum(cardFieldFlag: BOOLEAN; fieldNum: INTEGER; fieldVal: 
Handle);
{  Set the value of field fieldNum to be the zero-terminated string in 
fieldVal.  The contents of the Handle are copied, so you must still dispose 
it afterwards.  }
 
PROCEDURE SetFieldByID(cardFieldFlag: BOOLEAN; fieldID: INTEGER; fieldVal: 
Handle);
{  Set the value of the field whose ID is fieldID to be the zero-terminated 
string in fieldVal.  The contents of the Handle are copied, so you must 
still dispose it afterwards.  }
 
FUNCTION StringEqual(str1,str2: Str255): BOOLEAN;
{  Return true if the two strings have the same characters. Case insensitive 
compare of the strings.  }
 
PROCEDURE ReturnToPas(zeroStr: Ptr; VAR pasStr: Str255);
{  zeroStr points into a zero-terminated string.  Collect the characters 
from there to the next carriage Return and return them in the Pascal 
string pasStr.  If a Return is not found, collect chars until the end 
of the string. }
 
PROCEDURE ScanToReturn(VAR scanPtr: Ptr);
{  Move the pointer scanPtr along a zero-terminated string until it points 
at a Return character or a zero byte.  }
 
PROCEDURE ScanToZero(VAR scanPtr: Ptr);
{  Move the pointer scanPtr along a zero-terminated string until it points 
at a zero byte.  }
 
**** End of Pascal definitions )

\ **** Hypercard glue macros

CODE HC.prelude
 LINK A6,#-512   ( 512 bytes of local Forth stack )
 MOVEM.L A0-A5/D0-D7,-(A7)( save registers )
 MOVE.L A6,A3    ( setup local loop return stack )
 SUBA.L #256,A3  ( in the low 256 local stack bytes )
 MOVE.L 8(A6),D0 ( pointer to parameter block )
 MOVE.L D0,-(A6)
 RTS    \ just to indicate the MACHro stops here 
END-CODE MACH

CODE HC.epilogue
 MOVEM.L (A7)+,A0-A5/D0-D7( restore registers )
 UNLK A6
 MOVE.L (A7)+,A0 ( return address )
 ADD.W  #4,A7  ( pop off 4 bytes of parameters )
 JMP    (A0)
 RTS
END-CODE MACH

( ------------------ )

header flashy.start
 JMP flashy.start  ( to be filled later )

header doneString 
 DC.B ‘Done’
 DC.B 0

header errorString 
 DC.B ‘Error’
 DC.B 0

header PascalString 255 allot

CODE callJSR
 MOVE.L (A6)+,-(A7)
 RTS
END-CODE
: ZeroToPas { HCPars CStr PStr | -- }
 CStr HCPars inArgs + !
 PStr HCPars inArgs + 4 + !
 xreqZeroToPas HCPars request + w!
 HCPars entryPoint + @ callJSR ( call Hypercard here );  
: StrToNum  { HCPars Str | -- result }
 Str HCPars inArgs + !
 xreqStrToNum HCPars request + w!
 HCPars entryPoint + @ callJSR 
 HCPars outArgs + @;
  
: flashy{ HCpars | hP1 screen times -- }
 HCPars params + @ -> hP1 \ handle to first parameter
 \ (zero-terminated string)
 hP1 (call) HLock drop    \ yes, I know I’m paranoid 
 HCPars hP1 @ [‘] PascalString
 ZeroToPas
 hP1 (call) HUnLock drop
 HCPars [‘] PascalString 
 StrToNum -> times
 times 0> IF
   WMgrPort @ -> screen
   times 0 DO
 screen portRect + dup 
 (call) InvertRect (call) InvertRect
 LOOP 
   [‘] doneString 5 (call) PtrToHand drop
 HCpars returnValue + !
 ELSE ( if it is negative, return an error )
   [‘] errorString 6 (call) PtrToHand drop
 HCpars returnValue + !
 THEN;
: flashy.glue
 HC.prelude  flashy  HC.epilogue  ;
header flashy.end
( now setup correct entry address )
‘ flashy.glue ‘ flashy.start 2+ - ‘ flashy.start 2+ w!

( *** making the XCMD resource *** )
: $create-res call CreateResFile call ResError L_ext ;

: $open-res { addr | refNum -- result }
 addr call openresfile -> refNum
 call ResError L_ext
 dup not IF drop refNum THEN ;
: $close-res call CloseResFile call ResError L_ext ;

: make-xcmd { | refNum -- }
 “ xcmd.res” dup $create-res
 abort” You have to delete the old ‘xcmd.res’ file first.”
 $open-res dup -> refNum call UseResFile 
 [‘] flashy.start [‘] flashy.end over - 
 call PtrToHand drop ( result code )
 “xcmd 2000 “ flashy” call AddResource
 refNum $close-res drop ( result code );
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

OmniGraffle Pro 7.19.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
OmniGraffle 7.19.3 - Create diagrams, fl...
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
Hopper Disassembler 5.3.3- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
calibre 5.35.0 - Complete e-book library...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Sound Studio 4.10.0 - Robust audio recor...
Sound Studio lets you easily record and professionally edit audio on your Mac. Easily rip vinyls and digitize cassette tapes, or record lectures and voice memos. Prepare for live shows with live... Read more
Sparkle Pro 4.0 - Visual website creator...
Sparkle Pro will change your mind if you thought building websites wasn't for you. Sparkle is the intuitive site builder that lets you create sites for your online portfolio, team or band pages, or... Read more
Dropbox 140.4.1951 - Cloud backup and sy...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
FotoMagico 6.0.5 - Powerful slideshow cr...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more
Remotix 6.4.2 - Access all your computer...
Remotix is a fast and powerful application to easily access multiple Macs (and PCs) from your own Mac. Features: Complete Apple Screen Sharing support - including Mac OS X login, clipboard... Read more
Microsoft Office 365, 2019 16.57 - Popul...
Microsoft Office 365. The essentials to get it all done. Unmistakably Office, designed for Mac Get started quickly with new, modern versions of Word, Excel, PowerPoint, Outlook and OneNote-... Read more

Latest Forum Discussions

See All

A House Full of Covid – The TouchArcade...
It’s been a rough week as both of our young children tested positive for Covid, and since recording this early on Friday my wife has tested positive now too. Thankfully the kids seemed to recover fairly quickly and are mostly back to normal, and I... | Read more »
TouchArcade Game of the Week: ‘Krispee S...
Krispee Street is a new hidden object game from Frosty Pop that is based on their popular and almost painfully sweet webcomic Krispee. This is one of the latest titles to be added to the Netflix Games catalog, which means you’ll need to log into... | Read more »
SwitchArcade Round-Up: ‘Escape Lala’, ‘B...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 21st, 2022. In today’s article, we’ve got a lot of new releases. A lot. There were eight on the schedule when I went to bed last night. There were twenty-four when I woke up... | Read more »
Beta Testers Needed for Huge Version 2.0...
Ya’ll remember Dungeon Raid, right? The phenomenal matching RPG hybrid that launched on mobile more than a decade ago, but was more or less abandoned by its developer only to die a slow death on the App Store before the 32-bit Appocalypse finally... | Read more »
‘Ark Legends’ Gives Players a Chance to...
It’s Airpods and Amazon gift cards galore as Melting Games opens pre-registration for Ark Legends. The upcoming mobile RPG is giving away tons of in-game goodies such as gold, energy, iron core, hero summon chest and rare iron core to players who... | Read more »
‘Nickelodeon Extreme Tennis’ Out Now on...
Nickelodeon Extreme Tennis () from Old Skull Games and Nickelodeon is this week’s new Apple Arcade release. Nickelodeon Extreme Tennis features characters from old and new Nickelodeon shows including SpongeBob, TMNT, and many more. The tennis game... | Read more »
SwitchArcade Round-Up: ‘RPGolf Legends’,...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 20th, 2022. In today’s article, we’ve got a massive amount of new releases to check out. We’ve got summaries of all of them, from heaven to hell. We also have the lists of... | Read more »
‘Zed Blade ACA NEOGEO’ Review – Well, It...
SNK’s NEOGEO platform played host to a great many classics, both famous and under-the-radar. The Metal Slug games. The King of Fighters series. Magician Lord. Shock Troopers. Sengoku 3. NEO Turf Masters. Fatal Fury. Samurai Shodown. Twinkle Star... | Read more »
‘Inua – A Story in Ice and Time’ is a Un...
One thing I know about ARTE from their output on mobile over the years is that they love collaborating with really interesting and unique studios to put out really interesting and unique gaming experiences. This is true yet again with the latest... | Read more »
Out Now: ‘Angry Birds Journey’, ‘RPG Dic...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »

Price Scanner via MacPrices.net

Amazon lowers prices on select 13″ M1 MacBook...
Amazon has select Apple 13″ M1 MacBook Airs on sale for $150 off MSRP this weekend, starting at only $849. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so... Read more
Apple has 13″ M1 MacBook Airs back in stock s...
Apple has restocked a full line of 13″ M1 MacBook Airs, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1-powered MacBooks for sale today at... Read more
In stock and on sale! 16″ 10-Core M1 Pro MacB...
Amazon has new 16″ 10-Core/512GB M1 Pro MacBook Pros in stock today and on sale for $50 off MSRP including free shipping. Their prices are the lowest available for new M1 Pro 16″ MacBook Pro from any... Read more
Deal Alert!: 14″ M1 Pro with 10-Core CPU in s...
Amazon has the new 14″ M1 Pro MacBook Pro with a 10-Core CPU and 16-Core GPU in stock today and on sale for $2299.99 including free shipping. Their price is $200 off Apple’s standard MSRP, and it’s... Read more
Apple has 24-inch M1 iMacs (8-Core CPU/8-Core...
Apple has restocked a wide array of 24-inch M1 iMacs with 8-Core CPUs and 8-Core GPUs in their Certified Refurbished store. Models are available starting at only $1269 and range up to $260 off... Read more
Select 24″ M1 iMacs are on sale for $100 off...
Sales of Apple’s new 24″ M1 iMacs have been rare since its introduction, perhaps due to global supply issues. However, B&H is offering a $100 discount on select 24″ iMacs, and they’re in stock... Read more
M1 Mac minis are back in stock today at Apple...
Apple has M1-powered Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year warranty, and shipping is free: –... Read more
B&H has M1-powered Mac minis on sale for...
B&H Photo has Apple’s Mac minis with M1 Apple Silicon CPUs in stock today and on sale for $50-$100 off MSRP, starting at $649. Free 1-2 shipping is free to many US addresses. Their prices are... Read more
New Amazon sale: Apple’s 13″ M1 MacBook Airs...
Amazon has Apple 13″ M1 MacBook Airs on sale for $100 off MSRP, starting at only $899. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so check their site... Read more
Get an Apple Watch Series 7 for $50 off MSRP,...
Amazon has Apple Watch Series 7 models on sale for $50 off MSRP including free shipping. Their prices are the lowest available for Apple Watch Series 7 models today: – 41mm Apple Watch Series 7 GPS... Read more

Jobs Board

Registered Nurse (RN) Employee Health PSJH -...
…is calling for a Registered Nurse (RN) Employee Health PSJH to our location in Apple Valley, CA.** We are seeking a Registered Nurse (RN) Employee Health PSJH to be Read more
Systems Administrator - Pearson (United State...
…and troubleshoot Windows operating systems (workstation and server), laptop computers, Apple iPads, Chromebooks and printers** + **Administer and troubleshoot all Read more
IT Assistant Level 1- IT Desktop Support Anal...
…providing tier-1 or better IT help desk support in a large Windows and Apple environment * Experience using IT Service Desk Management Software * Knowledge of IT Read more
Human Resources Business Partner PSJH - Provi...
…**is calling a** **Human Resources Business Partner, PSJH** **to our location in Apple Valley, CA.** **Applicants that meet qualifications will receive a text with Read more
Manager Community Health Investment Programs...
…is calling a Manager Community Health Investment Programs PSJH to our location in Apple Valley, CA.** **Qualified candidates will be invited to do a self-paced video Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.