ShiftMod
Volume Number: | | 3
|
Issue Number: | | 9
|
Column Tag: | | Technical Note
|
ShiftMod Patch
By Mike Scanlin, San Diego, CA
Last week we were talking with an IBM freak about the differences between our Macs and his footrest, that is, PC. One of his complaints about the Mac was that you get upper case letters when both the shift key and caps lock key are down at the same time. He wanted to be able to type a lower case letter if the caps lock key was down by pressing the shift key. Then he could type sentences like i WISH i HAD A mACINTOSH without having to hold down the shift key the whole time. Well, Mac fans, fear not. Now the Mac can do this too. Say hello to Mr. ShiftMod.
All that is really needed is a tail patch to _GetNextEvent that checks if a keyDown or autoKey event occured while the caps lock and shift keys were held down. The only problem with implementing it is that because _GetNextEvent is a stack based trap (it gets its parameters from the stack) the patch requires a bit of self modifying code to get it to set things up correctly. While this example is trivial, the technique used here can be used to patch any stack based trap routine.
A tail patch is something that first calls the original trap and then does some post-processing before returning to the caller. If the original trap is not stack based, we can do a JSR OriginalTrap at the beginning of our patch, do our post-processing, and end with an RTS. However, if the original trap is stack based it will expect the stack to look a certain way when it gets called. By doing a JSR to it, we put a return address on top of the existing stack which, in effect, shifts all of the parameters by 4 bytes (relative to the top of the stack). The routine being called does not know about the extra return address and will use the wrong bytes as parameters.
The code listed here is an application, but it can be easily (via ResEdit) be made into an INIT resource and pasted into your system file. It installs itself in the system heap and hangs around as long as you dont turn your Mac off. This code is useful if you want to install any event processing stuff that is global for all applications. For instance, you could call _Eject every time you detect a diskEvt and count how many times someone tries to insert a disk in a minute. Or you could do something semi-useful like adding a keyclick after keyDowns (how about a mouseclick on mouseDowns?) for debugging.
Now how about an IBM hacker writing a routine to make his machine return upper case letters when both the shift key and caps lock key are down? Is it even possible?
/* shiftMod.c 2 July 1987
*
* by Mike Scanlin and Andy Voelker
*
* This program installs a tail patch on _GetNextEvent so
* that the shift key toggles between upper and lower case
* letters if the caps lock key is down.
*
* No toggle is done if the option and/or command key was
* held down.
*/
#includeAsm.h
#includeEventMgr.h
#define whatOFFSET(EventRecord,what)
#define message OFFSET(EventRecord,message)
#define modifiersOFFSET(EventRecord,modifiers)
#define GetNextEvent 0xA970
#define JMP 0x4EF9
#define memFullErr -108
main()
{
asm {
move.l D3,-(SP)
/* set up the JMP instruction at the end of the patch */
lea @patchExit,A0
move #JMP,(A0)
/* get the old trap address */
move #GetNextEvent,D0
_GetTrapAddress
/* set up the JMP instruction that calls the original trap */
lea @origTrap,A1
move #JMP,(A1)+
move.l A0,(A1)
/* get some space in the system heap */
lea @last,A0
lea @first,A1
suba.l A1,A0
move.l A0,D0
move.l D0,D3 /* save for _BlockMove */
_NewPtrSYS
cmpi #memFullErr,D0
beq.s @noPatch
move.l A0,-(SP) /* save for _BlockMove */
/* set the trap address to the space we just got in the
* system heap
*/
move #GetNextEvent,D0
_SetTrapAddress
/* now move it into place */
lea @first,A0
move.l (SP)+,A1
move.l D3,D0
_BlockMove
@noPatchmove.l (SP)+,D3
rts
/* Heres the new _GetNextEvent. It calls the existing
* _GetNextEvent and then checks if a keyDown or autoKey
* event is being reported.
*/
/* save original return address */
@first lea @exitAddress,A0
move.l (SP)+,(A0)
/* save ptr to event record */
lea @eventPtr,A0
move.l (SP),(A0)
/* return to our routine */
pea @tailPatch
@origTrap nop /* JMP to original trap */
nop
nop
@tailPatchlea @eventPtr,A0
move.l (A0),A0
/* Is it a keyDown or autoKey? */
move what(A0),D0
cmpi #keyDown,D0
beq.s @isKeyDown
cmpi #autoKey,D0
bne.s @patchExit
@isKeyDownmove modifiers(A0),D0
andi #shiftKey+alphaLock+optionKey
+cmdKey,D0
eori #shiftKey+alphaLock,D0
bne.s @patchExit
move.l message(A0),D0
cmpi.b #A,D0
bmi.s @patchExit
cmpi.b #Z,D0
bgt.s @patchExit
addi.b #a-A,D0
move.l D0,message(A0)
/* return to original caller via long JMP */
@patchExitnop
@exitAddressnop
nop
@eventPtr dc.l 0 /* storage for event record ptr */
@last
}
}