Author Topic: [Tutorial] Key hooks in Axe  (Read 5776 times)

0 Members and 1 Guest are viewing this topic.

Offline MGOS

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 336
  • Rating: +95/-0
    • View Profile
[Tutorial] Key hooks in Axe
« on: December 24, 2013, 05:52:56 pm »
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":
Code: [Select]
: 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.
Code: [Select]
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:

Code: [Select]
: 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.
Code: [Select]
in a,(6)   ;current ROM page to A
 _bcall(_EnableRawKeyHook)
 ; Usually we now quit the program using
 _bjump(_JForceCmdNoChar)
Now in Axe:
Code: [Select]
: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:
Code: [Select]
: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.
Code: [Select]
pop AF
 ld a,h
 push AF
In Axe it looks like this:
Code: [Select]
: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.
Code: [Select]
:.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.... :)
« Last Edit: December 27, 2013, 07:04:01 pm by MGOS »

Offline Sorunome

  • Fox Fox Fox Fox Fox Fox Fox!
  • Support Staff
  • LV13 Extreme Addict (Next: 9001)
  • *************
  • Posts: 7918
  • Rating: +374/-13
  • Derpy Hooves
    • View Profile
    • My website! (You might lose the game)
Re: [Tutorial] Key hooks in Axe
« Reply #1 on: December 24, 2013, 07:37:32 pm »
Wha, this is a pretty nice tutorial, nice use of asm to get more awesome things done in axe, great job!
:thumbsup:

THE GAME
Also, check out my website
If OmnomIRC is screwed up, blame me!
Click here to give me an internet!

Offline ClrDraw

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 627
  • Rating: +61/-2
    • View Profile
    • GitHub
Re: [Tutorial] Key hooks in Axe
« Reply #2 on: December 27, 2013, 04:45:15 pm »
I have always wanted to use hooks in Axe but thought it was a stupid question and wouldn't work for some reason or another... Thank you so much, I will be using this!
Visit my GitHub for all my TI programs as well as other projects.
Also check out my website.

Offline Matrefeytontias

  • Axe roxxor (kinda)
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1982
  • Rating: +310/-12
  • Axe roxxor
    • View Profile
    • RMV Pixel Engineers
Re: [Tutorial] Key hooks in Axe
« Reply #3 on: December 27, 2013, 05:56:18 pm »
How to make it work from RAM :

Replace Asm(CD50002740) by Return. As first instruction, copy your hook to a fixed RAM area and put that area in HL before enabling the hook. Example from the tutorial but running in RAM :

.KHOOK
Copy(LHook, L1,LEnd-LHook)
L1
Asm(DB06EF664F)
Return

Lbl Hook
Asm(83C5D5E5F56F2600)
->K
...
Asm(F1E1D1C1)
Return
Lbl End

Offline MGOS

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 336
  • Rating: +95/-0
    • View Profile
Re: [Tutorial] Key hooks in Axe
« Reply #4 on: December 27, 2013, 06:18:05 pm »
How to make it work from RAM :

Replace Asm(CD50002740) by Return. As first instruction, copy your hook to a fixed RAM area and put that area in HL before enabling the hook.

Sure, you need to make sure that the memory area doesn't get corrupted by other programs (many programs and apps use it) and the function mustn't be to large. Otherwise that works fine!
Thanks for the addition!
« Last Edit: December 27, 2013, 06:23:13 pm by MGOS »

Offline Matrefeytontias

  • Axe roxxor (kinda)
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1982
  • Rating: +310/-12
  • Axe roxxor
    • View Profile
    • RMV Pixel Engineers
Re: [Tutorial] Key hooks in Axe
« Reply #5 on: December 27, 2013, 06:25:03 pm »
You're welcome :) and yeah, I just didn't want to think too much about a free RAM area to use. I use to use $8000.

Offline ClrDraw

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 627
  • Rating: +61/-2
    • View Profile
    • GitHub
Re: [Tutorial] Key hooks in Axe
« Reply #6 on: December 27, 2013, 06:31:51 pm »
You forgot the End between these two lines of code in your example program:

Code: [Select]
:Asm(EF6F4F)    //Disable the hook
:!If K-142   //someone pressed  "0"

Also what do I do to disable the memory menu instead of the 0 key?

edit 1: Figured it out. It's K-54 instead of K-142. I might add this to AlphaCS. I'll put you in the readme of course.
edit 2: Is there a way to chain hooks?
« Last Edit: December 27, 2013, 06:49:56 pm by ClrDraw »
Visit my GitHub for all my TI programs as well as other projects.
Also check out my website.

Offline MGOS

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 336
  • Rating: +95/-0
    • View Profile
Re: [Tutorial] Key hooks in Axe
« Reply #7 on: December 27, 2013, 06:59:19 pm »
You forgot the End between these two lines of code in your example program:

Code: [Select]
:Asm(EF6F4F)    //Disable the hook
:!If K-142   //someone pressed  "0"
Fixed, was actually and Else

Also what do I do to disable the memory menu instead of the 0 key?
Check out the Wikipad by DT, it's very handy for this kind of stuff.

Haha... Well... to be honest that was my initial intention which lead me to explore this whole thing....
Not just blocking it, but rather open up a menu that looks and behaves visually the same but doesn't do anything... ehhhrmmm I better stop talking now... ;)

edit 2: Is there a way to chain hooks?
I'm not an assembly programmer, I just get by... What do you mean by chaining hooks?
« Last Edit: December 27, 2013, 07:01:26 pm by MGOS »

Offline ClrDraw

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 627
  • Rating: +61/-2
    • View Profile
    • GitHub
Re: [Tutorial] Key hooks in Axe
« Reply #8 on: December 27, 2013, 07:25:14 pm »
Quote
Haha... Well... to be honest that was my initial intention which lead me to explore this whole thing....
Not just blocking it, but rather open up a menu that looks and behaves visually the same but doesn't do anything... ehhhrmmm I better stop talking now...  ;)
That is pretty cool, good luck with that!

Quote
I'm not an assembly programmer, I just get by... What do you mean by chaining hooks?
I mean making it so that the hook doesn't override other hooks, specifically zStart's.


I'm already doing some cool stuff with that code. I made it so that whenever enter is pressed it displayes "do it yourself". This is so going on my friends calculator XD
« Last Edit: December 27, 2013, 09:11:17 pm by ClrDraw »
Visit my GitHub for all my TI programs as well as other projects.
Also check out my website.

Offline Matrefeytontias

  • Axe roxxor (kinda)
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1982
  • Rating: +310/-12
  • Axe roxxor
    • View Profile
    • RMV Pixel Engineers
Re: [Tutorial] Key hooks in Axe
« Reply #9 on: December 27, 2013, 08:37:34 pm »
Hook chaining is done by calling the already existing hook within your own hook, I guess. Remember to put the right value in A though.

Offline MGOS

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 336
  • Rating: +95/-0
    • View Profile
Re: [Tutorial] Key hooks in Axe
« Reply #10 on: December 28, 2013, 04:32:57 am »
Hook chaining is done by calling the already existing hook within your own hook, I guess. Remember to put the right value in A though.
Seems doable, so you have to somehow get the address of the other existing hook and then call it.

Quote
Haha... Well... to be honest that was my initial intention which lead me to explore this whole thing....
Not just blocking it, but rather open up a menu that looks and behaves visually the same but doesn't do anything... ehhhrmmm I better stop talking now...  ;)
That is pretty cool, good luck with that!
Actually that seems to be more than scratching the edge of legality in schools... "attempt to deceive" is what it's called when it gets busted.


I'm already doing some cool stuff with that code. I made it so that whenever enter is pressed it displayes "do it yourself". This is so going on my friends calculator XD
Nice!
Actually there might be another (maybe more convenient) way to achieve this: The hoomscreen hook. It is run when one of the following things happen, what happened is specified by the register A so you can check that in your routine:
  • A = 0: Displaying a result - hack here to change the result. It is stored in OP1. When the Zeroflag is reset on return, then nothing is displayed.
  • A = 1: Key press - keycode is in B, you can change that here. When the Zeroflag is reset on return, then the key press is ignored.
  • A = 2: Expression is entered. When the Zeroflag is reset on return, the operation is cancelled.
  • A = 3: Edit/changing context -last context value is in B. Return with zeroflag set!
Source

Interesting is A = 0. We can actually recycle almost the whole code from before, except the bcalls and "cp a"


Code: [Select]
; address in HL
 in a,(6)   ; current rom page
 _bcall(_EnableHomescreenHook)
 _bjump(_JForceCmdNoChar) ; Quit


 ;Hook itself:
 add a,e  ; 0x83 required to be identified as legal routine
 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
 cp a    ; set zeroflag



Disable:
 _bcall(_ClrHomescreenHook)


Code: [Select]
:LHOOK
:Asm(DB06EFAB4F)
:Asm(CD50002740)
:
:Lbl Hook
:Asm(83E5C5D5F56F2600)
:!If ->A
://Do some crazy stuff like _bcall(_Random) store some random stuff in OP1:
:Asm(EF794B)
://Disable the hook:
:Asm(EFAE4F)
:End
://restore and cp a
:Asm(F1D1C1E1BF)   //note the BF, otherwise things get messed up badly
:Return

Hope that helps

Offline Matrefeytontias

  • Axe roxxor (kinda)
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1982
  • Rating: +310/-12
  • Axe roxxor
    • View Profile
    • RMV Pixel Engineers
Re: [Tutorial] Key hooks in Axe
« Reply #11 on: December 28, 2013, 05:59:11 am »
Hook chaining is done by calling the already existing hook within your own hook, I guess. Remember to put the right value in A though.
Seems doable, so you have to somehow get the address of the other existing hook and then call it.
You can check if a raw key hook is being active by checking bit 5 of (iy+$34). If it's set, you can then retrieve the address + page of the hook from ($9B84). I'll have to do some tests in Wabbitemu to check what is what though.
« Last Edit: December 28, 2013, 05:59:35 am by Matrefeytontias »

Offline E37

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 292
  • Rating: +20/-0
  • Trial and error is the best teacher
    • View Profile
Re: [Tutorial] Key hooks in Axe
« Reply #12 on: March 20, 2016, 10:18:19 am »
Hey! Sorry for the necro-post but I don't feel like my question deserves a new topic.
Whenever I try to do any memory commands GetCalc(ptr,NUMBER) to create a new program or even run one it throws ERR:MEMORY
I am trying to use a homescreen hook and have already successfully done other commands like Disp when a key set is pressed.
I'm still around... kind of.

Offline Runer112

  • Project Author
  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2289
  • Rating: +639/-31
    • View Profile
Re: [Tutorial] Key hooks in Axe
« Reply #13 on: March 20, 2016, 11:46:36 am »
This is because text entry on the home screen is implemented using an edit buffer. An edit buffer expands the edited variable to consume all free user RAM to ensure that no expansion or mass data movement needs to occur while editing, which could cause noticeable slowdowns.

The most straightforward solution would be to close the edit buffer, create whatever variables you need to create, and then reopen the edit buffer. The trickiest part would be reopening it to the same location, which frankly I haven't done enough with edit buffers to confidently say I know how to do. At the very least, in case you didn't know, I can tell you that the name of the homescreen entry variable is "prgm!".
« Last Edit: March 20, 2016, 11:49:43 am by Runer112 »

Offline E37

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 292
  • Rating: +20/-0
  • Trial and error is the best teacher
    • View Profile
Re: [Tutorial] Key hooks in Axe
« Reply #14 on: March 20, 2016, 12:13:30 pm »
I figured out what prgm! does, all I am trying to do is when the user presses on and enter at the same time that instead of the OS evaluating the expression that instead it jumps to a program that does something different to it.
My goal is to create a multi-variable equation solver on the home screen.
Also, when you delete an app it defragements, is defragmenting small apps just as bad for the archive as big ones?
I'm still around... kind of.