TweetFollow Us on Twitter

Button DA
Volume Number:2
Issue Number:5
Column Tag:Assembly Language Lab

Panic Button DA Shows Screen Blanking

By Ed Ludwig, Garfield, NJ

Panic Button DA

It is very difficult these days to find a computing magazine that isn't 98% fluff (and meaningless advertisments) and 2% useful information. MacTutor is the only magazine that I actually look forward to reading each month from cover to cover!

Here is a little gem, just to show my appreciation for all the hard work the MacTutor staff puts in each month, and one I hope proves useful to our readers as well. Here is a desk accessory (very tiny!) that I wrote using the MDS Assembler. It's function is very simple; it's a Panic Button! There are probably others that are more sophisticated than this one, but sometimes less is more.

Fig. 1 The Panic Button Exposed!

When you invoke it, you are given a tiny dialog box which you can move and hide in a corner of the screen. When you are in the middle of your favorite program (doodling in MacPaint for example...) and your boss comes along, what do you do? HIT THE PANIC BOTTON! When you hit the button, the whole screen goes dark as if the Mac is turned off and you are save! After the boss goes away, click the mouse, and everything is just as you left it.

There are only two things to remember: First, move the mouse away from the spot where you pressed the panic button when you click to restore the screen, because if you click inside the button again, the whole screen will flash dark again. Second, the program grabs about 22K of storage to save away the screen buffer while the screen is dark (and then restores it again) so it may not work with large programs that don't leave 22K of memory in the Mac to spare; however, a simple Alert box will be triggered with the message "Not Enough Memory' to inform you of that fact if a memory problem develops.

Program Details

Desk accessories were covered in detail last month in both C and assembly, but this little DA is very nicely done and easy to follow. In fact, it will serve as a better introduction than the more complicated DA published last month if you are new to DA programming. As with all DA's, we begin with a table of word length constants that tell the desk manager where the five entry points of the DA are. These five entry points are standard for any type driver application, which is what a desk accessory is. The entry points are for opening the DA, closing the DA and control of the DA. The other two entry points are normally associated with device drivers that require a status check or a prime routine for set-up. Immediately after this table of entry points is the string title of our desk accessory.

The three routines for open, close and control then follow. In general, the desk manager will pass control to either one of these three routines from the currently running application depending on circumstances. When the user first selects the DA from the Apple menu, the open entry will be invoked. When a control in the DA's window is pressed, the control entry will be invoked. When the user no longer requires the DA and presses the close box, then the close entry will be invoked. We must be prepared for the DA to begin execution at any one of these three entry points.

Open and Close

When we open or close the DA, we first save the application's graph port. For some reason, the desk manager does not do this for us. Care must be taken to save all the registers and especially to protect the contents of A0 and A1, which contain information vital to the DA. It is very easy to forget and make a trap call and find out the DCE pointer is messed up because the A1 register was not protected from alteration. When the DA is called, register A0 contains a pointer to the IO Parameter Block and A1 contains the pointer to the Device Control Entry Record. These two structures contain all the information the DA needs to find out what to do. In the IO Parameter block is a field called csCode which we use to branch on to handle the various events passed to the DA. This event processing is done in the Control entry of our DA. In the Open and Close entry portions, we make use of the Device Control Entry Record to set up any global storage space required by the DA. The field dCtlStorage is used to hold a handle to any global variable space required by the DA. Both the DCE and the IO Parameter block were shown in detail in last months's issue of MacTutor, and are documented in Inside Macintosh.

Resource ID

One thing we need to do when we open the DA is to establish the base ID for our resources. In the top of our DA source code, we tell the linker that the code that follows is a resource of type DRVR and we give it a resource Id number as shown:

RESOURCE 'DRVR' 25 'Panic Button'

In fact, this entire source file is all resources! After the code resource, we have our 'traditional' resources. Anyway, the number 25 indicates the Id number for this DRVR resource, and hence for our DA. All other resources must be a function of this number, because the Font/DA Mover will change the DA number when it inserts this DA into the system file so that no two DA's have the same Id. The formula for calculating the RMaker resource ID number from this DA reference number is as follows:

RMaker ID = (DArefNum X 32) + Index - 16384

The index is used for owned resources such as Alert items, where the resource "owns" other resources. Otherwise, it is normally zero. Since our DA reference number is 25, we calculate:

RMaker ID = 25 X 32 +0 -16384 = -15584

Hence this is the number we would use for our resource ID's if we were using the RMaker file to compile our resources. It turns out that RMaker has a problem opening a ".REL" file created by the Consulair C compiler for a DRVR Proc type resource. As a result of this bug, I was unable to use RMaker to complete the DA so I have translated all the RMaker resources into assembly language and have used the Consulair linker to create the DA. If your using the MDS assembler, simply assemble and link this file with the MDS linker. I have to use the Consulair compiler for my MDS assembler because I have a Mac Plus and Apple hasen't finished fixing the MDS system to run properly under HFS. Actually, the Consulair Compiler makes a great MDS alternative. But when the 16 bit value -15584 is used for the resource ID, the assembler converts this to a 32 bit value of FFFF C320 which does not work for a resource ID. So what we do is take -15584, convert it to hex with a hex calculator (included on the source code disk for this issue) to FFFF C320, take the last word, C320 and convert that back to decimal, which gives us 49952. This is the decimal value we use for our resource ID numbers when coding resources in assembly language.

The last thing we do in our open routine is to set up a dialog type window and to save the window pointer back in our DCE record and mark that window as a system window belonging to a DA by updating the windowkind field in our newly created window record. We then restore the users graph port and exit, clearing D0 to show the DA executed properly.

For the close routine, we do much the same thing only in reverse. This time we dispose of our window and release any global storage we might have referenced to dctlstorage field of our DCE record. In this DA, we don't use any storage except briefly during our control loop when we blank the screen.

The Control Loop

That brings us to the control loop, where the events processed by this DA are handled. The IO Parameter block entry cscode tells us which event was passed to us from the desk manager. In our case, the only event we will see is a dialog event when the user presses the panic button. The csparam field of the IO Parameter block contains the pointer to our event record, so we use that along with our window pointer to call _dialogselect, which returns the itemhit with the control item that caused the event. If we didn't get an event, then we take the control exit through JIODone and return to the current application.

If we did get an event, then that means the user pressed the panic button and our job is to clear the screen. Now the control entry code does it's work. We first get a handle to 22K of storage space so we can copy the current screen contents into a safe area on the heap. If this request becomes a problem for the Mac, then we check the error code returned in D0 to see if it's a memory full error, -108. If so, then we forget the screen blanking and exit because there is not enough memory available to lock up the necessary storage to blank the screen. However if it's some other type of error, the routine is executed with the assumption that the new handle was returned. This might cause a problem but I can't think under what circumstances it might happen. If the error is a memory full, we display an alert message using our alert resource, and then dispose our window and exit through JIODone. Since our simple DA doesn't need any attention, we return no error code and it's just as if we were never called.

Assuming no error was returned, we can then blank the screen. We do this by locking our handle after saving it in the dctlstorage field of the DCE record and then calling our blank screen subroutine. When we return, we unlock our handle and get rid of the storage space and exit.

Screen Save

That leaves only the screen save and get id base subroutines to discuss. The screen save subroutine moves each byte of the screen buffer to our 22K storage space on the heap so it can be restored later, and fills the screen with $FFFF for black. A pointer to the base of the screen buffer area is contained in the system global location $824. We load the screen buffer pointer into A3 and the deferenced handle of our screen storage area into A2 and do an indirect move to move each word from the screen to the storage. Our loop counter is 10,943 words of memory or 21,886 bytes of memory. (Note that in the old 128K Macs, after the screen buffer, you have just over 100K, and with another 20K for various system things like heaps and stacks and such, the poor machine had only 80K or so for programs. Since most Mac programs take at least 60K, it's no wonder the skinny mac died of memory starvation.)

After setting the screen black, we loop on the buttonstate until it changes and we can then restore the screen and exit. The button state is stored in a system global at $172 and will be set when a button down event is registered by the system.

Our resources provide us with a dialog window containing a single control item, a button. The DLOG and first DITL resources specify our window and panic button. The next resource is an alert: a sorry button control and a string telling the user there is not enough memory. This alert is invoked when we detect a memory full error when we try to get a handle to 22K of heap space for saving the screen buffer. Note that the alert resource "owns it's DITL list and so the resource ID of it's DITL list is incremented accordingly.

That completes our simple, but fun little DA. Enjoy.

Assembly Language Source Code

;Panic Button DA
; By Ed Ludwig for MacTutor

RESOURCE 'DRVR' 25 'Panic Button'

;includes (probably not all are neccesary) 

Include MacTraps.d ; sys and toolbox traps
Include ToolEqu.d; Use toolbox equates
Include QuickEqu.d ; Use quickdraw equates
Include SysEqu.d ; Use system equates
include FSEqu.d  ;file system equates

;my equates

screenbaseequ  $824;pointer
buttonstate equ  $172;byte

; first comes the device control entry table

accentry:

 dc.w   $0400  ;control events only 
 dc.w   0 ;set only if acc. needs time
 dc.w   $0142  ;event mask
 dc.w   0 ;menu id (none at the moment)
 
 ;offsets into routines used by desk manager
 
 dc.w   accopen-accentry  ;open routine
 dc.w   accdone-accentry  ;prime (unused)
 dc.w   accctl-accentry ;control routine
 dc.w   accdone-accentry  ;status (unused)
 dc.w   accclose-accentry ;close routine
 
acctitle: ;name of accessory
 dc.b   @2-@1
@1 dc.b 'panic button - by Ed Ludwig'
@2
.align  2
 
;------------------------------open------------------------------------

;save application port and allocate window
 
accopen:
 movem.la0-a4/d0-d4,-(sp) ;save registers
 move.l a1,a4    ;move dce to safe place
 
 subq.l #4,sp    ;make room for the port
 move.l sp,-(sp) ;push a pointer to it
 _getport ;get it, now  on stack
 
 tst.l  dctlwindow(a4)  ;already have a window?    bne   standardreturn
 ;yes, non-zero so exit
 ;no, make new window
 bsr getidbase   ;get the base id number
 
 clr.l  -(sp)    ;space for window ptr
 move.w d0,-(sp) ;push basenum
 clr.l  -(sp)    ;nil for dstorage
 moveq  #-1,d0   ;get -1 into d0
 move.l d0,-(sp) ;push behind = -1
 _getnewdialog   ;display
 move.l (sp)+,a0 ;pop dialog pointer 
 
 move.l a0,dctlwindow(a4) ;save window pointer in dce          
 ;mark as system window
 move.w dctlrefnum(a4),windowkind(a0)

standardreturn:
 ;graph port on stack
 _setport ;make window current portmovem.l   (sp)+,a0-a4/d0-d4 ;restore 
registers
accdone:
 moveq  #0,d0    ;return no error
 rts
 
;-------------------------------close----------------------------------

accclose:

 movem.la0-a4/d0-d4,-(sp) ;save registers
 move.l a1,a4    ;move dce to safe place
 
 subq.l #4,sp    ;make room for the port
 move.l sp,-(sp) ;push a pointer to it
 _getport ;get it, now its on the stack
 
 tst.l  dctlwindow(a4)  ;do we have a window?
 beq  @1;no, go past disposal
 ;yes, prepare to trash it
 move.l dctlwindow(a4),-(sp);dispose window
 clr.l  dctlwindow(a4)  ;clear entry in dce
 _disposdialog   ;goodby window
@1 ;note port still on stack
 _setport ;make window current port
 movem.l(sp)+,a0-a4/d0-d4 ;restore registers
 moveq  #0,d0    ;return no error
 rts
;-------------------------------control------------------------------------

accctl:
 movem.la0-a4/d0-d4,-(sp) ;save registers
 move.l a1,a4    ;move dce to safe place
 moveq  #0,d0    ;clear d0

 move.w cscode(a0),d0;get control code
 cmpi.w #accevent,d0 ;is it an event?
 bne  done;no, all done
 ;yes, do event
 clr.w  -(sp)    ;clear result
 move.l csparam(a0),-(sp)   ;push ptr to event record
 pea  dctlwindow(a4) ;push address of dialog ptr
 pea  itemhit    ;push address of itemhit
 _dialogselect
 move.w (sp)+,d0 ;clear boolean 
 bne  whichevent ;yes handle it
done:
 movem.l(sp)+,a0-a4/d0-d4 ;restore registers
 moveq  #0,d0    ;return no error
 move.l jiodone,-(sp);jump to iodone
 rts
 
whichevent:
 move.w itemhit,d0
 subq.w #1,d0    ;was an item hit?
 bne  done;no, all done
 ;yes, so go to it!
 move.l #21888,d0;# of bytes on mac screen
 _newhandle ;get handle to 22K of store! 
 beq  @7;everything okay so branch
 cmpi.w #-108,d0 ;no, mem full error?
 bne  @7;no, go on to branch
 ;(could this happen?)
 
@6 ;yes, display error alert
 bsr getidbase   ;get base id num  in d0
 clr.w  -(sp)    ;save space
 move.w d0,-(sp) ;push basenum
 clr.l  -(sp)    ;nil for filter proc.
 _alert ;display alert
 move.w (sp)+,d0 ;pop result
 ;prepare to exit
 move.l dctlwindow(a4),-(sp)
 clr.l  dctlwindow(a4)  ;clear entry in dce
 _disposdialog   ;dump window
 bra  done;exit
 
@7 ;got handle ok
 move.l a0,dctlstorage(a4);save handle in dce
 move.l a0,a2    ;set up screen save
 _hlock ;lock handle
 bsr  screensave ;do the save and restore
 move.l dctlstorage(a4),a0
 _hunlock ;unlock storage
 move.l dctlstorage(a4),a0
 _disposhandle   ;dispose storage  (22k!)
 clr.l  dctlstorage(a4) ;clear storage bra   done        ;exit 
;------------------  SUBROUTINES  ----------------------------

screensave: ;clear and save screen
 ;enters with handle to storage
 _hidecursor;hide for proper restore
 move.l (a2),a2  ;derefence handle
 move.l #10943,d1;set loop index
 moveq  #0,d0    ;set screen index
 move.l screenbase,a3;base address of screen
@1 ;move word from screen to storage
 move.w 0(a3,d0.l),0(a2,d0.l) 
 move.w #$ffff,0(a3,d0.l) ;black out screen word
 addq.l #2,d0    ;inc screen index
 dbf  d1,@1 ;dec loop index
 moveq  #0,d0
@2
 move.b buttonstate,d0  ;user press button?
 bne  @2;no, wait until it is ;yes, restore screen
 move.l #10943,d1;set loop index moveq #0,d0       ;set screen index
@3 ;move word from storage to screen
 move.l 0(a2,d0.l),0(a3,d0.l) 
 addq.l #2,d0    ;inc screen index
 dbf  d1,@3 ;dec loop index
 _showcursor;restore cursor
 rts    ;return
 
getidbase:;exits with base id number in d0
 moveq  #0,d0    ;clear d0
 move.w dctlrefnum(a4),d0 ;get reference number
 addq.w #1,d0    ;and calc base number
 muls #-1,d0;for resource id 
 mulu #32,d0
 add.w  #$c000,d0
 rts
 
;--------------------------------data----------------------------------

itemhit dc.w0

;---------------------------- Resources ----------------------------

; resource ID numbers must be negative functions of 
; DA ref. number. The formula is: RMaker ID =
; (DArefNum X 32) + (index of owned resources) - 16384
; where index of owned resources is 0 on up. If our DA were
; 12 then 12 X 32 + 0 -16384 = -16000 which is the RMaker ID.
; But the asm converts -16000 to a 32 bit number FFFF C180
; which doesn't work. So, convert -16000 to hex and use the
; first word: -16000 = FFFF C180, so convert C180 to Decimal
; to get 49536, a 16 bit number, which is the assember 
; ID number for DA 12.
 
RESOURCE 'DLOG' 49952 'title'
 DC.W 295 ;TOP
 DC.W 399 ;LEFT
 DC.W 325 ;BOTTOM
 DC.W 492 ;RIGHT
 DC.W 4 ;window type
 DC.B 1 ;visible (T=1/F=0)
 DC.B 0 ;ignored (part of visible)
 DC.B 1 ;goaway box (1=yes, 0=none)
 DC.B 0 ;ignored (part of goaway)
 DC.L 0 ;refcon
 DC.W 49952 ;id of DITL list
 DC.B @2-@1 ;length byte
@1 DC.B 'Don',39,'t' ;window title
@2
.align 2
RESOURCE 'DITL' 49952 'Button'
 DC.W 0 ;number of items -1
 
 DC.L 0 ;handle holder
 DC.W 5 ;top
 DC.W 16;left
 DC.W 25;bottom
 DC.W 77;right
 DC.B 4+0 ;item type (cntrl button)
 DC.B @4-@3 ;item length (even)
@3 DC.B 'Panic!' ;item itself
@4
.ALIGN 2
RESOURCE 'ALRT' 49952 'error'
 DC.W 254 ;top
 DC.W 334 ;left
 DC.W 320 ;bottom
 DC.W 491 ;right
 DC.W $C321 ;resource ID of DITL list (49953)
 DC.W $4444 ;stages
.ALIGN 2
RESOURCE 'DITL' 49953 'msg'
 DC.W 1 ;number of items -1
 
 DC.L 0 ;handle holder
 DC.W 33;top
 DC.W 80;left
 DC.W 55;bottom
 DC.W 137 ;right
 DC.B 4+0 ;item type (cntrl button)
 DC.B @6-@5 ;item length (even)
@5 DC.B 'Sorry!' ;item itself
@6
 DC.L 0 ;handle holder
 DC.W 3 ;top
 DC.W 5 ;left
 DC.W 22;bottom
 DC.W 145 ;right
 DC.B 128+8 ;item type (disabled static text)
 DC.B @8-@7 ;item length (even)
@7 DC.B 'Not enough memory.';item itself
@8
END
; Link file for panic da

]
/Resources
 panic
/output  panic button
/Type 'DFIL' 'DMOV'
$
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

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
War Thunder 2.13.0.66 - Multiplayer war...
In War Thunder, aircraft, attack helicopters, ground forces and naval ships collaborate in realistic competitive battles. You can choose from over 1,500 vehicles and an extensive variety of combat... Read more
RoboForm 9.2.8 - Password manager; syncs...
RoboForm is a password manager that offers one-click login, mobile syncing, easy form filling, and reliable security. Password Manager. RoboForm remembers your passwords so you don't have to! Just... Read more
Adobe Photoshop 23.1.1 - Professional im...
You can download Photoshop for Mac as a part of Creative Cloud for only $20.99/month (or $9.99/month if you have purchased an earlier software version). Adobe Photoshop is a recognized classic of... Read more

Latest Forum Discussions

See All

‘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 »
SwitchArcade Round-Up: ‘Banjo-Kazooie’ C...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 19th, 2022. After a couple of big-pants articles in recent days, today is somewhat of a lighter one. We’ve got a little news to go over, a small handful of new releases to... | Read more »
‘Yu-Gi-Oh! Master Duel’ Is Out Now on PC...
Yu-Gi-Oh! Master Duel was confirmed for all consoles including Switch, PS5, and more in addition to PC and mobile platforms last year. Since then, Yu-Gi-Oh! | Read more »
SwitchArcade Round-Up: Reviews Featuring...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 18th, 2022. We’ve got another batch of reviews today, with our pal Mikhail covering the lamentable Grand Theft Auto: The Trilogy Definitive Edition. Has it been whipped into... | Read more »
‘Total War: Medieval II’ Coming to iOS a...
After a bit of teasing, Feral Interactive just announced that it is bringing Total War: Medieval II to iOS and Android. This will be the developer’s first release since the brilliant Alien Isolation. Check out my review of the iOS version here. | Read more »

Price Scanner via MacPrices.net

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
Here are the details of Apple’s 2022 Educatio...
Need a new Apple Mac or iPad for school? Whether you’re a student, teacher, or staff member, you can use your .edu email address when ordering at Apple Education to take up to $400 off the price of a... Read more
Amazon is blowing out 2020 21″ iMacs for only...
Amazon has clearance 2020 21″ iMacs (2.3GHz Dual-Core i5, 8GB RAM, 256GB SSD) on sale right now for $599.99 including free shipping. Original MSRP for this model was $1099. Amazon expects delivery in... 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.