Omnimaga
Calculator Community => TI Calculators => Axe => Topic started by: MGOS 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":
: 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 (http://clrhome.org/keypad/) 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.... :)
-
Wha, this is a pretty nice tutorial, nice use of asm to get more awesome things done in axe, great job!
:thumbsup:
-
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!
-
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
-
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!
-
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.
-
You forgot the End between these two lines of code in your example program:
: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?
-
You forgot the End between these two lines of code in your example program:
: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 (http://clrhome.org/keypad/), 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?
-
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!
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
-
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.
-
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.
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 (http://wikiti.brandonw.net/index.php?title=83Plus:Hooks:9B8C)
Interesting is A = 0. We can actually recycle almost the whole code from before, except the bcalls and "cp a"
; 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)
: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
-
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.
-
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.
-
This is because text entry on the home screen is implemented using an edit buffer (http://wikiti.brandonw.net/index.php?title=83Plus:OS:Edit_Buffers). 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!".
-
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?
-
Yes, deleting a small app is about as "bad" as deleting a big app. But unless you're doing it 100+ times a day, I don't think you'll ever actually wear the flash out.
-
So, if I write about 30000 bytes to archive a day, I have nothing to worry about?
Is there a hook that or way to run a program when the enter key is pressed instead of the OS evaluating it?
Thanks for your help! :thumbsup:
-
I'm sure you could manage it with a key hook, but perhaps the homescreen hook (http://wikiti.brandonw.net/index.php?title=83Plus:Hooks:9B8C) is better.
-
GetCalc([0523]) can't get the address of the entry
Sometimes if I look for the file (Y1) instead, it works but other times it fails.
Do I need to do something else?
Also the error handlers from RUNPRGM always crashes it.
It does run when the enter key is pressed though!
Thanks for your help!
-
You need a zero byte at the end of the name. You can either add it manually, or you could write the name in string form and the compiler would automatically add it for you.
As for error handler issues, I can't say I understand what you're experiencing.
-
That makes me feel like an idiot. I should have realized that! :-\
It works now but the basic part runs a lot slower any reason why?
-
Are you running this on an 83+SE or 84+? If so, it's possible that your hook (and anything it does as a result) is only running at 6MHz, while BASIC normally runs at 15MHz. In this case, try setting full CPU speed.
-
Thanks! it worked!
After the hook is finished, it then evaluates the OS's interpratation of the expression.
How do I return the nz flag?
-
if you know that in a is something other than zero you can do "or a / ret". If you already know that z is reset the answer is obvious :P
-
Maybe I came over as knowing more than I do.
What would the hex code be? Can I put it anywhere or just at the end?
-
Maybe I came over as knowing more than I do.
What would the hex code be? Can I put it anywhere or just at the end?
Put this right before you return from your hook to return nz: Asm(AF3D).
-
It works! How do I disable it from displaying done?
(it runs a basic program)
-
It works! How do I disable it from displaying done?
(it runs a basic program)
Asm(FDCB00AE) might do it. If not, I'm not sure how.