Disclaimer: The code discussed here can be used in completely save and harmless applications, but may also cause damage to your calc if used improperly or even viciously. I'm not responsible for any results in abuse or misuse of this code.
Note this tutorial can be adapted to work with other types of hooks - I find the key hook to be quite easy and still useful so I chose that one for this tutorial.
What is a key hook?Key hooks are functions which are called on a certain key presses anywhere in the OS or TI-Basic program (or assembly/axe program that uses the OS key press functions). Key hooks thus can be used to add your own awesome functionality to the OS. Many shells use some kinds of key hooks such as zStart. What I'm going to show in this tutorial is how to set up your own key hooks in your Axe program. I'm not that great at assembly programming, so it might not be the cleanest code, but it works nicely and is probably easy to understand.
Important note: To make this work properly and not create memory leaks, you need to compile the program as an APPLICATION! Other keyhooks (such as zStart's) have to be turned OFF to cause no interference!The key hook function:That is the routine that runs when
any of the keys or combinations (like 2nd+[ ] or Alpha+[ ]) are pressed. You then need to check which one it was and decide what functionality to execute (similar to TI-Basic's getkey-function).
First we need to create a label with the name of the function (like any Axe subroutine). Because I am that creative I call it "
HOOK":
: Header
:... //Some other parts of your program
:
:Lbl HOOK
:
:
:Return
When the function is called we get the code of the key pressed last in the registers a. However, we have to add a,e as the first instruction so the OS recognizes our function as a hook. Because we don't want to mess up the registers in our function, we push them on the stack to back up.
add a,e
push HL
push BC
push DE
push AF
ld l,a ; load a to l to make it usable in axe
ld h,00
;function goes here
pop AF ; restore the registers we might have messed up
pop DE
pop BC
pop HL
Now the key code is prepared to be used in axe, since it is stored in axe's "ans", the HL register.
I already translated that assembly to Hex, so you can simply add it to your program:
: Header
... Some other parts of your program
:
:Lbl HOOK
:Asm(83E5C5D5F56F2600)
:->K //store ans in axe variable K to use in your compare statements
:
:Asm(F1D1C1E1)
:Return
That's the first part done. No feel free to run any instruction according to your needs,
as long as you don't jump out of the function. You find a list with the key codes (they differ from the getKey(x) ones) on
DeepThought's WikiPad here under "
_GetKey (Axe getKeyr)".
Installing your key hook:Before we can use the key hook function, we need to tell the OS that it should run a key hook and where it is.
These few assembly instructions will set the current HL as the key hook address. In our case, that is the address of the function "HOOK", and we get that using the little "
L" you find in the List-OPS menu. So "
LHOOK" will return the address of that label.
in a,(6) ;current ROM page to A
_bcall(_EnableRawKeyHook)
; Usually we now quit the program using
_bjump(_JForceCmdNoChar)
Now in Axe:
:LHOOK
:Asm(DB06EF664F)
:
: // return to OS
:Asm(CD50002740)
You can put this in any position of your code - I usually put it right to the beginning of the program, so it will be always installed when you run it. But you can also create a menu with an option to turn it on that will then run this code etc.
Now you are basically set to work with your hook.
Disabling the hook:Usually there is at some point the need to disable the hook again, so there is this simple line of Hex to do that job:
:Asm(EF6F4F) // = _bcall(_ClrRawKeyHook)
You can disable the hook when a certain key is pressed (put in the hook routine) or at some other point (e. g. using again some kind of menu).
Manipulating key code data:You can manipulate the return value of the getKey function inside your hook routine - a really mighty feature, e. g. you can block a certain key.
Since the key code is stored in register A which is on top of the stack we need to pop it off, manipulate it and push it back on the stack.
This only works at the end of the hook routine, since there the stack is like we left it and we don't want to mess things up.
This stores the value of the "ans" register HL as the return value of our routine.
pop AF
ld a,h
push AF
In Axe it looks like this:
:9 //this now replaces the key pressed with the code for "CLEAR" (example)
:Asm(F17CF5)
:... //use only ends of if statements here - that doesn't mess anything up.
:Asm(F1D1C1E1) //usual restore routine
:
Storing 0 will make the OS ignore the key press, that way you can block certain keys.
Now you should be ready to setup your own routines!
Example:This example will disable the functionality of the "0" key. No idea why someone would do that
. Pressing "STO->" makes it normal again.
:.KeyHook //name of my app
:LHOOK
:Asm(DB06EF664F) //enable Hook
:Asm(CD50002740) //return
:Lbl HOOK
:Asm(83E5C5D5F56F2600)
:->K
:!If K-138 //someone pressed "STO->"
:Asm(EF6F4F) //Disable the hook
:Else!If K-142 //someone pressed "0"
:0
:Asm(F17CF5) //write 0 so the keypress is being ignored
:End //only an end of an "if"-statement, that's ok
:Asm(F1D1C1E1)
:Return
Have fun - and be careful not to mess things up - that happens quite easily....